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)
91 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
92 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
93 #define PANEL_XPOS(p) (DX + ALIGNED_MENU_XPOS(p))
94 #define PANEL_YPOS(p) (DY + ALIGNED_MENU_YPOS(p))
96 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
97 #define PANEL_XPOS(p) (ALIGNED_XPOS((p).x, (p).width, (p).align))
98 #define PANEL_YPOS(p) ((p).y)
101 /* special positions in the game control window (relative to control window) */
102 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
103 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
104 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
105 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
106 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
107 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
108 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
109 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
110 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
111 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
112 #define XX_SCORE (PANEL_XPOS(game.panel.score))
113 #define YY_SCORE (PANEL_YPOS(game.panel.score))
114 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
115 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
116 #define XX_TIME (PANEL_XPOS(game.panel.time))
117 #define YY_TIME (PANEL_YPOS(game.panel.time))
119 /* special positions in the game control window (relative to main window) */
120 #define DX_LEVEL1 (DX + XX_LEVEL1)
121 #define DX_LEVEL2 (DX + XX_LEVEL2)
122 #define DX_LEVEL (DX + XX_LEVEL)
123 #define DY_LEVEL (DY + YY_LEVEL)
124 #define DX_EMERALDS (DX + XX_EMERALDS)
125 #define DY_EMERALDS (DY + YY_EMERALDS)
126 #define DX_DYNAMITE (DX + XX_DYNAMITE)
127 #define DY_DYNAMITE (DY + YY_DYNAMITE)
128 #define DX_KEYS (DX + XX_KEYS)
129 #define DY_KEYS (DY + YY_KEYS)
130 #define DX_SCORE (DX + XX_SCORE)
131 #define DY_SCORE (DY + YY_SCORE)
132 #define DX_TIME1 (DX + XX_TIME1)
133 #define DX_TIME2 (DX + XX_TIME2)
134 #define DX_TIME (DX + XX_TIME)
135 #define DY_TIME (DY + YY_TIME)
138 /* game panel display and control definitions */
140 #define GAME_CONTROL_LEVEL 0
141 #define GAME_CONTROL_GEMS 1
142 #define GAME_CONTROL_INVENTORY 2
143 #define GAME_CONTROL_KEYS 3
144 #define GAME_CONTROL_SCORE 4
145 #define GAME_CONTROL_TIME 5
146 #define GAME_CONTROL_TIME_HH 6
147 #define GAME_CONTROL_TIME_MM 7
148 #define GAME_CONTROL_TIME_SS 8
149 #define GAME_CONTROL_DROP_NEXT_1 9
150 #define GAME_CONTROL_DROP_NEXT_2 10
151 #define GAME_CONTROL_DROP_NEXT_3 11
152 #define GAME_CONTROL_DROP_NEXT_4 12
153 #define GAME_CONTROL_DROP_NEXT_5 13
154 #define GAME_CONTROL_DROP_NEXT_6 14
155 #define GAME_CONTROL_DROP_NEXT_7 15
156 #define GAME_CONTROL_DROP_NEXT_8 16
157 #define GAME_CONTROL_EMC_KEYS 17
158 #define GAME_CONTROL_KEY_1 18
159 #define GAME_CONTROL_KEY_2 19
160 #define GAME_CONTROL_KEY_3 20
161 #define GAME_CONTROL_KEY_4 21
162 #define GAME_CONTROL_KEY_5 22
163 #define GAME_CONTROL_KEY_6 23
164 #define GAME_CONTROL_KEY_7 24
165 #define GAME_CONTROL_KEY_8 25
166 #define GAME_CONTROL_KEY_WHITE 26
167 #define GAME_CONTROL_KEY_WHITE_COUNT 27
168 #define GAME_CONTROL_SHIELD_NORMAL 28
169 #define GAME_CONTROL_SHIELD_NORMAL_TIME 29
170 #define GAME_CONTROL_SHIELD_DEADLY 30
171 #define GAME_CONTROL_SHIELD_DEADLY_TIME 31
172 #define GAME_CONTROL_EXIT 32
173 #define GAME_CONTROL_EM_EXIT 33
174 #define GAME_CONTROL_SP_EXIT 34
175 #define GAME_CONTROL_STEEL_EXIT 35
176 #define GAME_CONTROL_EM_STEEL_EXIT 36
177 #define GAME_CONTROL_EMC_MAGIC_BALL 37
178 #define GAME_CONTROL_EMC_MAGIC_BALL_TIME 38
179 #define GAME_CONTROL_LIGHT_SWITCH 39
180 #define GAME_CONTROL_LIGHT_SWITCH_TIME 40
181 #define GAME_CONTROL_TIMEGATE_SWITCH 41
182 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME 42
183 #define GAME_CONTROL_SWITCHGATE_SWITCH 43
184 #define GAME_CONTROL_EMC_LENSES 44
185 #define GAME_CONTROL_EMC_LENSES_TIME 45
186 #define GAME_CONTROL_EMC_MAGNIFIER 46
187 #define GAME_CONTROL_EMC_MAGNIFIER_TIME 47
188 #define GAME_CONTROL_BALLOON_SWITCH 48
189 #define GAME_CONTROL_DYNABOMB_NUMBER 49
190 #define GAME_CONTROL_DYNABOMB_SIZE 50
191 #define GAME_CONTROL_DYNABOMB_POWER 51
192 #define GAME_CONTROL_PENGUINS 52
193 #define GAME_CONTROL_SOKOBAN_OBJECTS 53
194 #define GAME_CONTROL_SOKOBAN_FIELDS 54
195 #define GAME_CONTROL_ROBOT_WHEEL 55
196 #define GAME_CONTROL_CONVEYOR_BELT_1 56
197 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH 57
198 #define GAME_CONTROL_CONVEYOR_BELT_2 58
199 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH 59
200 #define GAME_CONTROL_CONVEYOR_BELT_3 60
201 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH 61
202 #define GAME_CONTROL_CONVEYOR_BELT_4 62
203 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH 63
204 #define GAME_CONTROL_MAGIC_WALL 64
205 #define GAME_CONTROL_MAGIC_WALL_TIME 65
206 #define GAME_CONTROL_BD_MAGIC_WALL 66
207 #define GAME_CONTROL_DC_MAGIC_WALL 67
208 #define GAME_CONTROL_PLAYER_NAME 68
209 #define GAME_CONTROL_LEVEL_NAME 69
210 #define GAME_CONTROL_LEVEL_AUTHOR 70
212 struct GameControlInfo
216 struct TextPosInfo *pos_text;
221 static struct GameControlInfo game_controls[] =
234 GAME_CONTROL_INVENTORY,
235 &game.panel.inventory,
254 GAME_CONTROL_TIME_HH,
259 GAME_CONTROL_TIME_MM,
264 GAME_CONTROL_TIME_SS,
269 GAME_CONTROL_DROP_NEXT_1,
270 &game.panel.drop_next_1,
274 GAME_CONTROL_DROP_NEXT_2,
275 &game.panel.drop_next_2,
279 GAME_CONTROL_DROP_NEXT_3,
280 &game.panel.drop_next_3,
284 GAME_CONTROL_DROP_NEXT_4,
285 &game.panel.drop_next_4,
289 GAME_CONTROL_DROP_NEXT_5,
290 &game.panel.drop_next_5,
294 GAME_CONTROL_DROP_NEXT_6,
295 &game.panel.drop_next_6,
299 GAME_CONTROL_DROP_NEXT_7,
300 &game.panel.drop_next_7,
304 GAME_CONTROL_DROP_NEXT_8,
305 &game.panel.drop_next_8,
309 GAME_CONTROL_EMC_KEYS,
310 &game.panel.emc_keys,
354 GAME_CONTROL_KEY_WHITE,
355 &game.panel.key_white,
359 GAME_CONTROL_KEY_WHITE_COUNT,
360 &game.panel.key_white_count,
364 GAME_CONTROL_SHIELD_NORMAL,
365 &game.panel.shield_normal,
369 GAME_CONTROL_SHIELD_NORMAL_TIME,
370 &game.panel.shield_normal_time,
374 GAME_CONTROL_SHIELD_DEADLY,
375 &game.panel.shield_deadly,
379 GAME_CONTROL_SHIELD_DEADLY_TIME,
380 &game.panel.shield_deadly_time,
389 GAME_CONTROL_EM_EXIT,
394 GAME_CONTROL_SP_EXIT,
399 GAME_CONTROL_STEEL_EXIT,
400 &game.panel.steel_exit,
404 GAME_CONTROL_EM_STEEL_EXIT,
405 &game.panel.em_steel_exit,
409 GAME_CONTROL_EMC_MAGIC_BALL,
410 &game.panel.emc_magic_ball,
414 GAME_CONTROL_EMC_MAGIC_BALL_TIME,
415 &game.panel.emc_magic_ball_time,
419 GAME_CONTROL_LIGHT_SWITCH,
420 &game.panel.light_switch,
424 GAME_CONTROL_LIGHT_SWITCH_TIME,
425 &game.panel.light_switch_time,
429 GAME_CONTROL_TIMEGATE_SWITCH,
430 &game.panel.timegate_switch,
434 GAME_CONTROL_TIMEGATE_SWITCH_TIME,
435 &game.panel.timegate_switch_time,
439 GAME_CONTROL_SWITCHGATE_SWITCH,
440 &game.panel.switchgate_switch,
444 GAME_CONTROL_EMC_LENSES,
445 &game.panel.emc_lenses,
449 GAME_CONTROL_EMC_LENSES_TIME,
450 &game.panel.emc_lenses_time,
454 GAME_CONTROL_EMC_MAGNIFIER,
455 &game.panel.emc_magnifier,
459 GAME_CONTROL_EMC_MAGNIFIER_TIME,
460 &game.panel.emc_magnifier_time,
464 GAME_CONTROL_BALLOON_SWITCH,
465 &game.panel.balloon_switch,
469 GAME_CONTROL_DYNABOMB_NUMBER,
470 &game.panel.dynabomb_number,
474 GAME_CONTROL_DYNABOMB_SIZE,
475 &game.panel.dynabomb_size,
479 GAME_CONTROL_DYNABOMB_POWER,
480 &game.panel.dynabomb_power,
484 GAME_CONTROL_PENGUINS,
485 &game.panel.penguins,
489 GAME_CONTROL_SOKOBAN_OBJECTS,
490 &game.panel.sokoban_objects,
494 GAME_CONTROL_SOKOBAN_FIELDS,
495 &game.panel.sokoban_fields,
499 GAME_CONTROL_ROBOT_WHEEL,
500 &game.panel.robot_wheel,
504 GAME_CONTROL_CONVEYOR_BELT_1,
505 &game.panel.conveyor_belt_1,
509 GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
510 &game.panel.conveyor_belt_1_switch,
514 GAME_CONTROL_CONVEYOR_BELT_2,
515 &game.panel.conveyor_belt_2,
519 GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
520 &game.panel.conveyor_belt_2_switch,
524 GAME_CONTROL_CONVEYOR_BELT_3,
525 &game.panel.conveyor_belt_3,
529 GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
530 &game.panel.conveyor_belt_3_switch,
534 GAME_CONTROL_CONVEYOR_BELT_4,
535 &game.panel.conveyor_belt_4,
539 GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
540 &game.panel.conveyor_belt_4_switch,
544 GAME_CONTROL_MAGIC_WALL,
545 &game.panel.magic_wall,
549 GAME_CONTROL_MAGIC_WALL_TIME,
550 &game.panel.magic_wall_time,
554 GAME_CONTROL_BD_MAGIC_WALL,
555 &game.panel.bd_magic_wall,
559 GAME_CONTROL_DC_MAGIC_WALL,
560 &game.panel.dc_magic_wall,
564 GAME_CONTROL_PLAYER_NAME,
565 &game.panel.player_name,
569 GAME_CONTROL_LEVEL_NAME,
570 &game.panel.level_name,
574 GAME_CONTROL_LEVEL_AUTHOR,
575 &game.panel.level_author,
589 /* values for delayed check of falling and moving elements and for collision */
590 #define CHECK_DELAY_MOVING 3
591 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
592 #define CHECK_DELAY_COLLISION 2
593 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
595 /* values for initial player move delay (initial delay counter value) */
596 #define INITIAL_MOVE_DELAY_OFF -1
597 #define INITIAL_MOVE_DELAY_ON 0
599 /* values for player movement speed (which is in fact a delay value) */
600 #define MOVE_DELAY_MIN_SPEED 32
601 #define MOVE_DELAY_NORMAL_SPEED 8
602 #define MOVE_DELAY_HIGH_SPEED 4
603 #define MOVE_DELAY_MAX_SPEED 1
605 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
606 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
608 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
609 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
611 /* values for other actions */
612 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
613 #define MOVE_STEPSIZE_MIN (1)
614 #define MOVE_STEPSIZE_MAX (TILEX)
616 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
617 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
619 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
621 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
622 RND(element_info[e].push_delay_random))
623 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
624 RND(element_info[e].drop_delay_random))
625 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
626 RND(element_info[e].move_delay_random))
627 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
628 (element_info[e].move_delay_random))
629 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
630 RND(element_info[e].ce_value_random_initial))
631 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
632 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
633 RND((c)->delay_random * (c)->delay_frames))
634 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
635 RND((c)->delay_random))
638 #define GET_VALID_RUNTIME_ELEMENT(e) \
639 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
641 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
642 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
643 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
644 (be) + (e) - EL_SELF)
646 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
647 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
648 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
649 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
650 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
651 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
652 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
653 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
654 RESOLVED_REFERENCE_ELEMENT(be, e) : \
657 #define CAN_GROW_INTO(e) \
658 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
660 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
661 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
664 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
665 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
666 (CAN_MOVE_INTO_ACID(e) && \
667 Feld[x][y] == EL_ACID) || \
670 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
671 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
672 (CAN_MOVE_INTO_ACID(e) && \
673 Feld[x][y] == EL_ACID) || \
676 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
677 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
679 (CAN_MOVE_INTO_ACID(e) && \
680 Feld[x][y] == EL_ACID) || \
681 (DONT_COLLIDE_WITH(e) && \
683 !PLAYER_ENEMY_PROTECTED(x, y))))
685 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
686 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
688 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
689 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
691 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
692 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
694 #define ANDROID_CAN_CLONE_FIELD(x, y) \
695 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
696 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
698 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
699 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
701 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
702 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
704 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
705 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
707 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
708 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
710 #define PIG_CAN_ENTER_FIELD(e, x, y) \
711 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
713 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
714 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
715 Feld[x][y] == EL_EM_EXIT_OPEN || \
716 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
717 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
718 IS_FOOD_PENGUIN(Feld[x][y])))
719 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
720 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
722 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
723 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
725 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
726 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
728 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
729 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
730 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
732 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
734 #define CE_ENTER_FIELD_COND(e, x, y) \
735 (!IS_PLAYER(x, y) && \
736 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
738 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
739 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
741 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
742 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
744 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
745 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
746 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
747 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
749 /* game button identifiers */
750 #define GAME_CTRL_ID_STOP 0
751 #define GAME_CTRL_ID_PAUSE 1
752 #define GAME_CTRL_ID_PLAY 2
753 #define SOUND_CTRL_ID_MUSIC 3
754 #define SOUND_CTRL_ID_LOOPS 4
755 #define SOUND_CTRL_ID_SIMPLE 5
757 #define NUM_GAME_BUTTONS 6
760 /* forward declaration for internal use */
762 static void CreateField(int, int, int);
764 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
765 static void AdvanceFrameAndPlayerCounters(int);
767 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
768 static boolean MovePlayer(struct PlayerInfo *, int, int);
769 static void ScrollPlayer(struct PlayerInfo *, int);
770 static void ScrollScreen(struct PlayerInfo *, int);
772 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
774 static void InitBeltMovement(void);
775 static void CloseAllOpenTimegates(void);
776 static void CheckGravityMovement(struct PlayerInfo *);
777 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
778 static void KillPlayerUnlessEnemyProtected(int, int);
779 static void KillPlayerUnlessExplosionProtected(int, int);
781 static void TestIfPlayerTouchesCustomElement(int, int);
782 static void TestIfElementTouchesCustomElement(int, int);
783 static void TestIfElementHitsCustomElement(int, int, int);
785 static void TestIfElementSmashesCustomElement(int, int, int);
788 static void HandleElementChange(int, int, int);
789 static void ExecuteCustomElementAction(int, int, int, int);
790 static boolean ChangeElement(int, int, int, int);
792 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
793 #define CheckTriggeredElementChange(x, y, e, ev) \
794 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
795 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
796 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
797 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
798 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
799 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
800 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
802 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
803 #define CheckElementChange(x, y, e, te, ev) \
804 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
805 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
806 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
807 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
808 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
810 static void PlayLevelSound(int, int, int);
811 static void PlayLevelSoundNearest(int, int, int);
812 static void PlayLevelSoundAction(int, int, int);
813 static void PlayLevelSoundElementAction(int, int, int, int);
814 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
815 static void PlayLevelSoundActionIfLoop(int, int, int);
816 static void StopLevelSoundActionIfLoop(int, int, int);
817 static void PlayLevelMusic();
819 static void MapGameButtons();
820 static void HandleGameButtons(struct GadgetInfo *);
822 int AmoebeNachbarNr(int, int);
823 void AmoebeUmwandeln(int, int);
824 void ContinueMoving(int, int);
826 void InitMovDir(int, int);
827 void InitAmoebaNr(int, int);
828 int NewHiScore(void);
830 void TestIfGoodThingHitsBadThing(int, int, int);
831 void TestIfBadThingHitsGoodThing(int, int, int);
832 void TestIfPlayerTouchesBadThing(int, int);
833 void TestIfPlayerRunsIntoBadThing(int, int, int);
834 void TestIfBadThingTouchesPlayer(int, int);
835 void TestIfBadThingRunsIntoPlayer(int, int, int);
836 void TestIfFriendTouchesBadThing(int, int);
837 void TestIfBadThingTouchesFriend(int, int);
838 void TestIfBadThingTouchesOtherBadThing(int, int);
840 void KillPlayer(struct PlayerInfo *);
841 void BuryPlayer(struct PlayerInfo *);
842 void RemovePlayer(struct PlayerInfo *);
844 boolean SnapField(struct PlayerInfo *, int, int);
845 boolean DropElement(struct PlayerInfo *);
847 static int getInvisibleActiveFromInvisibleElement(int);
848 static int getInvisibleFromInvisibleActiveElement(int);
850 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
852 /* for detection of endless loops, caused by custom element programming */
853 /* (using maximal playfield width x 10 is just a rough approximation) */
854 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
856 #define RECURSION_LOOP_DETECTION_START(e, rc) \
858 if (recursion_loop_detected) \
861 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
863 recursion_loop_detected = TRUE; \
864 recursion_loop_element = (e); \
867 recursion_loop_depth++; \
870 #define RECURSION_LOOP_DETECTION_END() \
872 recursion_loop_depth--; \
875 static int recursion_loop_depth;
876 static boolean recursion_loop_detected;
877 static boolean recursion_loop_element;
880 /* ------------------------------------------------------------------------- */
881 /* definition of elements that automatically change to other elements after */
882 /* a specified time, eventually calling a function when changing */
883 /* ------------------------------------------------------------------------- */
885 /* forward declaration for changer functions */
886 static void InitBuggyBase(int, int);
887 static void WarnBuggyBase(int, int);
889 static void InitTrap(int, int);
890 static void ActivateTrap(int, int);
891 static void ChangeActiveTrap(int, int);
893 static void InitRobotWheel(int, int);
894 static void RunRobotWheel(int, int);
895 static void StopRobotWheel(int, int);
897 static void InitTimegateWheel(int, int);
898 static void RunTimegateWheel(int, int);
900 static void InitMagicBallDelay(int, int);
901 static void ActivateMagicBall(int, int);
903 struct ChangingElementInfo
908 void (*pre_change_function)(int x, int y);
909 void (*change_function)(int x, int y);
910 void (*post_change_function)(int x, int y);
913 static struct ChangingElementInfo change_delay_list[] =
948 EL_STEEL_EXIT_OPENING,
956 EL_STEEL_EXIT_CLOSING,
957 EL_STEEL_EXIT_CLOSED,
984 EL_EM_STEEL_EXIT_OPENING,
985 EL_EM_STEEL_EXIT_OPEN,
992 EL_EM_STEEL_EXIT_CLOSING,
996 EL_EM_STEEL_EXIT_CLOSED,
1020 EL_SWITCHGATE_OPENING,
1028 EL_SWITCHGATE_CLOSING,
1029 EL_SWITCHGATE_CLOSED,
1036 EL_TIMEGATE_OPENING,
1044 EL_TIMEGATE_CLOSING,
1053 EL_ACID_SPLASH_LEFT,
1061 EL_ACID_SPLASH_RIGHT,
1070 EL_SP_BUGGY_BASE_ACTIVATING,
1077 EL_SP_BUGGY_BASE_ACTIVATING,
1078 EL_SP_BUGGY_BASE_ACTIVE,
1085 EL_SP_BUGGY_BASE_ACTIVE,
1109 EL_ROBOT_WHEEL_ACTIVE,
1117 EL_TIMEGATE_SWITCH_ACTIVE,
1125 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1126 EL_DC_TIMEGATE_SWITCH,
1133 EL_EMC_MAGIC_BALL_ACTIVE,
1134 EL_EMC_MAGIC_BALL_ACTIVE,
1141 EL_EMC_SPRING_BUMPER_ACTIVE,
1142 EL_EMC_SPRING_BUMPER,
1149 EL_DIAGONAL_SHRINKING,
1157 EL_DIAGONAL_GROWING,
1178 int push_delay_fixed, push_delay_random;
1182 { EL_SPRING, 0, 0 },
1183 { EL_BALLOON, 0, 0 },
1185 { EL_SOKOBAN_OBJECT, 2, 0 },
1186 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1187 { EL_SATELLITE, 2, 0 },
1188 { EL_SP_DISK_YELLOW, 2, 0 },
1190 { EL_UNDEFINED, 0, 0 },
1198 move_stepsize_list[] =
1200 { EL_AMOEBA_DROP, 2 },
1201 { EL_AMOEBA_DROPPING, 2 },
1202 { EL_QUICKSAND_FILLING, 1 },
1203 { EL_QUICKSAND_EMPTYING, 1 },
1204 { EL_QUICKSAND_FAST_FILLING, 2 },
1205 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1206 { EL_MAGIC_WALL_FILLING, 2 },
1207 { EL_MAGIC_WALL_EMPTYING, 2 },
1208 { EL_BD_MAGIC_WALL_FILLING, 2 },
1209 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1210 { EL_DC_MAGIC_WALL_FILLING, 2 },
1211 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1213 { EL_UNDEFINED, 0 },
1221 collect_count_list[] =
1224 { EL_BD_DIAMOND, 1 },
1225 { EL_EMERALD_YELLOW, 1 },
1226 { EL_EMERALD_RED, 1 },
1227 { EL_EMERALD_PURPLE, 1 },
1229 { EL_SP_INFOTRON, 1 },
1233 { EL_UNDEFINED, 0 },
1241 access_direction_list[] =
1243 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1244 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1245 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1246 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1247 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1248 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1249 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1250 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1251 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1252 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1253 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1255 { EL_SP_PORT_LEFT, MV_RIGHT },
1256 { EL_SP_PORT_RIGHT, MV_LEFT },
1257 { EL_SP_PORT_UP, MV_DOWN },
1258 { EL_SP_PORT_DOWN, MV_UP },
1259 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1260 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1261 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1262 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1263 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1264 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1265 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1266 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1267 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1268 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1269 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1270 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1271 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1272 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1273 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1275 { EL_UNDEFINED, MV_NONE }
1278 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1280 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1281 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1282 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1283 IS_JUST_CHANGING(x, y))
1285 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1287 /* static variables for playfield scan mode (scanning forward or backward) */
1288 static int playfield_scan_start_x = 0;
1289 static int playfield_scan_start_y = 0;
1290 static int playfield_scan_delta_x = 1;
1291 static int playfield_scan_delta_y = 1;
1293 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1294 (y) >= 0 && (y) <= lev_fieldy - 1; \
1295 (y) += playfield_scan_delta_y) \
1296 for ((x) = playfield_scan_start_x; \
1297 (x) >= 0 && (x) <= lev_fieldx - 1; \
1298 (x) += playfield_scan_delta_x)
1301 void DEBUG_SetMaximumDynamite()
1305 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1306 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1307 local_player->inventory_element[local_player->inventory_size++] =
1312 static void InitPlayfieldScanModeVars()
1314 if (game.use_reverse_scan_direction)
1316 playfield_scan_start_x = lev_fieldx - 1;
1317 playfield_scan_start_y = lev_fieldy - 1;
1319 playfield_scan_delta_x = -1;
1320 playfield_scan_delta_y = -1;
1324 playfield_scan_start_x = 0;
1325 playfield_scan_start_y = 0;
1327 playfield_scan_delta_x = 1;
1328 playfield_scan_delta_y = 1;
1332 static void InitPlayfieldScanMode(int mode)
1334 game.use_reverse_scan_direction =
1335 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1337 InitPlayfieldScanModeVars();
1340 static int get_move_delay_from_stepsize(int move_stepsize)
1343 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1345 /* make sure that stepsize value is always a power of 2 */
1346 move_stepsize = (1 << log_2(move_stepsize));
1348 return TILEX / move_stepsize;
1351 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1354 int player_nr = player->index_nr;
1355 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1356 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1358 /* do no immediately change move delay -- the player might just be moving */
1359 player->move_delay_value_next = move_delay;
1361 /* information if player can move must be set separately */
1362 player->cannot_move = cannot_move;
1366 player->move_delay = game.initial_move_delay[player_nr];
1367 player->move_delay_value = game.initial_move_delay_value[player_nr];
1369 player->move_delay_value_next = -1;
1371 player->move_delay_reset_counter = 0;
1375 void GetPlayerConfig()
1377 GameFrameDelay = setup.game_frame_delay;
1379 if (!audio.sound_available)
1380 setup.sound_simple = FALSE;
1382 if (!audio.loops_available)
1383 setup.sound_loops = FALSE;
1385 if (!audio.music_available)
1386 setup.sound_music = FALSE;
1388 if (!video.fullscreen_available)
1389 setup.fullscreen = FALSE;
1391 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1393 SetAudioMode(setup.sound);
1397 int GetElementFromGroupElement(int element)
1399 if (IS_GROUP_ELEMENT(element))
1401 struct ElementGroupInfo *group = element_info[element].group;
1402 int last_anim_random_frame = gfx.anim_random_frame;
1405 if (group->choice_mode == ANIM_RANDOM)
1406 gfx.anim_random_frame = RND(group->num_elements_resolved);
1408 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1409 group->choice_mode, 0,
1412 if (group->choice_mode == ANIM_RANDOM)
1413 gfx.anim_random_frame = last_anim_random_frame;
1415 group->choice_pos++;
1417 element = group->element_resolved[element_pos];
1423 static void InitPlayerField(int x, int y, int element, boolean init_game)
1425 if (element == EL_SP_MURPHY)
1429 if (stored_player[0].present)
1431 Feld[x][y] = EL_SP_MURPHY_CLONE;
1437 stored_player[0].use_murphy = TRUE;
1439 if (!level.use_artwork_element[0])
1440 stored_player[0].artwork_element = EL_SP_MURPHY;
1443 Feld[x][y] = EL_PLAYER_1;
1449 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1450 int jx = player->jx, jy = player->jy;
1452 player->present = TRUE;
1454 player->block_last_field = (element == EL_SP_MURPHY ?
1455 level.sp_block_last_field :
1456 level.block_last_field);
1458 /* ---------- initialize player's last field block delay --------------- */
1460 /* always start with reliable default value (no adjustment needed) */
1461 player->block_delay_adjustment = 0;
1463 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1464 if (player->block_last_field && element == EL_SP_MURPHY)
1465 player->block_delay_adjustment = 1;
1467 /* special case 2: in game engines before 3.1.1, blocking was different */
1468 if (game.use_block_last_field_bug)
1469 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1471 if (!options.network || player->connected)
1473 player->active = TRUE;
1475 /* remove potentially duplicate players */
1476 if (StorePlayer[jx][jy] == Feld[x][y])
1477 StorePlayer[jx][jy] = 0;
1479 StorePlayer[x][y] = Feld[x][y];
1483 printf("Player %d activated.\n", player->element_nr);
1484 printf("[Local player is %d and currently %s.]\n",
1485 local_player->element_nr,
1486 local_player->active ? "active" : "not active");
1490 Feld[x][y] = EL_EMPTY;
1492 player->jx = player->last_jx = x;
1493 player->jy = player->last_jy = y;
1497 static void InitField(int x, int y, boolean init_game)
1499 int element = Feld[x][y];
1508 InitPlayerField(x, y, element, init_game);
1511 case EL_SOKOBAN_FIELD_PLAYER:
1512 element = Feld[x][y] = EL_PLAYER_1;
1513 InitField(x, y, init_game);
1515 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1516 InitField(x, y, init_game);
1519 case EL_SOKOBAN_FIELD_EMPTY:
1520 local_player->sokobanfields_still_needed++;
1524 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1525 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1526 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1527 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1528 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1529 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1530 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1531 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1532 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1533 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1542 case EL_SPACESHIP_RIGHT:
1543 case EL_SPACESHIP_UP:
1544 case EL_SPACESHIP_LEFT:
1545 case EL_SPACESHIP_DOWN:
1546 case EL_BD_BUTTERFLY:
1547 case EL_BD_BUTTERFLY_RIGHT:
1548 case EL_BD_BUTTERFLY_UP:
1549 case EL_BD_BUTTERFLY_LEFT:
1550 case EL_BD_BUTTERFLY_DOWN:
1552 case EL_BD_FIREFLY_RIGHT:
1553 case EL_BD_FIREFLY_UP:
1554 case EL_BD_FIREFLY_LEFT:
1555 case EL_BD_FIREFLY_DOWN:
1556 case EL_PACMAN_RIGHT:
1558 case EL_PACMAN_LEFT:
1559 case EL_PACMAN_DOWN:
1561 case EL_YAMYAM_LEFT:
1562 case EL_YAMYAM_RIGHT:
1564 case EL_YAMYAM_DOWN:
1565 case EL_DARK_YAMYAM:
1568 case EL_SP_SNIKSNAK:
1569 case EL_SP_ELECTRON:
1578 case EL_AMOEBA_FULL:
1583 case EL_AMOEBA_DROP:
1584 if (y == lev_fieldy - 1)
1586 Feld[x][y] = EL_AMOEBA_GROWING;
1587 Store[x][y] = EL_AMOEBA_WET;
1591 case EL_DYNAMITE_ACTIVE:
1592 case EL_SP_DISK_RED_ACTIVE:
1593 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1594 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1595 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1596 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1597 MovDelay[x][y] = 96;
1600 case EL_EM_DYNAMITE_ACTIVE:
1601 MovDelay[x][y] = 32;
1605 local_player->lights_still_needed++;
1609 local_player->friends_still_needed++;
1614 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1617 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1618 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1619 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1620 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1621 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1622 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1623 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1624 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1625 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1626 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1627 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1628 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1631 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1632 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1633 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1635 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1637 game.belt_dir[belt_nr] = belt_dir;
1638 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1640 else /* more than one switch -- set it like the first switch */
1642 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1647 #if !USE_BOTH_SWITCHGATE_SWITCHES
1648 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1650 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1653 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1655 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1659 case EL_LIGHT_SWITCH_ACTIVE:
1661 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1664 case EL_INVISIBLE_STEELWALL:
1665 case EL_INVISIBLE_WALL:
1666 case EL_INVISIBLE_SAND:
1667 if (game.light_time_left > 0 ||
1668 game.lenses_time_left > 0)
1669 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1672 case EL_EMC_MAGIC_BALL:
1673 if (game.ball_state)
1674 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1677 case EL_EMC_MAGIC_BALL_SWITCH:
1678 if (game.ball_state)
1679 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1683 if (IS_CUSTOM_ELEMENT(element))
1685 if (CAN_MOVE(element))
1688 #if USE_NEW_CUSTOM_VALUE
1689 if (!element_info[element].use_last_ce_value || init_game)
1690 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1693 else if (IS_GROUP_ELEMENT(element))
1695 Feld[x][y] = GetElementFromGroupElement(element);
1697 InitField(x, y, init_game);
1704 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1707 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1709 InitField(x, y, init_game);
1711 /* not needed to call InitMovDir() -- already done by InitField()! */
1712 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1713 CAN_MOVE(Feld[x][y]))
1717 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1719 int old_element = Feld[x][y];
1721 InitField(x, y, init_game);
1723 /* not needed to call InitMovDir() -- already done by InitField()! */
1724 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1725 CAN_MOVE(old_element) &&
1726 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1729 /* this case is in fact a combination of not less than three bugs:
1730 first, it calls InitMovDir() for elements that can move, although this is
1731 already done by InitField(); then, it checks the element that was at this
1732 field _before_ the call to InitField() (which can change it); lastly, it
1733 was not called for "mole with direction" elements, which were treated as
1734 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1740 void DrawGameValue_Emeralds(int value)
1742 struct TextPosInfo *pos = &game.panel.gems;
1744 int font_nr = pos->font;
1746 int font_nr = FONT_TEXT_2;
1748 int font_width = getFontWidth(font_nr);
1749 int chars = pos->chars;
1751 if (PANEL_DEACTIVATED(pos))
1754 pos->width = chars * font_width;
1756 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1759 void DrawGameValue_Dynamite(int value)
1761 struct TextPosInfo *pos = &game.panel.inventory;
1763 int font_nr = pos->font;
1765 int font_nr = FONT_TEXT_2;
1767 int font_width = getFontWidth(font_nr);
1768 int chars = pos->chars;
1770 if (PANEL_DEACTIVATED(pos))
1773 pos->width = chars * font_width;
1775 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1778 void DrawGameValue_Score(int value)
1780 struct TextPosInfo *pos = &game.panel.score;
1782 int font_nr = pos->font;
1784 int font_nr = FONT_TEXT_2;
1786 int font_width = getFontWidth(font_nr);
1787 int chars = pos->chars;
1789 if (PANEL_DEACTIVATED(pos))
1792 pos->width = chars * font_width;
1794 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1797 void DrawGameValue_Time(int value)
1799 struct TextPosInfo *pos = &game.panel.time;
1800 static int last_value = -1;
1803 int chars = pos->chars;
1805 int font1_nr = pos->font;
1806 int font2_nr = pos->font_alt;
1808 int font1_nr = FONT_TEXT_2;
1809 int font2_nr = FONT_TEXT_1;
1811 int font_nr = font1_nr;
1812 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1814 if (PANEL_DEACTIVATED(pos))
1817 if (use_dynamic_chars) /* use dynamic number of chars */
1819 chars = (value < 1000 ? chars1 : chars2);
1820 font_nr = (value < 1000 ? font1_nr : font2_nr);
1823 /* clear background if value just changed its size (dynamic chars only) */
1824 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
1826 int width1 = chars1 * getFontWidth(font1_nr);
1827 int width2 = chars2 * getFontWidth(font2_nr);
1828 int max_width = MAX(width1, width2);
1829 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1831 pos->width = max_width;
1833 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1834 max_width, max_height);
1837 pos->width = chars * getFontWidth(font_nr);
1839 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1844 void DrawGameValue_Level(int value)
1846 struct TextPosInfo *pos = &game.panel.level;
1849 int chars = pos->chars;
1851 int font1_nr = pos->font;
1852 int font2_nr = pos->font_alt;
1854 int font1_nr = FONT_TEXT_2;
1855 int font2_nr = FONT_TEXT_1;
1857 int font_nr = font1_nr;
1858 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1860 if (PANEL_DEACTIVATED(pos))
1863 if (use_dynamic_chars) /* use dynamic number of chars */
1865 chars = (level_nr < 100 ? chars1 : chars2);
1866 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1869 pos->width = chars * getFontWidth(font_nr);
1871 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1874 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1876 struct TextPosInfo *pos = &game.panel.keys;
1877 int base_key_graphic = EL_KEY_1;
1880 if (PANEL_DEACTIVATED(pos))
1883 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1884 base_key_graphic = EL_EM_KEY_1;
1886 pos->width = 4 * MINI_TILEX;
1888 /* currently only 4 of 8 possible keys are displayed */
1889 for (i = 0; i < STD_NUM_KEYS; i++)
1891 int src_x = DOOR_GFX_PAGEX5 + 18;
1892 int src_y = DOOR_GFX_PAGEY1 + 123;
1893 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1894 int dst_y = PANEL_YPOS(pos);
1897 /* masked blit with tiles from half-size scaled bitmap does not work yet
1898 (no mask bitmap created for these sizes after loading and scaling) --
1899 solution: load without creating mask, scale, then create final mask */
1901 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1902 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1906 int graphic = el2edimg(base_key_graphic + i);
1910 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1912 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1913 dst_x - src_x, dst_y - src_y);
1914 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
1919 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1921 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1922 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1929 void DrawGameValue_Emeralds(int value)
1931 int font_nr = FONT_TEXT_2;
1932 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1934 if (PANEL_DEACTIVATED(game.panel.gems))
1937 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1940 void DrawGameValue_Dynamite(int value)
1942 int font_nr = FONT_TEXT_2;
1943 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1945 if (PANEL_DEACTIVATED(game.panel.inventory))
1948 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1951 void DrawGameValue_Score(int value)
1953 int font_nr = FONT_TEXT_2;
1954 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1956 if (PANEL_DEACTIVATED(game.panel.score))
1959 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1962 void DrawGameValue_Time(int value)
1964 int font1_nr = FONT_TEXT_2;
1966 int font2_nr = FONT_TEXT_1;
1968 int font2_nr = FONT_LEVEL_NUMBER;
1970 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1971 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1973 if (PANEL_DEACTIVATED(game.panel.time))
1976 /* clear background if value just changed its size */
1977 if (value == 999 || value == 1000)
1978 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1981 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1983 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1986 void DrawGameValue_Level(int value)
1988 int font1_nr = FONT_TEXT_2;
1990 int font2_nr = FONT_TEXT_1;
1992 int font2_nr = FONT_LEVEL_NUMBER;
1995 if (PANEL_DEACTIVATED(game.panel.level))
1999 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2001 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2004 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2006 int base_key_graphic = EL_KEY_1;
2009 if (PANEL_DEACTIVATED(game.panel.keys))
2012 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2013 base_key_graphic = EL_EM_KEY_1;
2015 /* currently only 4 of 8 possible keys are displayed */
2016 for (i = 0; i < STD_NUM_KEYS; i++)
2018 int x = XX_KEYS + i * MINI_TILEX;
2022 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2024 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2025 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2031 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2034 int key[MAX_NUM_KEYS];
2037 /* prevent EM engine from updating time/score values parallel to GameWon() */
2038 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2039 local_player->LevelSolved)
2042 for (i = 0; i < MAX_NUM_KEYS; i++)
2043 key[i] = key_bits & (1 << i);
2045 DrawGameValue_Level(level_nr);
2047 DrawGameValue_Emeralds(emeralds);
2048 DrawGameValue_Dynamite(dynamite);
2049 DrawGameValue_Score(score);
2050 DrawGameValue_Time(time);
2052 DrawGameValue_Keys(key);
2055 void DrawGameDoorValues()
2057 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2058 int dynamite_value = 0;
2059 int score_value = (local_player->LevelSolved ? local_player->score_final :
2060 local_player->score);
2061 int gems_value = local_player->gems_still_needed;
2065 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2067 DrawGameDoorValues_EM();
2072 if (game.centered_player_nr == -1)
2074 for (i = 0; i < MAX_PLAYERS; i++)
2076 for (j = 0; j < MAX_NUM_KEYS; j++)
2077 if (stored_player[i].key[j])
2078 key_bits |= (1 << j);
2080 dynamite_value += stored_player[i].inventory_size;
2085 int player_nr = game.centered_player_nr;
2087 for (i = 0; i < MAX_NUM_KEYS; i++)
2088 if (stored_player[player_nr].key[i])
2089 key_bits |= (1 << i);
2091 dynamite_value = stored_player[player_nr].inventory_size;
2094 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2100 =============================================================================
2102 -----------------------------------------------------------------------------
2103 initialize game engine due to level / tape version number
2104 =============================================================================
2107 static void InitGameEngine()
2109 int i, j, k, l, x, y;
2111 /* set game engine from tape file when re-playing, else from level file */
2112 game.engine_version = (tape.playing ? tape.engine_version :
2113 level.game_version);
2115 /* ---------------------------------------------------------------------- */
2116 /* set flags for bugs and changes according to active game engine version */
2117 /* ---------------------------------------------------------------------- */
2120 Summary of bugfix/change:
2121 Fixed handling for custom elements that change when pushed by the player.
2123 Fixed/changed in version:
2127 Before 3.1.0, custom elements that "change when pushing" changed directly
2128 after the player started pushing them (until then handled in "DigField()").
2129 Since 3.1.0, these custom elements are not changed until the "pushing"
2130 move of the element is finished (now handled in "ContinueMoving()").
2132 Affected levels/tapes:
2133 The first condition is generally needed for all levels/tapes before version
2134 3.1.0, which might use the old behaviour before it was changed; known tapes
2135 that are affected are some tapes from the level set "Walpurgis Gardens" by
2137 The second condition is an exception from the above case and is needed for
2138 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2139 above (including some development versions of 3.1.0), but before it was
2140 known that this change would break tapes like the above and was fixed in
2141 3.1.1, so that the changed behaviour was active although the engine version
2142 while recording maybe was before 3.1.0. There is at least one tape that is
2143 affected by this exception, which is the tape for the one-level set "Bug
2144 Machine" by Juergen Bonhagen.
2147 game.use_change_when_pushing_bug =
2148 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2150 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2151 tape.game_version < VERSION_IDENT(3,1,1,0)));
2154 Summary of bugfix/change:
2155 Fixed handling for blocking the field the player leaves when moving.
2157 Fixed/changed in version:
2161 Before 3.1.1, when "block last field when moving" was enabled, the field
2162 the player is leaving when moving was blocked for the time of the move,
2163 and was directly unblocked afterwards. This resulted in the last field
2164 being blocked for exactly one less than the number of frames of one player
2165 move. Additionally, even when blocking was disabled, the last field was
2166 blocked for exactly one frame.
2167 Since 3.1.1, due to changes in player movement handling, the last field
2168 is not blocked at all when blocking is disabled. When blocking is enabled,
2169 the last field is blocked for exactly the number of frames of one player
2170 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2171 last field is blocked for exactly one more than the number of frames of
2174 Affected levels/tapes:
2175 (!!! yet to be determined -- probably many !!!)
2178 game.use_block_last_field_bug =
2179 (game.engine_version < VERSION_IDENT(3,1,1,0));
2182 Summary of bugfix/change:
2183 Changed behaviour of CE changes with multiple changes per single frame.
2185 Fixed/changed in version:
2189 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2190 This resulted in race conditions where CEs seem to behave strange in some
2191 situations (where triggered CE changes were just skipped because there was
2192 already a CE change on that tile in the playfield in that engine frame).
2193 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2194 (The number of changes per frame must be limited in any case, because else
2195 it is easily possible to define CE changes that would result in an infinite
2196 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2197 should be set large enough so that it would only be reached in cases where
2198 the corresponding CE change conditions run into a loop. Therefore, it seems
2199 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2200 maximal number of change pages for custom elements.)
2202 Affected levels/tapes:
2206 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2207 game.max_num_changes_per_frame = 1;
2209 game.max_num_changes_per_frame =
2210 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2213 /* ---------------------------------------------------------------------- */
2215 /* default scan direction: scan playfield from top/left to bottom/right */
2216 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2218 /* dynamically adjust element properties according to game engine version */
2219 InitElementPropertiesEngine(game.engine_version);
2222 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2223 printf(" tape version == %06d [%s] [file: %06d]\n",
2224 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2226 printf(" => game.engine_version == %06d\n", game.engine_version);
2229 /* ---------- initialize player's initial move delay --------------------- */
2231 /* dynamically adjust player properties according to level information */
2232 for (i = 0; i < MAX_PLAYERS; i++)
2233 game.initial_move_delay_value[i] =
2234 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2236 /* dynamically adjust player properties according to game engine version */
2237 for (i = 0; i < MAX_PLAYERS; i++)
2238 game.initial_move_delay[i] =
2239 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2240 game.initial_move_delay_value[i] : 0);
2242 /* ---------- initialize player's initial push delay --------------------- */
2244 /* dynamically adjust player properties according to game engine version */
2245 game.initial_push_delay_value =
2246 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2248 /* ---------- initialize changing elements ------------------------------- */
2250 /* initialize changing elements information */
2251 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2253 struct ElementInfo *ei = &element_info[i];
2255 /* this pointer might have been changed in the level editor */
2256 ei->change = &ei->change_page[0];
2258 if (!IS_CUSTOM_ELEMENT(i))
2260 ei->change->target_element = EL_EMPTY_SPACE;
2261 ei->change->delay_fixed = 0;
2262 ei->change->delay_random = 0;
2263 ei->change->delay_frames = 1;
2266 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2268 ei->has_change_event[j] = FALSE;
2270 ei->event_page_nr[j] = 0;
2271 ei->event_page[j] = &ei->change_page[0];
2275 /* add changing elements from pre-defined list */
2276 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2278 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2279 struct ElementInfo *ei = &element_info[ch_delay->element];
2281 ei->change->target_element = ch_delay->target_element;
2282 ei->change->delay_fixed = ch_delay->change_delay;
2284 ei->change->pre_change_function = ch_delay->pre_change_function;
2285 ei->change->change_function = ch_delay->change_function;
2286 ei->change->post_change_function = ch_delay->post_change_function;
2288 ei->change->can_change = TRUE;
2289 ei->change->can_change_or_has_action = TRUE;
2291 ei->has_change_event[CE_DELAY] = TRUE;
2293 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2294 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2297 /* ---------- initialize internal run-time variables ------------- */
2299 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2301 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2303 for (j = 0; j < ei->num_change_pages; j++)
2305 ei->change_page[j].can_change_or_has_action =
2306 (ei->change_page[j].can_change |
2307 ei->change_page[j].has_action);
2311 /* add change events from custom element configuration */
2312 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2314 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2316 for (j = 0; j < ei->num_change_pages; j++)
2318 if (!ei->change_page[j].can_change_or_has_action)
2321 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2323 /* only add event page for the first page found with this event */
2324 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2326 ei->has_change_event[k] = TRUE;
2328 ei->event_page_nr[k] = j;
2329 ei->event_page[k] = &ei->change_page[j];
2335 /* ---------- initialize run-time trigger player and element ------------- */
2337 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2339 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2341 for (j = 0; j < ei->num_change_pages; j++)
2343 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2344 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2345 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2346 ei->change_page[j].actual_trigger_ce_value = 0;
2347 ei->change_page[j].actual_trigger_ce_score = 0;
2351 /* ---------- initialize trigger events ---------------------------------- */
2353 /* initialize trigger events information */
2354 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2355 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2356 trigger_events[i][j] = FALSE;
2358 /* add trigger events from element change event properties */
2359 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2361 struct ElementInfo *ei = &element_info[i];
2363 for (j = 0; j < ei->num_change_pages; j++)
2365 if (!ei->change_page[j].can_change_or_has_action)
2368 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2370 int trigger_element = ei->change_page[j].trigger_element;
2372 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2374 if (ei->change_page[j].has_event[k])
2376 if (IS_GROUP_ELEMENT(trigger_element))
2378 struct ElementGroupInfo *group =
2379 element_info[trigger_element].group;
2381 for (l = 0; l < group->num_elements_resolved; l++)
2382 trigger_events[group->element_resolved[l]][k] = TRUE;
2384 else if (trigger_element == EL_ANY_ELEMENT)
2385 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2386 trigger_events[l][k] = TRUE;
2388 trigger_events[trigger_element][k] = TRUE;
2395 /* ---------- initialize push delay -------------------------------------- */
2397 /* initialize push delay values to default */
2398 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2400 if (!IS_CUSTOM_ELEMENT(i))
2402 /* set default push delay values (corrected since version 3.0.7-1) */
2403 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2405 element_info[i].push_delay_fixed = 2;
2406 element_info[i].push_delay_random = 8;
2410 element_info[i].push_delay_fixed = 8;
2411 element_info[i].push_delay_random = 8;
2416 /* set push delay value for certain elements from pre-defined list */
2417 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2419 int e = push_delay_list[i].element;
2421 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2422 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2425 /* set push delay value for Supaplex elements for newer engine versions */
2426 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2428 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2430 if (IS_SP_ELEMENT(i))
2432 /* set SP push delay to just enough to push under a falling zonk */
2433 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2435 element_info[i].push_delay_fixed = delay;
2436 element_info[i].push_delay_random = 0;
2441 /* ---------- initialize move stepsize ----------------------------------- */
2443 /* initialize move stepsize values to default */
2444 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2445 if (!IS_CUSTOM_ELEMENT(i))
2446 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2448 /* set move stepsize value for certain elements from pre-defined list */
2449 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2451 int e = move_stepsize_list[i].element;
2453 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2456 /* ---------- initialize collect score ----------------------------------- */
2458 /* initialize collect score values for custom elements from initial value */
2459 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2460 if (IS_CUSTOM_ELEMENT(i))
2461 element_info[i].collect_score = element_info[i].collect_score_initial;
2463 /* ---------- initialize collect count ----------------------------------- */
2465 /* initialize collect count values for non-custom elements */
2466 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2467 if (!IS_CUSTOM_ELEMENT(i))
2468 element_info[i].collect_count_initial = 0;
2470 /* add collect count values for all elements from pre-defined list */
2471 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2472 element_info[collect_count_list[i].element].collect_count_initial =
2473 collect_count_list[i].count;
2475 /* ---------- initialize access direction -------------------------------- */
2477 /* initialize access direction values to default (access from every side) */
2478 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2479 if (!IS_CUSTOM_ELEMENT(i))
2480 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2482 /* set access direction value for certain elements from pre-defined list */
2483 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2484 element_info[access_direction_list[i].element].access_direction =
2485 access_direction_list[i].direction;
2487 /* ---------- initialize explosion content ------------------------------- */
2488 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2490 if (IS_CUSTOM_ELEMENT(i))
2493 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2495 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2497 element_info[i].content.e[x][y] =
2498 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2499 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2500 i == EL_PLAYER_3 ? EL_EMERALD :
2501 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2502 i == EL_MOLE ? EL_EMERALD_RED :
2503 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2504 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2505 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2506 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2507 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2508 i == EL_WALL_EMERALD ? EL_EMERALD :
2509 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2510 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2511 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2512 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2513 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2514 i == EL_WALL_PEARL ? EL_PEARL :
2515 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2520 /* ---------- initialize recursion detection ------------------------------ */
2521 recursion_loop_depth = 0;
2522 recursion_loop_detected = FALSE;
2523 recursion_loop_element = EL_UNDEFINED;
2526 int get_num_special_action(int element, int action_first, int action_last)
2528 int num_special_action = 0;
2531 for (i = action_first; i <= action_last; i++)
2533 boolean found = FALSE;
2535 for (j = 0; j < NUM_DIRECTIONS; j++)
2536 if (el_act_dir2img(element, i, j) !=
2537 el_act_dir2img(element, ACTION_DEFAULT, j))
2541 num_special_action++;
2546 return num_special_action;
2551 =============================================================================
2553 -----------------------------------------------------------------------------
2554 initialize and start new game
2555 =============================================================================
2560 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2561 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2562 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2563 boolean do_fading = (game_status == GAME_MODE_MAIN);
2566 game_status = GAME_MODE_PLAYING;
2570 /* don't play tapes over network */
2571 network_playing = (options.network && !tape.playing);
2573 for (i = 0; i < MAX_PLAYERS; i++)
2575 struct PlayerInfo *player = &stored_player[i];
2577 player->index_nr = i;
2578 player->index_bit = (1 << i);
2579 player->element_nr = EL_PLAYER_1 + i;
2581 player->present = FALSE;
2582 player->active = FALSE;
2583 player->killed = FALSE;
2586 player->effective_action = 0;
2587 player->programmed_action = 0;
2590 player->score_final = 0;
2592 player->gems_still_needed = level.gems_needed;
2593 player->sokobanfields_still_needed = 0;
2594 player->lights_still_needed = 0;
2595 player->friends_still_needed = 0;
2597 for (j = 0; j < MAX_NUM_KEYS; j++)
2598 player->key[j] = FALSE;
2600 player->num_white_keys = 0;
2602 player->dynabomb_count = 0;
2603 player->dynabomb_size = 1;
2604 player->dynabombs_left = 0;
2605 player->dynabomb_xl = FALSE;
2607 player->MovDir = MV_NONE;
2610 player->GfxDir = MV_NONE;
2611 player->GfxAction = ACTION_DEFAULT;
2613 player->StepFrame = 0;
2615 player->use_murphy = FALSE;
2616 player->artwork_element =
2617 (level.use_artwork_element[i] ? level.artwork_element[i] :
2618 player->element_nr);
2620 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2621 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2623 player->gravity = level.initial_player_gravity[i];
2625 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2627 player->actual_frame_counter = 0;
2629 player->step_counter = 0;
2631 player->last_move_dir = MV_NONE;
2633 player->is_active = FALSE;
2635 player->is_waiting = FALSE;
2636 player->is_moving = FALSE;
2637 player->is_auto_moving = FALSE;
2638 player->is_digging = FALSE;
2639 player->is_snapping = FALSE;
2640 player->is_collecting = FALSE;
2641 player->is_pushing = FALSE;
2642 player->is_switching = FALSE;
2643 player->is_dropping = FALSE;
2644 player->is_dropping_pressed = FALSE;
2646 player->is_bored = FALSE;
2647 player->is_sleeping = FALSE;
2649 player->frame_counter_bored = -1;
2650 player->frame_counter_sleeping = -1;
2652 player->anim_delay_counter = 0;
2653 player->post_delay_counter = 0;
2655 player->dir_waiting = MV_NONE;
2656 player->action_waiting = ACTION_DEFAULT;
2657 player->last_action_waiting = ACTION_DEFAULT;
2658 player->special_action_bored = ACTION_DEFAULT;
2659 player->special_action_sleeping = ACTION_DEFAULT;
2661 player->switch_x = -1;
2662 player->switch_y = -1;
2664 player->drop_x = -1;
2665 player->drop_y = -1;
2667 player->show_envelope = 0;
2669 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2671 player->push_delay = -1; /* initialized when pushing starts */
2672 player->push_delay_value = game.initial_push_delay_value;
2674 player->drop_delay = 0;
2675 player->drop_pressed_delay = 0;
2677 player->last_jx = -1;
2678 player->last_jy = -1;
2682 player->shield_normal_time_left = 0;
2683 player->shield_deadly_time_left = 0;
2685 player->inventory_infinite_element = EL_UNDEFINED;
2686 player->inventory_size = 0;
2688 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2689 SnapField(player, 0, 0);
2691 player->LevelSolved = FALSE;
2692 player->GameOver = FALSE;
2694 player->LevelSolved_GameWon = FALSE;
2695 player->LevelSolved_GameEnd = FALSE;
2696 player->LevelSolved_PanelOff = FALSE;
2697 player->LevelSolved_SaveTape = FALSE;
2698 player->LevelSolved_SaveScore = FALSE;
2701 network_player_action_received = FALSE;
2703 #if defined(NETWORK_AVALIABLE)
2704 /* initial null action */
2705 if (network_playing)
2706 SendToServer_MovePlayer(MV_NONE);
2715 TimeLeft = level.time;
2718 ScreenMovDir = MV_NONE;
2722 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2724 AllPlayersGone = FALSE;
2726 game.yamyam_content_nr = 0;
2727 game.magic_wall_active = FALSE;
2728 game.magic_wall_time_left = 0;
2729 game.light_time_left = 0;
2730 game.timegate_time_left = 0;
2731 game.switchgate_pos = 0;
2732 game.wind_direction = level.wind_direction_initial;
2734 #if !USE_PLAYER_GRAVITY
2735 game.gravity = FALSE;
2736 game.explosions_delayed = TRUE;
2739 game.lenses_time_left = 0;
2740 game.magnify_time_left = 0;
2742 game.ball_state = level.ball_state_initial;
2743 game.ball_content_nr = 0;
2745 game.envelope_active = FALSE;
2747 /* set focus to local player for network games, else to all players */
2748 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2749 game.centered_player_nr_next = game.centered_player_nr;
2750 game.set_centered_player = FALSE;
2752 if (network_playing && tape.recording)
2754 /* store client dependent player focus when recording network games */
2755 tape.centered_player_nr_next = game.centered_player_nr_next;
2756 tape.set_centered_player = TRUE;
2759 for (i = 0; i < NUM_BELTS; i++)
2761 game.belt_dir[i] = MV_NONE;
2762 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2765 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2766 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2768 SCAN_PLAYFIELD(x, y)
2770 Feld[x][y] = level.field[x][y];
2771 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2772 ChangeDelay[x][y] = 0;
2773 ChangePage[x][y] = -1;
2774 #if USE_NEW_CUSTOM_VALUE
2775 CustomValue[x][y] = 0; /* initialized in InitField() */
2777 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2779 WasJustMoving[x][y] = 0;
2780 WasJustFalling[x][y] = 0;
2781 CheckCollision[x][y] = 0;
2782 CheckImpact[x][y] = 0;
2784 Pushed[x][y] = FALSE;
2786 ChangeCount[x][y] = 0;
2787 ChangeEvent[x][y] = -1;
2789 ExplodePhase[x][y] = 0;
2790 ExplodeDelay[x][y] = 0;
2791 ExplodeField[x][y] = EX_TYPE_NONE;
2793 RunnerVisit[x][y] = 0;
2794 PlayerVisit[x][y] = 0;
2797 GfxRandom[x][y] = INIT_GFX_RANDOM();
2798 GfxElement[x][y] = EL_UNDEFINED;
2799 GfxAction[x][y] = ACTION_DEFAULT;
2800 GfxDir[x][y] = MV_NONE;
2803 SCAN_PLAYFIELD(x, y)
2805 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2807 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2809 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2812 InitField(x, y, TRUE);
2817 for (i = 0; i < MAX_PLAYERS; i++)
2819 struct PlayerInfo *player = &stored_player[i];
2821 /* set number of special actions for bored and sleeping animation */
2822 player->num_special_action_bored =
2823 get_num_special_action(player->artwork_element,
2824 ACTION_BORING_1, ACTION_BORING_LAST);
2825 player->num_special_action_sleeping =
2826 get_num_special_action(player->artwork_element,
2827 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2830 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2831 emulate_sb ? EMU_SOKOBAN :
2832 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2834 #if USE_NEW_ALL_SLIPPERY
2835 /* initialize type of slippery elements */
2836 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2838 if (!IS_CUSTOM_ELEMENT(i))
2840 /* default: elements slip down either to the left or right randomly */
2841 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2843 /* SP style elements prefer to slip down on the left side */
2844 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2845 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2847 /* BD style elements prefer to slip down on the left side */
2848 if (game.emulation == EMU_BOULDERDASH)
2849 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2854 /* initialize explosion and ignition delay */
2855 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2857 if (!IS_CUSTOM_ELEMENT(i))
2860 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2861 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2862 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2863 int last_phase = (num_phase + 1) * delay;
2864 int half_phase = (num_phase / 2) * delay;
2866 element_info[i].explosion_delay = last_phase - 1;
2867 element_info[i].ignition_delay = half_phase;
2869 if (i == EL_BLACK_ORB)
2870 element_info[i].ignition_delay = 1;
2874 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2875 element_info[i].explosion_delay = 1;
2877 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2878 element_info[i].ignition_delay = 1;
2882 /* correct non-moving belts to start moving left */
2883 for (i = 0; i < NUM_BELTS; i++)
2884 if (game.belt_dir[i] == MV_NONE)
2885 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2887 /* check if any connected player was not found in playfield */
2888 for (i = 0; i < MAX_PLAYERS; i++)
2890 struct PlayerInfo *player = &stored_player[i];
2892 if (player->connected && !player->present)
2894 for (j = 0; j < MAX_PLAYERS; j++)
2896 struct PlayerInfo *some_player = &stored_player[j];
2897 int jx = some_player->jx, jy = some_player->jy;
2899 /* assign first free player found that is present in the playfield */
2900 if (some_player->present && !some_player->connected)
2902 player->present = TRUE;
2903 player->active = TRUE;
2905 some_player->present = FALSE;
2906 some_player->active = FALSE;
2908 player->artwork_element = some_player->artwork_element;
2910 player->block_last_field = some_player->block_last_field;
2911 player->block_delay_adjustment = some_player->block_delay_adjustment;
2913 StorePlayer[jx][jy] = player->element_nr;
2914 player->jx = player->last_jx = jx;
2915 player->jy = player->last_jy = jy;
2925 /* when playing a tape, eliminate all players who do not participate */
2927 for (i = 0; i < MAX_PLAYERS; i++)
2929 if (stored_player[i].active && !tape.player_participates[i])
2931 struct PlayerInfo *player = &stored_player[i];
2932 int jx = player->jx, jy = player->jy;
2934 player->active = FALSE;
2935 StorePlayer[jx][jy] = 0;
2936 Feld[jx][jy] = EL_EMPTY;
2940 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2942 /* when in single player mode, eliminate all but the first active player */
2944 for (i = 0; i < MAX_PLAYERS; i++)
2946 if (stored_player[i].active)
2948 for (j = i + 1; j < MAX_PLAYERS; j++)
2950 if (stored_player[j].active)
2952 struct PlayerInfo *player = &stored_player[j];
2953 int jx = player->jx, jy = player->jy;
2955 player->active = FALSE;
2956 player->present = FALSE;
2958 StorePlayer[jx][jy] = 0;
2959 Feld[jx][jy] = EL_EMPTY;
2966 /* when recording the game, store which players take part in the game */
2969 for (i = 0; i < MAX_PLAYERS; i++)
2970 if (stored_player[i].active)
2971 tape.player_participates[i] = TRUE;
2976 for (i = 0; i < MAX_PLAYERS; i++)
2978 struct PlayerInfo *player = &stored_player[i];
2980 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2985 if (local_player == player)
2986 printf("Player %d is local player.\n", i+1);
2990 if (BorderElement == EL_EMPTY)
2993 SBX_Right = lev_fieldx - SCR_FIELDX;
2995 SBY_Lower = lev_fieldy - SCR_FIELDY;
3000 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3002 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3005 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3006 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3008 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3009 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3011 /* if local player not found, look for custom element that might create
3012 the player (make some assumptions about the right custom element) */
3013 if (!local_player->present)
3015 int start_x = 0, start_y = 0;
3016 int found_rating = 0;
3017 int found_element = EL_UNDEFINED;
3018 int player_nr = local_player->index_nr;
3020 SCAN_PLAYFIELD(x, y)
3022 int element = Feld[x][y];
3027 if (level.use_start_element[player_nr] &&
3028 level.start_element[player_nr] == element &&
3035 found_element = element;
3038 if (!IS_CUSTOM_ELEMENT(element))
3041 if (CAN_CHANGE(element))
3043 for (i = 0; i < element_info[element].num_change_pages; i++)
3045 /* check for player created from custom element as single target */
3046 content = element_info[element].change_page[i].target_element;
3047 is_player = ELEM_IS_PLAYER(content);
3049 if (is_player && (found_rating < 3 || element < found_element))
3055 found_element = element;
3060 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3062 /* check for player created from custom element as explosion content */
3063 content = element_info[element].content.e[xx][yy];
3064 is_player = ELEM_IS_PLAYER(content);
3066 if (is_player && (found_rating < 2 || element < found_element))
3068 start_x = x + xx - 1;
3069 start_y = y + yy - 1;
3072 found_element = element;
3075 if (!CAN_CHANGE(element))
3078 for (i = 0; i < element_info[element].num_change_pages; i++)
3080 /* check for player created from custom element as extended target */
3082 element_info[element].change_page[i].target_content.e[xx][yy];
3084 is_player = ELEM_IS_PLAYER(content);
3086 if (is_player && (found_rating < 1 || element < found_element))
3088 start_x = x + xx - 1;
3089 start_y = y + yy - 1;
3092 found_element = element;
3098 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3099 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3102 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3103 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3108 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3109 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3110 local_player->jx - MIDPOSX);
3112 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3113 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3114 local_player->jy - MIDPOSY);
3119 if (!game.restart_level)
3120 CloseDoor(DOOR_CLOSE_1);
3123 FadeOut(REDRAW_FIELD);
3125 /* !!! FIX THIS (START) !!! */
3126 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3128 InitGameEngine_EM();
3130 /* blit playfield from scroll buffer to normal back buffer for fading in */
3131 BlitScreenToBitmap_EM(backbuffer);
3138 /* after drawing the level, correct some elements */
3139 if (game.timegate_time_left == 0)
3140 CloseAllOpenTimegates();
3142 /* blit playfield from scroll buffer to normal back buffer for fading in */
3143 if (setup.soft_scrolling)
3144 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3146 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3148 /* !!! FIX THIS (END) !!! */
3151 FadeIn(REDRAW_FIELD);
3155 if (!game.restart_level)
3157 /* copy default game door content to main double buffer */
3158 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3159 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3162 SetPanelBackground();
3163 SetDrawBackgroundMask(REDRAW_DOOR_1);
3165 DrawGameDoorValues();
3167 if (!game.restart_level)
3171 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3172 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3173 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3177 /* copy actual game door content to door double buffer for OpenDoor() */
3178 BlitBitmap(drawto, bitmap_db_door,
3179 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3181 OpenDoor(DOOR_OPEN_ALL);
3183 PlaySound(SND_GAME_STARTING);
3185 if (setup.sound_music)
3188 KeyboardAutoRepeatOffUnlessAutoplay();
3192 for (i = 0; i < MAX_PLAYERS; i++)
3193 printf("Player %d %sactive.\n",
3194 i + 1, (stored_player[i].active ? "" : "not "));
3205 game.restart_level = FALSE;
3208 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3210 /* this is used for non-R'n'D game engines to update certain engine values */
3212 /* needed to determine if sounds are played within the visible screen area */
3213 scroll_x = actual_scroll_x;
3214 scroll_y = actual_scroll_y;
3217 void InitMovDir(int x, int y)
3219 int i, element = Feld[x][y];
3220 static int xy[4][2] =
3227 static int direction[3][4] =
3229 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3230 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3231 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3240 Feld[x][y] = EL_BUG;
3241 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3244 case EL_SPACESHIP_RIGHT:
3245 case EL_SPACESHIP_UP:
3246 case EL_SPACESHIP_LEFT:
3247 case EL_SPACESHIP_DOWN:
3248 Feld[x][y] = EL_SPACESHIP;
3249 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3252 case EL_BD_BUTTERFLY_RIGHT:
3253 case EL_BD_BUTTERFLY_UP:
3254 case EL_BD_BUTTERFLY_LEFT:
3255 case EL_BD_BUTTERFLY_DOWN:
3256 Feld[x][y] = EL_BD_BUTTERFLY;
3257 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3260 case EL_BD_FIREFLY_RIGHT:
3261 case EL_BD_FIREFLY_UP:
3262 case EL_BD_FIREFLY_LEFT:
3263 case EL_BD_FIREFLY_DOWN:
3264 Feld[x][y] = EL_BD_FIREFLY;
3265 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3268 case EL_PACMAN_RIGHT:
3270 case EL_PACMAN_LEFT:
3271 case EL_PACMAN_DOWN:
3272 Feld[x][y] = EL_PACMAN;
3273 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3276 case EL_YAMYAM_LEFT:
3277 case EL_YAMYAM_RIGHT:
3279 case EL_YAMYAM_DOWN:
3280 Feld[x][y] = EL_YAMYAM;
3281 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3284 case EL_SP_SNIKSNAK:
3285 MovDir[x][y] = MV_UP;
3288 case EL_SP_ELECTRON:
3289 MovDir[x][y] = MV_LEFT;
3296 Feld[x][y] = EL_MOLE;
3297 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3301 if (IS_CUSTOM_ELEMENT(element))
3303 struct ElementInfo *ei = &element_info[element];
3304 int move_direction_initial = ei->move_direction_initial;
3305 int move_pattern = ei->move_pattern;
3307 if (move_direction_initial == MV_START_PREVIOUS)
3309 if (MovDir[x][y] != MV_NONE)
3312 move_direction_initial = MV_START_AUTOMATIC;
3315 if (move_direction_initial == MV_START_RANDOM)
3316 MovDir[x][y] = 1 << RND(4);
3317 else if (move_direction_initial & MV_ANY_DIRECTION)
3318 MovDir[x][y] = move_direction_initial;
3319 else if (move_pattern == MV_ALL_DIRECTIONS ||
3320 move_pattern == MV_TURNING_LEFT ||
3321 move_pattern == MV_TURNING_RIGHT ||
3322 move_pattern == MV_TURNING_LEFT_RIGHT ||
3323 move_pattern == MV_TURNING_RIGHT_LEFT ||
3324 move_pattern == MV_TURNING_RANDOM)
3325 MovDir[x][y] = 1 << RND(4);
3326 else if (move_pattern == MV_HORIZONTAL)
3327 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3328 else if (move_pattern == MV_VERTICAL)
3329 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3330 else if (move_pattern & MV_ANY_DIRECTION)
3331 MovDir[x][y] = element_info[element].move_pattern;
3332 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3333 move_pattern == MV_ALONG_RIGHT_SIDE)
3335 /* use random direction as default start direction */
3336 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3337 MovDir[x][y] = 1 << RND(4);
3339 for (i = 0; i < NUM_DIRECTIONS; i++)
3341 int x1 = x + xy[i][0];
3342 int y1 = y + xy[i][1];
3344 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3346 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3347 MovDir[x][y] = direction[0][i];
3349 MovDir[x][y] = direction[1][i];
3358 MovDir[x][y] = 1 << RND(4);
3360 if (element != EL_BUG &&
3361 element != EL_SPACESHIP &&
3362 element != EL_BD_BUTTERFLY &&
3363 element != EL_BD_FIREFLY)
3366 for (i = 0; i < NUM_DIRECTIONS; i++)
3368 int x1 = x + xy[i][0];
3369 int y1 = y + xy[i][1];
3371 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3373 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3375 MovDir[x][y] = direction[0][i];
3378 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3379 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3381 MovDir[x][y] = direction[1][i];
3390 GfxDir[x][y] = MovDir[x][y];
3393 void InitAmoebaNr(int x, int y)
3396 int group_nr = AmoebeNachbarNr(x, y);
3400 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3402 if (AmoebaCnt[i] == 0)
3410 AmoebaNr[x][y] = group_nr;
3411 AmoebaCnt[group_nr]++;
3412 AmoebaCnt2[group_nr]++;
3415 static void PlayerWins(struct PlayerInfo *player)
3417 player->LevelSolved = TRUE;
3418 player->GameOver = TRUE;
3420 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3421 level.native_em_level->lev->score : player->score);
3426 static int time, time_final;
3427 static int score, score_final;
3428 static int game_over_delay_1 = 0;
3429 static int game_over_delay_2 = 0;
3430 int game_over_delay_value_1 = 50;
3431 int game_over_delay_value_2 = 50;
3433 if (!local_player->LevelSolved_GameWon)
3437 /* do not start end game actions before the player stops moving (to exit) */
3438 if (local_player->MovPos)
3441 local_player->LevelSolved_GameWon = TRUE;
3442 local_player->LevelSolved_SaveTape = tape.recording;
3443 local_player->LevelSolved_SaveScore = !tape.playing;
3445 if (tape.auto_play) /* tape might already be stopped here */
3446 tape.auto_play_level_solved = TRUE;
3452 game_over_delay_1 = game_over_delay_value_1;
3453 game_over_delay_2 = game_over_delay_value_2;
3455 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3456 score = score_final = local_player->score_final;
3461 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3463 else if (level.time == 0 && TimePlayed < 999)
3466 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3469 local_player->score_final = score_final;
3471 if (level_editor_test_game)
3474 score = score_final;
3476 DrawGameValue_Time(time);
3477 DrawGameValue_Score(score);
3480 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3482 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3484 /* close exit door after last player */
3485 if ((AllPlayersGone &&
3486 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3487 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3488 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3489 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3490 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3492 int element = Feld[ExitX][ExitY];
3495 if (element == EL_EM_EXIT_OPEN ||
3496 element == EL_EM_STEEL_EXIT_OPEN)
3503 Feld[ExitX][ExitY] =
3504 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3505 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3506 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3507 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3508 EL_EM_STEEL_EXIT_CLOSING);
3510 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3514 /* player disappears */
3515 DrawLevelField(ExitX, ExitY);
3518 for (i = 0; i < MAX_PLAYERS; i++)
3520 struct PlayerInfo *player = &stored_player[i];
3522 if (player->present)
3524 RemovePlayer(player);
3526 /* player disappears */
3527 DrawLevelField(player->jx, player->jy);
3532 PlaySound(SND_GAME_WINNING);
3535 if (game_over_delay_1 > 0)
3537 game_over_delay_1--;
3542 if (time != time_final)
3544 int time_to_go = ABS(time_final - time);
3545 int time_count_dir = (time < time_final ? +1 : -1);
3546 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3548 time += time_count_steps * time_count_dir;
3549 score += time_count_steps * level.score[SC_TIME_BONUS];
3551 DrawGameValue_Time(time);
3552 DrawGameValue_Score(score);
3554 if (time == time_final)
3555 StopSound(SND_GAME_LEVELTIME_BONUS);
3556 else if (setup.sound_loops)
3557 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3559 PlaySound(SND_GAME_LEVELTIME_BONUS);
3564 local_player->LevelSolved_PanelOff = TRUE;
3566 if (game_over_delay_2 > 0)
3568 game_over_delay_2--;
3581 boolean raise_level = FALSE;
3583 local_player->LevelSolved_GameEnd = TRUE;
3585 CloseDoor(DOOR_CLOSE_1);
3587 if (local_player->LevelSolved_SaveTape)
3594 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3596 SaveTape(tape.level_nr); /* ask to save tape */
3600 if (level_editor_test_game)
3602 game_status = GAME_MODE_MAIN;
3609 if (!local_player->LevelSolved_SaveScore)
3611 FadeOut(REDRAW_FIELD);
3613 game_status = GAME_MODE_MAIN;
3615 DrawAndFadeInMainMenu(REDRAW_FIELD);
3620 if (level_nr == leveldir_current->handicap_level)
3622 leveldir_current->handicap_level++;
3623 SaveLevelSetup_SeriesInfo();
3626 if (level_nr < leveldir_current->last_level)
3627 raise_level = TRUE; /* advance to next level */
3629 if ((hi_pos = NewHiScore()) >= 0)
3631 game_status = GAME_MODE_SCORES;
3633 DrawHallOfFame(hi_pos);
3643 FadeOut(REDRAW_FIELD);
3645 game_status = GAME_MODE_MAIN;
3653 DrawAndFadeInMainMenu(REDRAW_FIELD);
3662 LoadScore(level_nr);
3664 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3665 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3668 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3670 if (local_player->score_final > highscore[k].Score)
3672 /* player has made it to the hall of fame */
3674 if (k < MAX_SCORE_ENTRIES - 1)
3676 int m = MAX_SCORE_ENTRIES - 1;
3679 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3680 if (strEqual(setup.player_name, highscore[l].Name))
3682 if (m == k) /* player's new highscore overwrites his old one */
3686 for (l = m; l > k; l--)
3688 strcpy(highscore[l].Name, highscore[l - 1].Name);
3689 highscore[l].Score = highscore[l - 1].Score;
3696 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3697 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3698 highscore[k].Score = local_player->score_final;
3704 else if (!strncmp(setup.player_name, highscore[k].Name,
3705 MAX_PLAYER_NAME_LEN))
3706 break; /* player already there with a higher score */
3712 SaveScore(level_nr);
3717 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3719 int element = Feld[x][y];
3720 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3721 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3722 int horiz_move = (dx != 0);
3723 int sign = (horiz_move ? dx : dy);
3724 int step = sign * element_info[element].move_stepsize;
3726 /* special values for move stepsize for spring and things on conveyor belt */
3729 if (CAN_FALL(element) &&
3730 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3731 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3732 else if (element == EL_SPRING)
3733 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3739 inline static int getElementMoveStepsize(int x, int y)
3741 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3744 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3746 if (player->GfxAction != action || player->GfxDir != dir)
3749 printf("Player frame reset! (%d => %d, %d => %d)\n",
3750 player->GfxAction, action, player->GfxDir, dir);
3753 player->GfxAction = action;
3754 player->GfxDir = dir;
3756 player->StepFrame = 0;
3760 #if USE_GFX_RESET_GFX_ANIMATION
3761 static void ResetGfxFrame(int x, int y, boolean redraw)
3763 int element = Feld[x][y];
3764 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3765 int last_gfx_frame = GfxFrame[x][y];
3767 if (graphic_info[graphic].anim_global_sync)
3768 GfxFrame[x][y] = FrameCounter;
3769 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3770 GfxFrame[x][y] = CustomValue[x][y];
3771 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3772 GfxFrame[x][y] = element_info[element].collect_score;
3773 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3774 GfxFrame[x][y] = ChangeDelay[x][y];
3776 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3777 DrawLevelGraphicAnimation(x, y, graphic);
3781 static void ResetGfxAnimation(int x, int y)
3783 GfxAction[x][y] = ACTION_DEFAULT;
3784 GfxDir[x][y] = MovDir[x][y];
3787 #if USE_GFX_RESET_GFX_ANIMATION
3788 ResetGfxFrame(x, y, FALSE);
3792 static void ResetRandomAnimationValue(int x, int y)
3794 GfxRandom[x][y] = INIT_GFX_RANDOM();
3797 void InitMovingField(int x, int y, int direction)
3799 int element = Feld[x][y];
3800 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3801 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3804 boolean is_moving_before, is_moving_after;
3806 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3809 /* check if element was/is moving or being moved before/after mode change */
3812 is_moving_before = (WasJustMoving[x][y] != 0);
3814 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3815 is_moving_before = WasJustMoving[x][y];
3818 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3820 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3822 /* reset animation only for moving elements which change direction of moving
3823 or which just started or stopped moving
3824 (else CEs with property "can move" / "not moving" are reset each frame) */
3825 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3827 if (is_moving_before != is_moving_after ||
3828 direction != MovDir[x][y])
3829 ResetGfxAnimation(x, y);
3831 if ((is_moving_before || is_moving_after) && !continues_moving)
3832 ResetGfxAnimation(x, y);
3835 if (!continues_moving)
3836 ResetGfxAnimation(x, y);
3839 MovDir[x][y] = direction;
3840 GfxDir[x][y] = direction;
3842 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3843 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3844 direction == MV_DOWN && CAN_FALL(element) ?
3845 ACTION_FALLING : ACTION_MOVING);
3847 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3848 ACTION_FALLING : ACTION_MOVING);
3851 /* this is needed for CEs with property "can move" / "not moving" */
3853 if (is_moving_after)
3855 if (Feld[newx][newy] == EL_EMPTY)
3856 Feld[newx][newy] = EL_BLOCKED;
3858 MovDir[newx][newy] = MovDir[x][y];
3860 #if USE_NEW_CUSTOM_VALUE
3861 CustomValue[newx][newy] = CustomValue[x][y];
3864 GfxFrame[newx][newy] = GfxFrame[x][y];
3865 GfxRandom[newx][newy] = GfxRandom[x][y];
3866 GfxAction[newx][newy] = GfxAction[x][y];
3867 GfxDir[newx][newy] = GfxDir[x][y];
3871 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3873 int direction = MovDir[x][y];
3874 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3875 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3881 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3883 int oldx = x, oldy = y;
3884 int direction = MovDir[x][y];
3886 if (direction == MV_LEFT)
3888 else if (direction == MV_RIGHT)
3890 else if (direction == MV_UP)
3892 else if (direction == MV_DOWN)
3895 *comes_from_x = oldx;
3896 *comes_from_y = oldy;
3899 int MovingOrBlocked2Element(int x, int y)
3901 int element = Feld[x][y];
3903 if (element == EL_BLOCKED)
3907 Blocked2Moving(x, y, &oldx, &oldy);
3908 return Feld[oldx][oldy];
3914 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3916 /* like MovingOrBlocked2Element(), but if element is moving
3917 and (x,y) is the field the moving element is just leaving,
3918 return EL_BLOCKED instead of the element value */
3919 int element = Feld[x][y];
3921 if (IS_MOVING(x, y))
3923 if (element == EL_BLOCKED)
3927 Blocked2Moving(x, y, &oldx, &oldy);
3928 return Feld[oldx][oldy];
3937 static void RemoveField(int x, int y)
3939 Feld[x][y] = EL_EMPTY;
3945 #if USE_NEW_CUSTOM_VALUE
3946 CustomValue[x][y] = 0;
3950 ChangeDelay[x][y] = 0;
3951 ChangePage[x][y] = -1;
3952 Pushed[x][y] = FALSE;
3955 ExplodeField[x][y] = EX_TYPE_NONE;
3958 GfxElement[x][y] = EL_UNDEFINED;
3959 GfxAction[x][y] = ACTION_DEFAULT;
3960 GfxDir[x][y] = MV_NONE;
3963 void RemoveMovingField(int x, int y)
3965 int oldx = x, oldy = y, newx = x, newy = y;
3966 int element = Feld[x][y];
3967 int next_element = EL_UNDEFINED;
3969 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3972 if (IS_MOVING(x, y))
3974 Moving2Blocked(x, y, &newx, &newy);
3976 if (Feld[newx][newy] != EL_BLOCKED)
3978 /* element is moving, but target field is not free (blocked), but
3979 already occupied by something different (example: acid pool);
3980 in this case, only remove the moving field, but not the target */
3982 RemoveField(oldx, oldy);
3984 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3986 DrawLevelField(oldx, oldy);
3991 else if (element == EL_BLOCKED)
3993 Blocked2Moving(x, y, &oldx, &oldy);
3994 if (!IS_MOVING(oldx, oldy))
3998 if (element == EL_BLOCKED &&
3999 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4000 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4001 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4002 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4003 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4004 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4005 next_element = get_next_element(Feld[oldx][oldy]);
4007 RemoveField(oldx, oldy);
4008 RemoveField(newx, newy);
4010 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4012 if (next_element != EL_UNDEFINED)
4013 Feld[oldx][oldy] = next_element;
4015 DrawLevelField(oldx, oldy);
4016 DrawLevelField(newx, newy);
4019 void DrawDynamite(int x, int y)
4021 int sx = SCREENX(x), sy = SCREENY(y);
4022 int graphic = el2img(Feld[x][y]);
4025 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4028 if (IS_WALKABLE_INSIDE(Back[x][y]))
4032 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4033 else if (Store[x][y])
4034 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4036 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4038 if (Back[x][y] || Store[x][y])
4039 DrawGraphicThruMask(sx, sy, graphic, frame);
4041 DrawGraphic(sx, sy, graphic, frame);
4044 void CheckDynamite(int x, int y)
4046 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4050 if (MovDelay[x][y] != 0)
4053 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4059 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4064 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4066 boolean num_checked_players = 0;
4069 for (i = 0; i < MAX_PLAYERS; i++)
4071 if (stored_player[i].active)
4073 int sx = stored_player[i].jx;
4074 int sy = stored_player[i].jy;
4076 if (num_checked_players == 0)
4083 *sx1 = MIN(*sx1, sx);
4084 *sy1 = MIN(*sy1, sy);
4085 *sx2 = MAX(*sx2, sx);
4086 *sy2 = MAX(*sy2, sy);
4089 num_checked_players++;
4094 static boolean checkIfAllPlayersFitToScreen_RND()
4096 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4098 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4100 return (sx2 - sx1 < SCR_FIELDX &&
4101 sy2 - sy1 < SCR_FIELDY);
4104 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4106 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4108 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4110 *sx = (sx1 + sx2) / 2;
4111 *sy = (sy1 + sy2) / 2;
4114 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4115 boolean center_screen, boolean quick_relocation)
4117 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4118 boolean no_delay = (tape.warp_forward);
4119 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4120 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4122 if (quick_relocation)
4124 int offset = (setup.scroll_delay ? 3 : 0);
4126 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4130 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4131 x > SBX_Right + MIDPOSX ? SBX_Right :
4134 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4135 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4140 /* quick relocation (without scrolling), but do not center screen */
4142 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4143 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4146 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4147 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4150 int offset_x = x + (scroll_x - center_scroll_x);
4151 int offset_y = y + (scroll_y - center_scroll_y);
4153 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4154 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4155 offset_x - MIDPOSX);
4157 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4158 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4159 offset_y - MIDPOSY);
4164 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4165 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4166 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4168 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4169 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4170 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4172 /* don't scroll over playfield boundaries */
4173 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4174 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4176 /* don't scroll over playfield boundaries */
4177 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4178 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4181 RedrawPlayfield(TRUE, 0,0,0,0);
4185 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4186 x > SBX_Right + MIDPOSX ? SBX_Right :
4189 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4190 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4193 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4195 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4198 int fx = FX, fy = FY;
4200 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4201 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4203 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4209 fx += dx * TILEX / 2;
4210 fy += dy * TILEY / 2;
4212 ScrollLevel(dx, dy);
4215 /* scroll in two steps of half tile size to make things smoother */
4216 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4218 Delay(wait_delay_value);
4220 /* scroll second step to align at full tile size */
4222 Delay(wait_delay_value);
4227 Delay(wait_delay_value);
4231 void RelocatePlayer(int jx, int jy, int el_player_raw)
4233 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4234 int player_nr = GET_PLAYER_NR(el_player);
4235 struct PlayerInfo *player = &stored_player[player_nr];
4236 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4237 boolean no_delay = (tape.warp_forward);
4238 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4239 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4240 int old_jx = player->jx;
4241 int old_jy = player->jy;
4242 int old_element = Feld[old_jx][old_jy];
4243 int element = Feld[jx][jy];
4244 boolean player_relocated = (old_jx != jx || old_jy != jy);
4246 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4247 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4248 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4249 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4250 int leave_side_horiz = move_dir_horiz;
4251 int leave_side_vert = move_dir_vert;
4252 int enter_side = enter_side_horiz | enter_side_vert;
4253 int leave_side = leave_side_horiz | leave_side_vert;
4255 if (player->GameOver) /* do not reanimate dead player */
4258 if (!player_relocated) /* no need to relocate the player */
4261 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4263 RemoveField(jx, jy); /* temporarily remove newly placed player */
4264 DrawLevelField(jx, jy);
4267 if (player->present)
4269 while (player->MovPos)
4271 ScrollPlayer(player, SCROLL_GO_ON);
4272 ScrollScreen(NULL, SCROLL_GO_ON);
4274 AdvanceFrameAndPlayerCounters(player->index_nr);
4279 Delay(wait_delay_value);
4282 DrawPlayer(player); /* needed here only to cleanup last field */
4283 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4285 player->is_moving = FALSE;
4288 if (IS_CUSTOM_ELEMENT(old_element))
4289 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4291 player->index_bit, leave_side);
4293 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4295 player->index_bit, leave_side);
4297 Feld[jx][jy] = el_player;
4298 InitPlayerField(jx, jy, el_player, TRUE);
4300 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4302 Feld[jx][jy] = element;
4303 InitField(jx, jy, FALSE);
4306 /* only visually relocate centered player */
4307 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4308 FALSE, level.instant_relocation);
4310 TestIfPlayerTouchesBadThing(jx, jy);
4311 TestIfPlayerTouchesCustomElement(jx, jy);
4313 if (IS_CUSTOM_ELEMENT(element))
4314 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4315 player->index_bit, enter_side);
4317 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4318 player->index_bit, enter_side);
4321 void Explode(int ex, int ey, int phase, int mode)
4327 /* !!! eliminate this variable !!! */
4328 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4330 if (game.explosions_delayed)
4332 ExplodeField[ex][ey] = mode;
4336 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4338 int center_element = Feld[ex][ey];
4339 int artwork_element, explosion_element; /* set these values later */
4342 /* --- This is only really needed (and now handled) in "Impact()". --- */
4343 /* do not explode moving elements that left the explode field in time */
4344 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4345 center_element == EL_EMPTY &&
4346 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4351 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4352 if (mode == EX_TYPE_NORMAL ||
4353 mode == EX_TYPE_CENTER ||
4354 mode == EX_TYPE_CROSS)
4355 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4358 /* remove things displayed in background while burning dynamite */
4359 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4362 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4364 /* put moving element to center field (and let it explode there) */
4365 center_element = MovingOrBlocked2Element(ex, ey);
4366 RemoveMovingField(ex, ey);
4367 Feld[ex][ey] = center_element;
4370 /* now "center_element" is finally determined -- set related values now */
4371 artwork_element = center_element; /* for custom player artwork */
4372 explosion_element = center_element; /* for custom player artwork */
4374 if (IS_PLAYER(ex, ey))
4376 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4378 artwork_element = stored_player[player_nr].artwork_element;
4380 if (level.use_explosion_element[player_nr])
4382 explosion_element = level.explosion_element[player_nr];
4383 artwork_element = explosion_element;
4388 if (mode == EX_TYPE_NORMAL ||
4389 mode == EX_TYPE_CENTER ||
4390 mode == EX_TYPE_CROSS)
4391 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4394 last_phase = element_info[explosion_element].explosion_delay + 1;
4396 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4398 int xx = x - ex + 1;
4399 int yy = y - ey + 1;
4402 if (!IN_LEV_FIELD(x, y) ||
4403 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4404 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4407 element = Feld[x][y];
4409 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4411 element = MovingOrBlocked2Element(x, y);
4413 if (!IS_EXPLOSION_PROOF(element))
4414 RemoveMovingField(x, y);
4417 /* indestructible elements can only explode in center (but not flames) */
4418 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4419 mode == EX_TYPE_BORDER)) ||
4420 element == EL_FLAMES)
4423 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4424 behaviour, for example when touching a yamyam that explodes to rocks
4425 with active deadly shield, a rock is created under the player !!! */
4426 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4428 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4429 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4430 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4432 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4435 if (IS_ACTIVE_BOMB(element))
4437 /* re-activate things under the bomb like gate or penguin */
4438 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4445 /* save walkable background elements while explosion on same tile */
4446 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4447 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4448 Back[x][y] = element;
4450 /* ignite explodable elements reached by other explosion */
4451 if (element == EL_EXPLOSION)
4452 element = Store2[x][y];
4454 if (AmoebaNr[x][y] &&
4455 (element == EL_AMOEBA_FULL ||
4456 element == EL_BD_AMOEBA ||
4457 element == EL_AMOEBA_GROWING))
4459 AmoebaCnt[AmoebaNr[x][y]]--;
4460 AmoebaCnt2[AmoebaNr[x][y]]--;
4465 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4467 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4469 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4471 if (PLAYERINFO(ex, ey)->use_murphy)
4472 Store[x][y] = EL_EMPTY;
4475 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4476 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4477 else if (ELEM_IS_PLAYER(center_element))
4478 Store[x][y] = EL_EMPTY;
4479 else if (center_element == EL_YAMYAM)
4480 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4481 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4482 Store[x][y] = element_info[center_element].content.e[xx][yy];
4484 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4485 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4486 otherwise) -- FIX THIS !!! */
4487 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4488 Store[x][y] = element_info[element].content.e[1][1];
4490 else if (!CAN_EXPLODE(element))
4491 Store[x][y] = element_info[element].content.e[1][1];
4494 Store[x][y] = EL_EMPTY;
4496 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4497 center_element == EL_AMOEBA_TO_DIAMOND)
4498 Store2[x][y] = element;
4500 Feld[x][y] = EL_EXPLOSION;
4501 GfxElement[x][y] = artwork_element;
4503 ExplodePhase[x][y] = 1;
4504 ExplodeDelay[x][y] = last_phase;
4509 if (center_element == EL_YAMYAM)
4510 game.yamyam_content_nr =
4511 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4523 GfxFrame[x][y] = 0; /* restart explosion animation */
4525 last_phase = ExplodeDelay[x][y];
4527 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4531 /* activate this even in non-DEBUG version until cause for crash in
4532 getGraphicAnimationFrame() (see below) is found and eliminated */
4538 /* this can happen if the player leaves an explosion just in time */
4539 if (GfxElement[x][y] == EL_UNDEFINED)
4540 GfxElement[x][y] = EL_EMPTY;
4542 if (GfxElement[x][y] == EL_UNDEFINED)
4545 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4546 printf("Explode(): This should never happen!\n");
4549 GfxElement[x][y] = EL_EMPTY;
4555 border_element = Store2[x][y];
4556 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4557 border_element = StorePlayer[x][y];
4559 if (phase == element_info[border_element].ignition_delay ||
4560 phase == last_phase)
4562 boolean border_explosion = FALSE;
4564 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4565 !PLAYER_EXPLOSION_PROTECTED(x, y))
4567 KillPlayerUnlessExplosionProtected(x, y);
4568 border_explosion = TRUE;
4570 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4572 Feld[x][y] = Store2[x][y];
4575 border_explosion = TRUE;
4577 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4579 AmoebeUmwandeln(x, y);
4581 border_explosion = TRUE;
4584 /* if an element just explodes due to another explosion (chain-reaction),
4585 do not immediately end the new explosion when it was the last frame of
4586 the explosion (as it would be done in the following "if"-statement!) */
4587 if (border_explosion && phase == last_phase)
4591 if (phase == last_phase)
4595 element = Feld[x][y] = Store[x][y];
4596 Store[x][y] = Store2[x][y] = 0;
4597 GfxElement[x][y] = EL_UNDEFINED;
4599 /* player can escape from explosions and might therefore be still alive */
4600 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4601 element <= EL_PLAYER_IS_EXPLODING_4)
4603 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4604 int explosion_element = EL_PLAYER_1 + player_nr;
4605 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4606 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4608 if (level.use_explosion_element[player_nr])
4609 explosion_element = level.explosion_element[player_nr];
4611 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4612 element_info[explosion_element].content.e[xx][yy]);
4615 /* restore probably existing indestructible background element */
4616 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4617 element = Feld[x][y] = Back[x][y];
4620 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4621 GfxDir[x][y] = MV_NONE;
4622 ChangeDelay[x][y] = 0;
4623 ChangePage[x][y] = -1;
4625 #if USE_NEW_CUSTOM_VALUE
4626 CustomValue[x][y] = 0;
4629 InitField_WithBug2(x, y, FALSE);
4631 DrawLevelField(x, y);
4633 TestIfElementTouchesCustomElement(x, y);
4635 if (GFX_CRUMBLED(element))
4636 DrawLevelFieldCrumbledSandNeighbours(x, y);
4638 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4639 StorePlayer[x][y] = 0;
4641 if (ELEM_IS_PLAYER(element))
4642 RelocatePlayer(x, y, element);
4644 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4646 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4647 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4650 DrawLevelFieldCrumbledSand(x, y);
4652 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4654 DrawLevelElement(x, y, Back[x][y]);
4655 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4657 else if (IS_WALKABLE_UNDER(Back[x][y]))
4659 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4660 DrawLevelElementThruMask(x, y, Back[x][y]);
4662 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4663 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4667 void DynaExplode(int ex, int ey)
4670 int dynabomb_element = Feld[ex][ey];
4671 int dynabomb_size = 1;
4672 boolean dynabomb_xl = FALSE;
4673 struct PlayerInfo *player;
4674 static int xy[4][2] =
4682 if (IS_ACTIVE_BOMB(dynabomb_element))
4684 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4685 dynabomb_size = player->dynabomb_size;
4686 dynabomb_xl = player->dynabomb_xl;
4687 player->dynabombs_left++;
4690 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4692 for (i = 0; i < NUM_DIRECTIONS; i++)
4694 for (j = 1; j <= dynabomb_size; j++)
4696 int x = ex + j * xy[i][0];
4697 int y = ey + j * xy[i][1];
4700 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4703 element = Feld[x][y];
4705 /* do not restart explosions of fields with active bombs */
4706 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4709 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4711 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4712 !IS_DIGGABLE(element) && !dynabomb_xl)
4718 void Bang(int x, int y)
4720 int element = MovingOrBlocked2Element(x, y);
4721 int explosion_type = EX_TYPE_NORMAL;
4723 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4725 struct PlayerInfo *player = PLAYERINFO(x, y);
4727 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4728 player->element_nr);
4730 if (level.use_explosion_element[player->index_nr])
4732 int explosion_element = level.explosion_element[player->index_nr];
4734 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4735 explosion_type = EX_TYPE_CROSS;
4736 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4737 explosion_type = EX_TYPE_CENTER;
4745 case EL_BD_BUTTERFLY:
4748 case EL_DARK_YAMYAM:
4752 RaiseScoreElement(element);
4755 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4756 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4757 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4758 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4759 case EL_DYNABOMB_INCREASE_NUMBER:
4760 case EL_DYNABOMB_INCREASE_SIZE:
4761 case EL_DYNABOMB_INCREASE_POWER:
4762 explosion_type = EX_TYPE_DYNA;
4765 case EL_DC_LANDMINE:
4767 case EL_EM_EXIT_OPEN:
4768 case EL_EM_STEEL_EXIT_OPEN:
4770 explosion_type = EX_TYPE_CENTER;
4775 case EL_LAMP_ACTIVE:
4776 case EL_AMOEBA_TO_DIAMOND:
4777 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4778 explosion_type = EX_TYPE_CENTER;
4782 if (element_info[element].explosion_type == EXPLODES_CROSS)
4783 explosion_type = EX_TYPE_CROSS;
4784 else if (element_info[element].explosion_type == EXPLODES_1X1)
4785 explosion_type = EX_TYPE_CENTER;
4789 if (explosion_type == EX_TYPE_DYNA)
4792 Explode(x, y, EX_PHASE_START, explosion_type);
4794 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4797 void SplashAcid(int x, int y)
4799 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4800 (!IN_LEV_FIELD(x - 1, y - 2) ||
4801 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4802 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4804 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4805 (!IN_LEV_FIELD(x + 1, y - 2) ||
4806 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4807 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4809 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4812 static void InitBeltMovement()
4814 static int belt_base_element[4] =
4816 EL_CONVEYOR_BELT_1_LEFT,
4817 EL_CONVEYOR_BELT_2_LEFT,
4818 EL_CONVEYOR_BELT_3_LEFT,
4819 EL_CONVEYOR_BELT_4_LEFT
4821 static int belt_base_active_element[4] =
4823 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4824 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4825 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4826 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4831 /* set frame order for belt animation graphic according to belt direction */
4832 for (i = 0; i < NUM_BELTS; i++)
4836 for (j = 0; j < NUM_BELT_PARTS; j++)
4838 int element = belt_base_active_element[belt_nr] + j;
4839 int graphic = el2img(element);
4841 if (game.belt_dir[i] == MV_LEFT)
4842 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4844 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4848 SCAN_PLAYFIELD(x, y)
4850 int element = Feld[x][y];
4852 for (i = 0; i < NUM_BELTS; i++)
4854 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4856 int e_belt_nr = getBeltNrFromBeltElement(element);
4859 if (e_belt_nr == belt_nr)
4861 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4863 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4870 static void ToggleBeltSwitch(int x, int y)
4872 static int belt_base_element[4] =
4874 EL_CONVEYOR_BELT_1_LEFT,
4875 EL_CONVEYOR_BELT_2_LEFT,
4876 EL_CONVEYOR_BELT_3_LEFT,
4877 EL_CONVEYOR_BELT_4_LEFT
4879 static int belt_base_active_element[4] =
4881 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4882 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4883 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4884 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4886 static int belt_base_switch_element[4] =
4888 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4889 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4890 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4891 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4893 static int belt_move_dir[4] =
4901 int element = Feld[x][y];
4902 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4903 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4904 int belt_dir = belt_move_dir[belt_dir_nr];
4907 if (!IS_BELT_SWITCH(element))
4910 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4911 game.belt_dir[belt_nr] = belt_dir;
4913 if (belt_dir_nr == 3)
4916 /* set frame order for belt animation graphic according to belt direction */
4917 for (i = 0; i < NUM_BELT_PARTS; i++)
4919 int element = belt_base_active_element[belt_nr] + i;
4920 int graphic = el2img(element);
4922 if (belt_dir == MV_LEFT)
4923 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4925 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4928 SCAN_PLAYFIELD(xx, yy)
4930 int element = Feld[xx][yy];
4932 if (IS_BELT_SWITCH(element))
4934 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4936 if (e_belt_nr == belt_nr)
4938 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4939 DrawLevelField(xx, yy);
4942 else if (IS_BELT(element) && belt_dir != MV_NONE)
4944 int e_belt_nr = getBeltNrFromBeltElement(element);
4946 if (e_belt_nr == belt_nr)
4948 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4950 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4951 DrawLevelField(xx, yy);
4954 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4956 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4958 if (e_belt_nr == belt_nr)
4960 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4962 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4963 DrawLevelField(xx, yy);
4969 static void ToggleSwitchgateSwitch(int x, int y)
4973 game.switchgate_pos = !game.switchgate_pos;
4975 SCAN_PLAYFIELD(xx, yy)
4977 int element = Feld[xx][yy];
4979 #if !USE_BOTH_SWITCHGATE_SWITCHES
4980 if (element == EL_SWITCHGATE_SWITCH_UP ||
4981 element == EL_SWITCHGATE_SWITCH_DOWN)
4983 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4984 DrawLevelField(xx, yy);
4986 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4987 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4989 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4990 DrawLevelField(xx, yy);
4993 if (element == EL_SWITCHGATE_SWITCH_UP)
4995 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4996 DrawLevelField(xx, yy);
4998 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5000 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5001 DrawLevelField(xx, yy);
5003 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5005 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5006 DrawLevelField(xx, yy);
5008 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5010 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5011 DrawLevelField(xx, yy);
5014 else if (element == EL_SWITCHGATE_OPEN ||
5015 element == EL_SWITCHGATE_OPENING)
5017 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5019 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5021 else if (element == EL_SWITCHGATE_CLOSED ||
5022 element == EL_SWITCHGATE_CLOSING)
5024 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5026 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5031 static int getInvisibleActiveFromInvisibleElement(int element)
5033 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5034 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5035 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5039 static int getInvisibleFromInvisibleActiveElement(int element)
5041 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5042 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5043 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5047 static void RedrawAllLightSwitchesAndInvisibleElements()
5051 SCAN_PLAYFIELD(x, y)
5053 int element = Feld[x][y];
5055 if (element == EL_LIGHT_SWITCH &&
5056 game.light_time_left > 0)
5058 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5059 DrawLevelField(x, y);
5061 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5062 game.light_time_left == 0)
5064 Feld[x][y] = EL_LIGHT_SWITCH;
5065 DrawLevelField(x, y);
5067 else if (element == EL_EMC_DRIPPER &&
5068 game.light_time_left > 0)
5070 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5071 DrawLevelField(x, y);
5073 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5074 game.light_time_left == 0)
5076 Feld[x][y] = EL_EMC_DRIPPER;
5077 DrawLevelField(x, y);
5079 else if (element == EL_INVISIBLE_STEELWALL ||
5080 element == EL_INVISIBLE_WALL ||
5081 element == EL_INVISIBLE_SAND)
5083 if (game.light_time_left > 0)
5084 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5086 DrawLevelField(x, y);
5088 /* uncrumble neighbour fields, if needed */
5089 if (element == EL_INVISIBLE_SAND)
5090 DrawLevelFieldCrumbledSandNeighbours(x, y);
5092 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5093 element == EL_INVISIBLE_WALL_ACTIVE ||
5094 element == EL_INVISIBLE_SAND_ACTIVE)
5096 if (game.light_time_left == 0)
5097 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5099 DrawLevelField(x, y);
5101 /* re-crumble neighbour fields, if needed */
5102 if (element == EL_INVISIBLE_SAND)
5103 DrawLevelFieldCrumbledSandNeighbours(x, y);
5108 static void RedrawAllInvisibleElementsForLenses()
5112 SCAN_PLAYFIELD(x, y)
5114 int element = Feld[x][y];
5116 if (element == EL_EMC_DRIPPER &&
5117 game.lenses_time_left > 0)
5119 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5120 DrawLevelField(x, y);
5122 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5123 game.lenses_time_left == 0)
5125 Feld[x][y] = EL_EMC_DRIPPER;
5126 DrawLevelField(x, y);
5128 else if (element == EL_INVISIBLE_STEELWALL ||
5129 element == EL_INVISIBLE_WALL ||
5130 element == EL_INVISIBLE_SAND)
5132 if (game.lenses_time_left > 0)
5133 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5135 DrawLevelField(x, y);
5137 /* uncrumble neighbour fields, if needed */
5138 if (element == EL_INVISIBLE_SAND)
5139 DrawLevelFieldCrumbledSandNeighbours(x, y);
5141 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5142 element == EL_INVISIBLE_WALL_ACTIVE ||
5143 element == EL_INVISIBLE_SAND_ACTIVE)
5145 if (game.lenses_time_left == 0)
5146 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5148 DrawLevelField(x, y);
5150 /* re-crumble neighbour fields, if needed */
5151 if (element == EL_INVISIBLE_SAND)
5152 DrawLevelFieldCrumbledSandNeighbours(x, y);
5157 static void RedrawAllInvisibleElementsForMagnifier()
5161 SCAN_PLAYFIELD(x, y)
5163 int element = Feld[x][y];
5165 if (element == EL_EMC_FAKE_GRASS &&
5166 game.magnify_time_left > 0)
5168 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5169 DrawLevelField(x, y);
5171 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5172 game.magnify_time_left == 0)
5174 Feld[x][y] = EL_EMC_FAKE_GRASS;
5175 DrawLevelField(x, y);
5177 else if (IS_GATE_GRAY(element) &&
5178 game.magnify_time_left > 0)
5180 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5181 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5182 IS_EM_GATE_GRAY(element) ?
5183 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5184 IS_EMC_GATE_GRAY(element) ?
5185 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5187 DrawLevelField(x, y);
5189 else if (IS_GATE_GRAY_ACTIVE(element) &&
5190 game.magnify_time_left == 0)
5192 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5193 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5194 IS_EM_GATE_GRAY_ACTIVE(element) ?
5195 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5196 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5197 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5199 DrawLevelField(x, y);
5204 static void ToggleLightSwitch(int x, int y)
5206 int element = Feld[x][y];
5208 game.light_time_left =
5209 (element == EL_LIGHT_SWITCH ?
5210 level.time_light * FRAMES_PER_SECOND : 0);
5212 RedrawAllLightSwitchesAndInvisibleElements();
5215 static void ActivateTimegateSwitch(int x, int y)
5219 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5221 SCAN_PLAYFIELD(xx, yy)
5223 int element = Feld[xx][yy];
5225 if (element == EL_TIMEGATE_CLOSED ||
5226 element == EL_TIMEGATE_CLOSING)
5228 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5229 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5233 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5235 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5236 DrawLevelField(xx, yy);
5243 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5244 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5246 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5250 void Impact(int x, int y)
5252 boolean last_line = (y == lev_fieldy - 1);
5253 boolean object_hit = FALSE;
5254 boolean impact = (last_line || object_hit);
5255 int element = Feld[x][y];
5256 int smashed = EL_STEELWALL;
5258 if (!last_line) /* check if element below was hit */
5260 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5263 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5264 MovDir[x][y + 1] != MV_DOWN ||
5265 MovPos[x][y + 1] <= TILEY / 2));
5267 /* do not smash moving elements that left the smashed field in time */
5268 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5269 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5272 #if USE_QUICKSAND_IMPACT_BUGFIX
5273 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5275 RemoveMovingField(x, y + 1);
5276 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5277 Feld[x][y + 2] = EL_ROCK;
5278 DrawLevelField(x, y + 2);
5283 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5285 RemoveMovingField(x, y + 1);
5286 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5287 Feld[x][y + 2] = EL_ROCK;
5288 DrawLevelField(x, y + 2);
5295 smashed = MovingOrBlocked2Element(x, y + 1);
5297 impact = (last_line || object_hit);
5300 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5302 SplashAcid(x, y + 1);
5306 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5307 /* only reset graphic animation if graphic really changes after impact */
5309 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5311 ResetGfxAnimation(x, y);
5312 DrawLevelField(x, y);
5315 if (impact && CAN_EXPLODE_IMPACT(element))
5320 else if (impact && element == EL_PEARL &&
5321 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5323 ResetGfxAnimation(x, y);
5325 Feld[x][y] = EL_PEARL_BREAKING;
5326 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5329 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5331 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5336 if (impact && element == EL_AMOEBA_DROP)
5338 if (object_hit && IS_PLAYER(x, y + 1))
5339 KillPlayerUnlessEnemyProtected(x, y + 1);
5340 else if (object_hit && smashed == EL_PENGUIN)
5344 Feld[x][y] = EL_AMOEBA_GROWING;
5345 Store[x][y] = EL_AMOEBA_WET;
5347 ResetRandomAnimationValue(x, y);
5352 if (object_hit) /* check which object was hit */
5354 if ((CAN_PASS_MAGIC_WALL(element) &&
5355 (smashed == EL_MAGIC_WALL ||
5356 smashed == EL_BD_MAGIC_WALL)) ||
5357 (CAN_PASS_DC_MAGIC_WALL(element) &&
5358 smashed == EL_DC_MAGIC_WALL))
5361 int activated_magic_wall =
5362 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5363 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5364 EL_DC_MAGIC_WALL_ACTIVE);
5366 /* activate magic wall / mill */
5367 SCAN_PLAYFIELD(xx, yy)
5369 if (Feld[xx][yy] == smashed)
5370 Feld[xx][yy] = activated_magic_wall;
5373 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5374 game.magic_wall_active = TRUE;
5376 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5377 SND_MAGIC_WALL_ACTIVATING :
5378 smashed == EL_BD_MAGIC_WALL ?
5379 SND_BD_MAGIC_WALL_ACTIVATING :
5380 SND_DC_MAGIC_WALL_ACTIVATING));
5383 if (IS_PLAYER(x, y + 1))
5385 if (CAN_SMASH_PLAYER(element))
5387 KillPlayerUnlessEnemyProtected(x, y + 1);
5391 else if (smashed == EL_PENGUIN)
5393 if (CAN_SMASH_PLAYER(element))
5399 else if (element == EL_BD_DIAMOND)
5401 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5407 else if (((element == EL_SP_INFOTRON ||
5408 element == EL_SP_ZONK) &&
5409 (smashed == EL_SP_SNIKSNAK ||
5410 smashed == EL_SP_ELECTRON ||
5411 smashed == EL_SP_DISK_ORANGE)) ||
5412 (element == EL_SP_INFOTRON &&
5413 smashed == EL_SP_DISK_YELLOW))
5418 else if (CAN_SMASH_EVERYTHING(element))
5420 if (IS_CLASSIC_ENEMY(smashed) ||
5421 CAN_EXPLODE_SMASHED(smashed))
5426 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5428 if (smashed == EL_LAMP ||
5429 smashed == EL_LAMP_ACTIVE)
5434 else if (smashed == EL_NUT)
5436 Feld[x][y + 1] = EL_NUT_BREAKING;
5437 PlayLevelSound(x, y, SND_NUT_BREAKING);
5438 RaiseScoreElement(EL_NUT);
5441 else if (smashed == EL_PEARL)
5443 ResetGfxAnimation(x, y);
5445 Feld[x][y + 1] = EL_PEARL_BREAKING;
5446 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5449 else if (smashed == EL_DIAMOND)
5451 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5452 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5455 else if (IS_BELT_SWITCH(smashed))
5457 ToggleBeltSwitch(x, y + 1);
5459 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5460 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5461 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5462 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5464 ToggleSwitchgateSwitch(x, y + 1);
5466 else if (smashed == EL_LIGHT_SWITCH ||
5467 smashed == EL_LIGHT_SWITCH_ACTIVE)
5469 ToggleLightSwitch(x, y + 1);
5474 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5477 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5479 CheckElementChangeBySide(x, y + 1, smashed, element,
5480 CE_SWITCHED, CH_SIDE_TOP);
5481 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5487 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5492 /* play sound of magic wall / mill */
5494 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5495 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5496 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5498 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5499 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5500 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5501 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5502 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5503 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5508 /* play sound of object that hits the ground */
5509 if (last_line || object_hit)
5510 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5513 inline static void TurnRoundExt(int x, int y)
5525 { 0, 0 }, { 0, 0 }, { 0, 0 },
5530 int left, right, back;
5534 { MV_DOWN, MV_UP, MV_RIGHT },
5535 { MV_UP, MV_DOWN, MV_LEFT },
5537 { MV_LEFT, MV_RIGHT, MV_DOWN },
5541 { MV_RIGHT, MV_LEFT, MV_UP }
5544 int element = Feld[x][y];
5545 int move_pattern = element_info[element].move_pattern;
5547 int old_move_dir = MovDir[x][y];
5548 int left_dir = turn[old_move_dir].left;
5549 int right_dir = turn[old_move_dir].right;
5550 int back_dir = turn[old_move_dir].back;
5552 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5553 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5554 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5555 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5557 int left_x = x + left_dx, left_y = y + left_dy;
5558 int right_x = x + right_dx, right_y = y + right_dy;
5559 int move_x = x + move_dx, move_y = y + move_dy;
5563 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5565 TestIfBadThingTouchesOtherBadThing(x, y);
5567 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5568 MovDir[x][y] = right_dir;
5569 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5570 MovDir[x][y] = left_dir;
5572 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5574 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5577 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5579 TestIfBadThingTouchesOtherBadThing(x, y);
5581 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5582 MovDir[x][y] = left_dir;
5583 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5584 MovDir[x][y] = right_dir;
5586 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5588 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5591 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5593 TestIfBadThingTouchesOtherBadThing(x, y);
5595 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5596 MovDir[x][y] = left_dir;
5597 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5598 MovDir[x][y] = right_dir;
5600 if (MovDir[x][y] != old_move_dir)
5603 else if (element == EL_YAMYAM)
5605 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5606 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5608 if (can_turn_left && can_turn_right)
5609 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5610 else if (can_turn_left)
5611 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5612 else if (can_turn_right)
5613 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5615 MovDir[x][y] = back_dir;
5617 MovDelay[x][y] = 16 + 16 * RND(3);
5619 else if (element == EL_DARK_YAMYAM)
5621 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5623 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5626 if (can_turn_left && can_turn_right)
5627 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5628 else if (can_turn_left)
5629 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5630 else if (can_turn_right)
5631 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5633 MovDir[x][y] = back_dir;
5635 MovDelay[x][y] = 16 + 16 * RND(3);
5637 else if (element == EL_PACMAN)
5639 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5640 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5642 if (can_turn_left && can_turn_right)
5643 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5644 else if (can_turn_left)
5645 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5646 else if (can_turn_right)
5647 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5649 MovDir[x][y] = back_dir;
5651 MovDelay[x][y] = 6 + RND(40);
5653 else if (element == EL_PIG)
5655 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5656 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5657 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5658 boolean should_turn_left, should_turn_right, should_move_on;
5660 int rnd = RND(rnd_value);
5662 should_turn_left = (can_turn_left &&
5664 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5665 y + back_dy + left_dy)));
5666 should_turn_right = (can_turn_right &&
5668 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5669 y + back_dy + right_dy)));
5670 should_move_on = (can_move_on &&
5673 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5674 y + move_dy + left_dy) ||
5675 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5676 y + move_dy + right_dy)));
5678 if (should_turn_left || should_turn_right || should_move_on)
5680 if (should_turn_left && should_turn_right && should_move_on)
5681 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5682 rnd < 2 * rnd_value / 3 ? right_dir :
5684 else if (should_turn_left && should_turn_right)
5685 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5686 else if (should_turn_left && should_move_on)
5687 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5688 else if (should_turn_right && should_move_on)
5689 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5690 else if (should_turn_left)
5691 MovDir[x][y] = left_dir;
5692 else if (should_turn_right)
5693 MovDir[x][y] = right_dir;
5694 else if (should_move_on)
5695 MovDir[x][y] = old_move_dir;
5697 else if (can_move_on && rnd > rnd_value / 8)
5698 MovDir[x][y] = old_move_dir;
5699 else if (can_turn_left && can_turn_right)
5700 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5701 else if (can_turn_left && rnd > rnd_value / 8)
5702 MovDir[x][y] = left_dir;
5703 else if (can_turn_right && rnd > rnd_value/8)
5704 MovDir[x][y] = right_dir;
5706 MovDir[x][y] = back_dir;
5708 xx = x + move_xy[MovDir[x][y]].dx;
5709 yy = y + move_xy[MovDir[x][y]].dy;
5711 if (!IN_LEV_FIELD(xx, yy) ||
5712 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5713 MovDir[x][y] = old_move_dir;
5717 else if (element == EL_DRAGON)
5719 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5720 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5721 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5723 int rnd = RND(rnd_value);
5725 if (can_move_on && rnd > rnd_value / 8)
5726 MovDir[x][y] = old_move_dir;
5727 else if (can_turn_left && can_turn_right)
5728 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5729 else if (can_turn_left && rnd > rnd_value / 8)
5730 MovDir[x][y] = left_dir;
5731 else if (can_turn_right && rnd > rnd_value / 8)
5732 MovDir[x][y] = right_dir;
5734 MovDir[x][y] = back_dir;
5736 xx = x + move_xy[MovDir[x][y]].dx;
5737 yy = y + move_xy[MovDir[x][y]].dy;
5739 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5740 MovDir[x][y] = old_move_dir;
5744 else if (element == EL_MOLE)
5746 boolean can_move_on =
5747 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5748 IS_AMOEBOID(Feld[move_x][move_y]) ||
5749 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5752 boolean can_turn_left =
5753 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5754 IS_AMOEBOID(Feld[left_x][left_y])));
5756 boolean can_turn_right =
5757 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5758 IS_AMOEBOID(Feld[right_x][right_y])));
5760 if (can_turn_left && can_turn_right)
5761 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5762 else if (can_turn_left)
5763 MovDir[x][y] = left_dir;
5765 MovDir[x][y] = right_dir;
5768 if (MovDir[x][y] != old_move_dir)
5771 else if (element == EL_BALLOON)
5773 MovDir[x][y] = game.wind_direction;
5776 else if (element == EL_SPRING)
5778 #if USE_NEW_SPRING_BUMPER
5779 if (MovDir[x][y] & MV_HORIZONTAL)
5781 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5782 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5784 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5785 ResetGfxAnimation(move_x, move_y);
5786 DrawLevelField(move_x, move_y);
5788 MovDir[x][y] = back_dir;
5790 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5791 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5792 MovDir[x][y] = MV_NONE;
5795 if (MovDir[x][y] & MV_HORIZONTAL &&
5796 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5797 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5798 MovDir[x][y] = MV_NONE;
5803 else if (element == EL_ROBOT ||
5804 element == EL_SATELLITE ||
5805 element == EL_PENGUIN ||
5806 element == EL_EMC_ANDROID)
5808 int attr_x = -1, attr_y = -1;
5819 for (i = 0; i < MAX_PLAYERS; i++)
5821 struct PlayerInfo *player = &stored_player[i];
5822 int jx = player->jx, jy = player->jy;
5824 if (!player->active)
5828 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5836 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5837 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5838 game.engine_version < VERSION_IDENT(3,1,0,0)))
5844 if (element == EL_PENGUIN)
5847 static int xy[4][2] =
5855 for (i = 0; i < NUM_DIRECTIONS; i++)
5857 int ex = x + xy[i][0];
5858 int ey = y + xy[i][1];
5860 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5861 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5862 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5863 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5872 MovDir[x][y] = MV_NONE;
5874 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5875 else if (attr_x > x)
5876 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5878 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5879 else if (attr_y > y)
5880 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5882 if (element == EL_ROBOT)
5886 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5887 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5888 Moving2Blocked(x, y, &newx, &newy);
5890 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5891 MovDelay[x][y] = 8 + 8 * !RND(3);
5893 MovDelay[x][y] = 16;
5895 else if (element == EL_PENGUIN)
5901 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5903 boolean first_horiz = RND(2);
5904 int new_move_dir = MovDir[x][y];
5907 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5908 Moving2Blocked(x, y, &newx, &newy);
5910 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5914 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5915 Moving2Blocked(x, y, &newx, &newy);
5917 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5920 MovDir[x][y] = old_move_dir;
5924 else if (element == EL_SATELLITE)
5930 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5932 boolean first_horiz = RND(2);
5933 int new_move_dir = MovDir[x][y];
5936 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5937 Moving2Blocked(x, y, &newx, &newy);
5939 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5943 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5944 Moving2Blocked(x, y, &newx, &newy);
5946 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5949 MovDir[x][y] = old_move_dir;
5953 else if (element == EL_EMC_ANDROID)
5955 static int check_pos[16] =
5957 -1, /* 0 => (invalid) */
5958 7, /* 1 => MV_LEFT */
5959 3, /* 2 => MV_RIGHT */
5960 -1, /* 3 => (invalid) */
5962 0, /* 5 => MV_LEFT | MV_UP */
5963 2, /* 6 => MV_RIGHT | MV_UP */
5964 -1, /* 7 => (invalid) */
5965 5, /* 8 => MV_DOWN */
5966 6, /* 9 => MV_LEFT | MV_DOWN */
5967 4, /* 10 => MV_RIGHT | MV_DOWN */
5968 -1, /* 11 => (invalid) */
5969 -1, /* 12 => (invalid) */
5970 -1, /* 13 => (invalid) */
5971 -1, /* 14 => (invalid) */
5972 -1, /* 15 => (invalid) */
5980 { -1, -1, MV_LEFT | MV_UP },
5982 { +1, -1, MV_RIGHT | MV_UP },
5983 { +1, 0, MV_RIGHT },
5984 { +1, +1, MV_RIGHT | MV_DOWN },
5986 { -1, +1, MV_LEFT | MV_DOWN },
5989 int start_pos, check_order;
5990 boolean can_clone = FALSE;
5993 /* check if there is any free field around current position */
5994 for (i = 0; i < 8; i++)
5996 int newx = x + check_xy[i].dx;
5997 int newy = y + check_xy[i].dy;
5999 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6007 if (can_clone) /* randomly find an element to clone */
6011 start_pos = check_pos[RND(8)];
6012 check_order = (RND(2) ? -1 : +1);
6014 for (i = 0; i < 8; i++)
6016 int pos_raw = start_pos + i * check_order;
6017 int pos = (pos_raw + 8) % 8;
6018 int newx = x + check_xy[pos].dx;
6019 int newy = y + check_xy[pos].dy;
6021 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6023 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6024 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6026 Store[x][y] = Feld[newx][newy];
6035 if (can_clone) /* randomly find a direction to move */
6039 start_pos = check_pos[RND(8)];
6040 check_order = (RND(2) ? -1 : +1);
6042 for (i = 0; i < 8; i++)
6044 int pos_raw = start_pos + i * check_order;
6045 int pos = (pos_raw + 8) % 8;
6046 int newx = x + check_xy[pos].dx;
6047 int newy = y + check_xy[pos].dy;
6048 int new_move_dir = check_xy[pos].dir;
6050 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6052 MovDir[x][y] = new_move_dir;
6053 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6062 if (can_clone) /* cloning and moving successful */
6065 /* cannot clone -- try to move towards player */
6067 start_pos = check_pos[MovDir[x][y] & 0x0f];
6068 check_order = (RND(2) ? -1 : +1);
6070 for (i = 0; i < 3; i++)
6072 /* first check start_pos, then previous/next or (next/previous) pos */
6073 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6074 int pos = (pos_raw + 8) % 8;
6075 int newx = x + check_xy[pos].dx;
6076 int newy = y + check_xy[pos].dy;
6077 int new_move_dir = check_xy[pos].dir;
6079 if (IS_PLAYER(newx, newy))
6082 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6084 MovDir[x][y] = new_move_dir;
6085 MovDelay[x][y] = level.android_move_time * 8 + 1;
6092 else if (move_pattern == MV_TURNING_LEFT ||
6093 move_pattern == MV_TURNING_RIGHT ||
6094 move_pattern == MV_TURNING_LEFT_RIGHT ||
6095 move_pattern == MV_TURNING_RIGHT_LEFT ||
6096 move_pattern == MV_TURNING_RANDOM ||
6097 move_pattern == MV_ALL_DIRECTIONS)
6099 boolean can_turn_left =
6100 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6101 boolean can_turn_right =
6102 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6104 if (element_info[element].move_stepsize == 0) /* "not moving" */
6107 if (move_pattern == MV_TURNING_LEFT)
6108 MovDir[x][y] = left_dir;
6109 else if (move_pattern == MV_TURNING_RIGHT)
6110 MovDir[x][y] = right_dir;
6111 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6112 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6113 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6114 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6115 else if (move_pattern == MV_TURNING_RANDOM)
6116 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6117 can_turn_right && !can_turn_left ? right_dir :
6118 RND(2) ? left_dir : right_dir);
6119 else if (can_turn_left && can_turn_right)
6120 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6121 else if (can_turn_left)
6122 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6123 else if (can_turn_right)
6124 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6126 MovDir[x][y] = back_dir;
6128 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6130 else if (move_pattern == MV_HORIZONTAL ||
6131 move_pattern == MV_VERTICAL)
6133 if (move_pattern & old_move_dir)
6134 MovDir[x][y] = back_dir;
6135 else if (move_pattern == MV_HORIZONTAL)
6136 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6137 else if (move_pattern == MV_VERTICAL)
6138 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6140 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6142 else if (move_pattern & MV_ANY_DIRECTION)
6144 MovDir[x][y] = move_pattern;
6145 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6147 else if (move_pattern & MV_WIND_DIRECTION)
6149 MovDir[x][y] = game.wind_direction;
6150 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6152 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6154 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6155 MovDir[x][y] = left_dir;
6156 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6157 MovDir[x][y] = right_dir;
6159 if (MovDir[x][y] != old_move_dir)
6160 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6162 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6164 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6165 MovDir[x][y] = right_dir;
6166 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6167 MovDir[x][y] = left_dir;
6169 if (MovDir[x][y] != old_move_dir)
6170 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6172 else if (move_pattern == MV_TOWARDS_PLAYER ||
6173 move_pattern == MV_AWAY_FROM_PLAYER)
6175 int attr_x = -1, attr_y = -1;
6177 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6188 for (i = 0; i < MAX_PLAYERS; i++)
6190 struct PlayerInfo *player = &stored_player[i];
6191 int jx = player->jx, jy = player->jy;
6193 if (!player->active)
6197 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6205 MovDir[x][y] = MV_NONE;
6207 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6208 else if (attr_x > x)
6209 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6211 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6212 else if (attr_y > y)
6213 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6215 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6217 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6219 boolean first_horiz = RND(2);
6220 int new_move_dir = MovDir[x][y];
6222 if (element_info[element].move_stepsize == 0) /* "not moving" */
6224 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6225 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6231 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6232 Moving2Blocked(x, y, &newx, &newy);
6234 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6238 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6239 Moving2Blocked(x, y, &newx, &newy);
6241 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6244 MovDir[x][y] = old_move_dir;
6247 else if (move_pattern == MV_WHEN_PUSHED ||
6248 move_pattern == MV_WHEN_DROPPED)
6250 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6251 MovDir[x][y] = MV_NONE;
6255 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6257 static int test_xy[7][2] =
6267 static int test_dir[7] =
6277 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6278 int move_preference = -1000000; /* start with very low preference */
6279 int new_move_dir = MV_NONE;
6280 int start_test = RND(4);
6283 for (i = 0; i < NUM_DIRECTIONS; i++)
6285 int move_dir = test_dir[start_test + i];
6286 int move_dir_preference;
6288 xx = x + test_xy[start_test + i][0];
6289 yy = y + test_xy[start_test + i][1];
6291 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6292 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6294 new_move_dir = move_dir;
6299 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6302 move_dir_preference = -1 * RunnerVisit[xx][yy];
6303 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6304 move_dir_preference = PlayerVisit[xx][yy];
6306 if (move_dir_preference > move_preference)
6308 /* prefer field that has not been visited for the longest time */
6309 move_preference = move_dir_preference;
6310 new_move_dir = move_dir;
6312 else if (move_dir_preference == move_preference &&
6313 move_dir == old_move_dir)
6315 /* prefer last direction when all directions are preferred equally */
6316 move_preference = move_dir_preference;
6317 new_move_dir = move_dir;
6321 MovDir[x][y] = new_move_dir;
6322 if (old_move_dir != new_move_dir)
6323 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6327 static void TurnRound(int x, int y)
6329 int direction = MovDir[x][y];
6333 GfxDir[x][y] = MovDir[x][y];
6335 if (direction != MovDir[x][y])
6339 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6341 ResetGfxFrame(x, y, FALSE);
6344 static boolean JustBeingPushed(int x, int y)
6348 for (i = 0; i < MAX_PLAYERS; i++)
6350 struct PlayerInfo *player = &stored_player[i];
6352 if (player->active && player->is_pushing && player->MovPos)
6354 int next_jx = player->jx + (player->jx - player->last_jx);
6355 int next_jy = player->jy + (player->jy - player->last_jy);
6357 if (x == next_jx && y == next_jy)
6365 void StartMoving(int x, int y)
6367 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6368 int element = Feld[x][y];
6373 if (MovDelay[x][y] == 0)
6374 GfxAction[x][y] = ACTION_DEFAULT;
6376 if (CAN_FALL(element) && y < lev_fieldy - 1)
6378 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6379 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6380 if (JustBeingPushed(x, y))
6383 if (element == EL_QUICKSAND_FULL)
6385 if (IS_FREE(x, y + 1))
6387 InitMovingField(x, y, MV_DOWN);
6388 started_moving = TRUE;
6390 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6391 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6392 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6393 Store[x][y] = EL_ROCK;
6395 Store[x][y] = EL_ROCK;
6398 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6400 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6402 if (!MovDelay[x][y])
6403 MovDelay[x][y] = TILEY + 1;
6412 Feld[x][y] = EL_QUICKSAND_EMPTY;
6413 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6414 Store[x][y + 1] = Store[x][y];
6417 PlayLevelSoundAction(x, y, ACTION_FILLING);
6420 else if (element == EL_QUICKSAND_FAST_FULL)
6422 if (IS_FREE(x, y + 1))
6424 InitMovingField(x, y, MV_DOWN);
6425 started_moving = TRUE;
6427 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6428 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6429 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6430 Store[x][y] = EL_ROCK;
6432 Store[x][y] = EL_ROCK;
6435 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6437 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6439 if (!MovDelay[x][y])
6440 MovDelay[x][y] = TILEY + 1;
6449 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6450 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6451 Store[x][y + 1] = Store[x][y];
6454 PlayLevelSoundAction(x, y, ACTION_FILLING);
6457 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6458 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6460 InitMovingField(x, y, MV_DOWN);
6461 started_moving = TRUE;
6463 Feld[x][y] = EL_QUICKSAND_FILLING;
6464 Store[x][y] = element;
6466 PlayLevelSoundAction(x, y, ACTION_FILLING);
6468 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6469 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6471 InitMovingField(x, y, MV_DOWN);
6472 started_moving = TRUE;
6474 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6475 Store[x][y] = element;
6477 PlayLevelSoundAction(x, y, ACTION_FILLING);
6479 else if (element == EL_MAGIC_WALL_FULL)
6481 if (IS_FREE(x, y + 1))
6483 InitMovingField(x, y, MV_DOWN);
6484 started_moving = TRUE;
6486 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6487 Store[x][y] = EL_CHANGED(Store[x][y]);
6489 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6491 if (!MovDelay[x][y])
6492 MovDelay[x][y] = TILEY/4 + 1;
6501 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6502 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6503 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6507 else if (element == EL_BD_MAGIC_WALL_FULL)
6509 if (IS_FREE(x, y + 1))
6511 InitMovingField(x, y, MV_DOWN);
6512 started_moving = TRUE;
6514 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6515 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6517 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6519 if (!MovDelay[x][y])
6520 MovDelay[x][y] = TILEY/4 + 1;
6529 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6530 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6531 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6535 else if (element == EL_DC_MAGIC_WALL_FULL)
6537 if (IS_FREE(x, y + 1))
6539 InitMovingField(x, y, MV_DOWN);
6540 started_moving = TRUE;
6542 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6543 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6545 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6547 if (!MovDelay[x][y])
6548 MovDelay[x][y] = TILEY/4 + 1;
6557 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6558 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6559 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6563 else if ((CAN_PASS_MAGIC_WALL(element) &&
6564 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6565 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6566 (CAN_PASS_DC_MAGIC_WALL(element) &&
6567 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6570 InitMovingField(x, y, MV_DOWN);
6571 started_moving = TRUE;
6574 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6575 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6576 EL_DC_MAGIC_WALL_FILLING);
6577 Store[x][y] = element;
6579 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6581 SplashAcid(x, y + 1);
6583 InitMovingField(x, y, MV_DOWN);
6584 started_moving = TRUE;
6586 Store[x][y] = EL_ACID;
6589 #if USE_FIX_IMPACT_COLLISION
6590 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6591 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6593 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6594 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6596 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6597 CAN_FALL(element) && WasJustFalling[x][y] &&
6598 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6600 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6601 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6602 (Feld[x][y + 1] == EL_BLOCKED)))
6604 /* this is needed for a special case not covered by calling "Impact()"
6605 from "ContinueMoving()": if an element moves to a tile directly below
6606 another element which was just falling on that tile (which was empty
6607 in the previous frame), the falling element above would just stop
6608 instead of smashing the element below (in previous version, the above
6609 element was just checked for "moving" instead of "falling", resulting
6610 in incorrect smashes caused by horizontal movement of the above
6611 element; also, the case of the player being the element to smash was
6612 simply not covered here... :-/ ) */
6614 CheckCollision[x][y] = 0;
6615 CheckImpact[x][y] = 0;
6619 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6621 if (MovDir[x][y] == MV_NONE)
6623 InitMovingField(x, y, MV_DOWN);
6624 started_moving = TRUE;
6627 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6629 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6630 MovDir[x][y] = MV_DOWN;
6632 InitMovingField(x, y, MV_DOWN);
6633 started_moving = TRUE;
6635 else if (element == EL_AMOEBA_DROP)
6637 Feld[x][y] = EL_AMOEBA_GROWING;
6638 Store[x][y] = EL_AMOEBA_WET;
6640 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6641 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6642 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6643 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6645 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6646 (IS_FREE(x - 1, y + 1) ||
6647 Feld[x - 1][y + 1] == EL_ACID));
6648 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6649 (IS_FREE(x + 1, y + 1) ||
6650 Feld[x + 1][y + 1] == EL_ACID));
6651 boolean can_fall_any = (can_fall_left || can_fall_right);
6652 boolean can_fall_both = (can_fall_left && can_fall_right);
6653 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6655 #if USE_NEW_ALL_SLIPPERY
6656 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6658 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6659 can_fall_right = FALSE;
6660 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6661 can_fall_left = FALSE;
6662 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6663 can_fall_right = FALSE;
6664 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6665 can_fall_left = FALSE;
6667 can_fall_any = (can_fall_left || can_fall_right);
6668 can_fall_both = FALSE;
6671 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6673 if (slippery_type == SLIPPERY_ONLY_LEFT)
6674 can_fall_right = FALSE;
6675 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6676 can_fall_left = FALSE;
6677 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6678 can_fall_right = FALSE;
6679 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6680 can_fall_left = FALSE;
6682 can_fall_any = (can_fall_left || can_fall_right);
6683 can_fall_both = (can_fall_left && can_fall_right);
6687 #if USE_NEW_ALL_SLIPPERY
6689 #if USE_NEW_SP_SLIPPERY
6690 /* !!! better use the same properties as for custom elements here !!! */
6691 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6692 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6694 can_fall_right = FALSE; /* slip down on left side */
6695 can_fall_both = FALSE;
6700 #if USE_NEW_ALL_SLIPPERY
6703 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6704 can_fall_right = FALSE; /* slip down on left side */
6706 can_fall_left = !(can_fall_right = RND(2));
6708 can_fall_both = FALSE;
6713 if (game.emulation == EMU_BOULDERDASH ||
6714 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6715 can_fall_right = FALSE; /* slip down on left side */
6717 can_fall_left = !(can_fall_right = RND(2));
6719 can_fall_both = FALSE;
6725 /* if not determined otherwise, prefer left side for slipping down */
6726 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6727 started_moving = TRUE;
6731 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6733 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6736 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6737 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6738 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6739 int belt_dir = game.belt_dir[belt_nr];
6741 if ((belt_dir == MV_LEFT && left_is_free) ||
6742 (belt_dir == MV_RIGHT && right_is_free))
6744 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6746 InitMovingField(x, y, belt_dir);
6747 started_moving = TRUE;
6749 Pushed[x][y] = TRUE;
6750 Pushed[nextx][y] = TRUE;
6752 GfxAction[x][y] = ACTION_DEFAULT;
6756 MovDir[x][y] = 0; /* if element was moving, stop it */
6761 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6763 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6765 if (CAN_MOVE(element) && !started_moving)
6768 int move_pattern = element_info[element].move_pattern;
6773 if (MovDir[x][y] == MV_NONE)
6775 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6776 x, y, element, element_info[element].token_name);
6777 printf("StartMoving(): This should never happen!\n");
6782 Moving2Blocked(x, y, &newx, &newy);
6784 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6787 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6788 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6790 WasJustMoving[x][y] = 0;
6791 CheckCollision[x][y] = 0;
6793 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6795 if (Feld[x][y] != element) /* element has changed */
6799 if (!MovDelay[x][y]) /* start new movement phase */
6801 /* all objects that can change their move direction after each step
6802 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6804 if (element != EL_YAMYAM &&
6805 element != EL_DARK_YAMYAM &&
6806 element != EL_PACMAN &&
6807 !(move_pattern & MV_ANY_DIRECTION) &&
6808 move_pattern != MV_TURNING_LEFT &&
6809 move_pattern != MV_TURNING_RIGHT &&
6810 move_pattern != MV_TURNING_LEFT_RIGHT &&
6811 move_pattern != MV_TURNING_RIGHT_LEFT &&
6812 move_pattern != MV_TURNING_RANDOM)
6816 if (MovDelay[x][y] && (element == EL_BUG ||
6817 element == EL_SPACESHIP ||
6818 element == EL_SP_SNIKSNAK ||
6819 element == EL_SP_ELECTRON ||
6820 element == EL_MOLE))
6821 DrawLevelField(x, y);
6825 if (MovDelay[x][y]) /* wait some time before next movement */
6829 if (element == EL_ROBOT ||
6830 element == EL_YAMYAM ||
6831 element == EL_DARK_YAMYAM)
6833 DrawLevelElementAnimationIfNeeded(x, y, element);
6834 PlayLevelSoundAction(x, y, ACTION_WAITING);
6836 else if (element == EL_SP_ELECTRON)
6837 DrawLevelElementAnimationIfNeeded(x, y, element);
6838 else if (element == EL_DRAGON)
6841 int dir = MovDir[x][y];
6842 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6843 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6844 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6845 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6846 dir == MV_UP ? IMG_FLAMES_1_UP :
6847 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6848 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6850 GfxAction[x][y] = ACTION_ATTACKING;
6852 if (IS_PLAYER(x, y))
6853 DrawPlayerField(x, y);
6855 DrawLevelField(x, y);
6857 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6859 for (i = 1; i <= 3; i++)
6861 int xx = x + i * dx;
6862 int yy = y + i * dy;
6863 int sx = SCREENX(xx);
6864 int sy = SCREENY(yy);
6865 int flame_graphic = graphic + (i - 1);
6867 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6872 int flamed = MovingOrBlocked2Element(xx, yy);
6876 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6878 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6879 RemoveMovingField(xx, yy);
6881 RemoveField(xx, yy);
6883 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6886 RemoveMovingField(xx, yy);
6889 ChangeDelay[xx][yy] = 0;
6891 Feld[xx][yy] = EL_FLAMES;
6893 if (IN_SCR_FIELD(sx, sy))
6895 DrawLevelFieldCrumbledSand(xx, yy);
6896 DrawGraphic(sx, sy, flame_graphic, frame);
6901 if (Feld[xx][yy] == EL_FLAMES)
6902 Feld[xx][yy] = EL_EMPTY;
6903 DrawLevelField(xx, yy);
6908 if (MovDelay[x][y]) /* element still has to wait some time */
6910 PlayLevelSoundAction(x, y, ACTION_WAITING);
6916 /* now make next step */
6918 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6920 if (DONT_COLLIDE_WITH(element) &&
6921 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6922 !PLAYER_ENEMY_PROTECTED(newx, newy))
6924 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6929 else if (CAN_MOVE_INTO_ACID(element) &&
6930 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6931 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6932 (MovDir[x][y] == MV_DOWN ||
6933 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6935 SplashAcid(newx, newy);
6936 Store[x][y] = EL_ACID;
6938 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6940 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6941 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6942 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6943 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6946 DrawLevelField(x, y);
6948 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6949 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6950 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6952 local_player->friends_still_needed--;
6953 if (!local_player->friends_still_needed &&
6954 !local_player->GameOver && AllPlayersGone)
6955 PlayerWins(local_player);
6959 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6961 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6962 DrawLevelField(newx, newy);
6964 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6966 else if (!IS_FREE(newx, newy))
6968 GfxAction[x][y] = ACTION_WAITING;
6970 if (IS_PLAYER(x, y))
6971 DrawPlayerField(x, y);
6973 DrawLevelField(x, y);
6978 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6980 if (IS_FOOD_PIG(Feld[newx][newy]))
6982 if (IS_MOVING(newx, newy))
6983 RemoveMovingField(newx, newy);
6986 Feld[newx][newy] = EL_EMPTY;
6987 DrawLevelField(newx, newy);
6990 PlayLevelSound(x, y, SND_PIG_DIGGING);
6992 else if (!IS_FREE(newx, newy))
6994 if (IS_PLAYER(x, y))
6995 DrawPlayerField(x, y);
6997 DrawLevelField(x, y);
7002 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7004 if (Store[x][y] != EL_EMPTY)
7006 boolean can_clone = FALSE;
7009 /* check if element to clone is still there */
7010 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7012 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7020 /* cannot clone or target field not free anymore -- do not clone */
7021 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7022 Store[x][y] = EL_EMPTY;
7025 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7027 if (IS_MV_DIAGONAL(MovDir[x][y]))
7029 int diagonal_move_dir = MovDir[x][y];
7030 int stored = Store[x][y];
7031 int change_delay = 8;
7034 /* android is moving diagonally */
7036 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7038 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7039 GfxElement[x][y] = EL_EMC_ANDROID;
7040 GfxAction[x][y] = ACTION_SHRINKING;
7041 GfxDir[x][y] = diagonal_move_dir;
7042 ChangeDelay[x][y] = change_delay;
7044 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7047 DrawLevelGraphicAnimation(x, y, graphic);
7048 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7050 if (Feld[newx][newy] == EL_ACID)
7052 SplashAcid(newx, newy);
7057 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7059 Store[newx][newy] = EL_EMC_ANDROID;
7060 GfxElement[newx][newy] = EL_EMC_ANDROID;
7061 GfxAction[newx][newy] = ACTION_GROWING;
7062 GfxDir[newx][newy] = diagonal_move_dir;
7063 ChangeDelay[newx][newy] = change_delay;
7065 graphic = el_act_dir2img(GfxElement[newx][newy],
7066 GfxAction[newx][newy], GfxDir[newx][newy]);
7068 DrawLevelGraphicAnimation(newx, newy, graphic);
7069 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7075 Feld[newx][newy] = EL_EMPTY;
7076 DrawLevelField(newx, newy);
7078 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7081 else if (!IS_FREE(newx, newy))
7084 if (IS_PLAYER(x, y))
7085 DrawPlayerField(x, y);
7087 DrawLevelField(x, y);
7093 else if (IS_CUSTOM_ELEMENT(element) &&
7094 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7096 int new_element = Feld[newx][newy];
7098 if (!IS_FREE(newx, newy))
7100 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7101 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7104 /* no element can dig solid indestructible elements */
7105 if (IS_INDESTRUCTIBLE(new_element) &&
7106 !IS_DIGGABLE(new_element) &&
7107 !IS_COLLECTIBLE(new_element))
7110 if (AmoebaNr[newx][newy] &&
7111 (new_element == EL_AMOEBA_FULL ||
7112 new_element == EL_BD_AMOEBA ||
7113 new_element == EL_AMOEBA_GROWING))
7115 AmoebaCnt[AmoebaNr[newx][newy]]--;
7116 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7119 if (IS_MOVING(newx, newy))
7120 RemoveMovingField(newx, newy);
7123 RemoveField(newx, newy);
7124 DrawLevelField(newx, newy);
7127 /* if digged element was about to explode, prevent the explosion */
7128 ExplodeField[newx][newy] = EX_TYPE_NONE;
7130 PlayLevelSoundAction(x, y, action);
7133 Store[newx][newy] = EL_EMPTY;
7135 /* this makes it possible to leave the removed element again */
7136 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7137 Store[newx][newy] = new_element;
7139 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7141 int move_leave_element = element_info[element].move_leave_element;
7143 /* this makes it possible to leave the removed element again */
7144 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7145 new_element : move_leave_element);
7149 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7151 RunnerVisit[x][y] = FrameCounter;
7152 PlayerVisit[x][y] /= 8; /* expire player visit path */
7155 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7157 if (!IS_FREE(newx, newy))
7159 if (IS_PLAYER(x, y))
7160 DrawPlayerField(x, y);
7162 DrawLevelField(x, y);
7168 boolean wanna_flame = !RND(10);
7169 int dx = newx - x, dy = newy - y;
7170 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7171 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7172 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7173 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7174 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7175 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7178 IS_CLASSIC_ENEMY(element1) ||
7179 IS_CLASSIC_ENEMY(element2)) &&
7180 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7181 element1 != EL_FLAMES && element2 != EL_FLAMES)
7183 ResetGfxAnimation(x, y);
7184 GfxAction[x][y] = ACTION_ATTACKING;
7186 if (IS_PLAYER(x, y))
7187 DrawPlayerField(x, y);
7189 DrawLevelField(x, y);
7191 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7193 MovDelay[x][y] = 50;
7197 RemoveField(newx, newy);
7199 Feld[newx][newy] = EL_FLAMES;
7200 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7203 RemoveField(newx1, newy1);
7205 Feld[newx1][newy1] = EL_FLAMES;
7207 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7210 RemoveField(newx2, newy2);
7212 Feld[newx2][newy2] = EL_FLAMES;
7219 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7220 Feld[newx][newy] == EL_DIAMOND)
7222 if (IS_MOVING(newx, newy))
7223 RemoveMovingField(newx, newy);
7226 Feld[newx][newy] = EL_EMPTY;
7227 DrawLevelField(newx, newy);
7230 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7232 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7233 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7235 if (AmoebaNr[newx][newy])
7237 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7238 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7239 Feld[newx][newy] == EL_BD_AMOEBA)
7240 AmoebaCnt[AmoebaNr[newx][newy]]--;
7245 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7247 RemoveMovingField(newx, newy);
7250 if (IS_MOVING(newx, newy))
7252 RemoveMovingField(newx, newy);
7257 Feld[newx][newy] = EL_EMPTY;
7258 DrawLevelField(newx, newy);
7261 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7263 else if ((element == EL_PACMAN || element == EL_MOLE)
7264 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7266 if (AmoebaNr[newx][newy])
7268 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7269 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7270 Feld[newx][newy] == EL_BD_AMOEBA)
7271 AmoebaCnt[AmoebaNr[newx][newy]]--;
7274 if (element == EL_MOLE)
7276 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7277 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7279 ResetGfxAnimation(x, y);
7280 GfxAction[x][y] = ACTION_DIGGING;
7281 DrawLevelField(x, y);
7283 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7285 return; /* wait for shrinking amoeba */
7287 else /* element == EL_PACMAN */
7289 Feld[newx][newy] = EL_EMPTY;
7290 DrawLevelField(newx, newy);
7291 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7294 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7295 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7296 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7298 /* wait for shrinking amoeba to completely disappear */
7301 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7303 /* object was running against a wall */
7308 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7309 if (move_pattern & MV_ANY_DIRECTION &&
7310 move_pattern == MovDir[x][y])
7312 int blocking_element =
7313 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7315 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7318 element = Feld[x][y]; /* element might have changed */
7322 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7323 DrawLevelElementAnimation(x, y, element);
7325 if (DONT_TOUCH(element))
7326 TestIfBadThingTouchesPlayer(x, y);
7331 InitMovingField(x, y, MovDir[x][y]);
7333 PlayLevelSoundAction(x, y, ACTION_MOVING);
7337 ContinueMoving(x, y);
7340 void ContinueMoving(int x, int y)
7342 int element = Feld[x][y];
7343 struct ElementInfo *ei = &element_info[element];
7344 int direction = MovDir[x][y];
7345 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7346 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7347 int newx = x + dx, newy = y + dy;
7348 int stored = Store[x][y];
7349 int stored_new = Store[newx][newy];
7350 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7351 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7352 boolean last_line = (newy == lev_fieldy - 1);
7354 MovPos[x][y] += getElementMoveStepsize(x, y);
7356 if (pushed_by_player) /* special case: moving object pushed by player */
7357 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7359 if (ABS(MovPos[x][y]) < TILEX)
7362 int ee = Feld[x][y];
7363 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7364 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7366 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7367 x, y, ABS(MovPos[x][y]),
7369 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7372 DrawLevelField(x, y);
7374 return; /* element is still moving */
7377 /* element reached destination field */
7379 Feld[x][y] = EL_EMPTY;
7380 Feld[newx][newy] = element;
7381 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7383 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7385 element = Feld[newx][newy] = EL_ACID;
7387 else if (element == EL_MOLE)
7389 Feld[x][y] = EL_SAND;
7391 DrawLevelFieldCrumbledSandNeighbours(x, y);
7393 else if (element == EL_QUICKSAND_FILLING)
7395 element = Feld[newx][newy] = get_next_element(element);
7396 Store[newx][newy] = Store[x][y];
7398 else if (element == EL_QUICKSAND_EMPTYING)
7400 Feld[x][y] = get_next_element(element);
7401 element = Feld[newx][newy] = Store[x][y];
7403 else if (element == EL_QUICKSAND_FAST_FILLING)
7405 element = Feld[newx][newy] = get_next_element(element);
7406 Store[newx][newy] = Store[x][y];
7408 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7410 Feld[x][y] = get_next_element(element);
7411 element = Feld[newx][newy] = Store[x][y];
7413 else if (element == EL_MAGIC_WALL_FILLING)
7415 element = Feld[newx][newy] = get_next_element(element);
7416 if (!game.magic_wall_active)
7417 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7418 Store[newx][newy] = Store[x][y];
7420 else if (element == EL_MAGIC_WALL_EMPTYING)
7422 Feld[x][y] = get_next_element(element);
7423 if (!game.magic_wall_active)
7424 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7425 element = Feld[newx][newy] = Store[x][y];
7427 #if USE_NEW_CUSTOM_VALUE
7428 InitField(newx, newy, FALSE);
7431 else if (element == EL_BD_MAGIC_WALL_FILLING)
7433 element = Feld[newx][newy] = get_next_element(element);
7434 if (!game.magic_wall_active)
7435 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7436 Store[newx][newy] = Store[x][y];
7438 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7440 Feld[x][y] = get_next_element(element);
7441 if (!game.magic_wall_active)
7442 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7443 element = Feld[newx][newy] = Store[x][y];
7445 #if USE_NEW_CUSTOM_VALUE
7446 InitField(newx, newy, FALSE);
7449 else if (element == EL_DC_MAGIC_WALL_FILLING)
7451 element = Feld[newx][newy] = get_next_element(element);
7452 if (!game.magic_wall_active)
7453 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7454 Store[newx][newy] = Store[x][y];
7456 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7458 Feld[x][y] = get_next_element(element);
7459 if (!game.magic_wall_active)
7460 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7461 element = Feld[newx][newy] = Store[x][y];
7463 #if USE_NEW_CUSTOM_VALUE
7464 InitField(newx, newy, FALSE);
7467 else if (element == EL_AMOEBA_DROPPING)
7469 Feld[x][y] = get_next_element(element);
7470 element = Feld[newx][newy] = Store[x][y];
7472 else if (element == EL_SOKOBAN_OBJECT)
7475 Feld[x][y] = Back[x][y];
7477 if (Back[newx][newy])
7478 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7480 Back[x][y] = Back[newx][newy] = 0;
7483 Store[x][y] = EL_EMPTY;
7488 MovDelay[newx][newy] = 0;
7490 if (CAN_CHANGE_OR_HAS_ACTION(element))
7492 /* copy element change control values to new field */
7493 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7494 ChangePage[newx][newy] = ChangePage[x][y];
7495 ChangeCount[newx][newy] = ChangeCount[x][y];
7496 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7499 #if USE_NEW_CUSTOM_VALUE
7500 CustomValue[newx][newy] = CustomValue[x][y];
7503 ChangeDelay[x][y] = 0;
7504 ChangePage[x][y] = -1;
7505 ChangeCount[x][y] = 0;
7506 ChangeEvent[x][y] = -1;
7508 #if USE_NEW_CUSTOM_VALUE
7509 CustomValue[x][y] = 0;
7512 /* copy animation control values to new field */
7513 GfxFrame[newx][newy] = GfxFrame[x][y];
7514 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7515 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7516 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7518 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7520 /* some elements can leave other elements behind after moving */
7522 if (ei->move_leave_element != EL_EMPTY &&
7523 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7524 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7526 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7527 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7528 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7531 int move_leave_element = ei->move_leave_element;
7535 /* this makes it possible to leave the removed element again */
7536 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7537 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7539 /* this makes it possible to leave the removed element again */
7540 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7541 move_leave_element = stored;
7544 /* this makes it possible to leave the removed element again */
7545 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7546 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7547 move_leave_element = stored;
7550 Feld[x][y] = move_leave_element;
7552 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7553 MovDir[x][y] = direction;
7555 InitField(x, y, FALSE);
7557 if (GFX_CRUMBLED(Feld[x][y]))
7558 DrawLevelFieldCrumbledSandNeighbours(x, y);
7560 if (ELEM_IS_PLAYER(move_leave_element))
7561 RelocatePlayer(x, y, move_leave_element);
7564 /* do this after checking for left-behind element */
7565 ResetGfxAnimation(x, y); /* reset animation values for old field */
7567 if (!CAN_MOVE(element) ||
7568 (CAN_FALL(element) && direction == MV_DOWN &&
7569 (element == EL_SPRING ||
7570 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7571 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7572 GfxDir[x][y] = MovDir[newx][newy] = 0;
7574 DrawLevelField(x, y);
7575 DrawLevelField(newx, newy);
7577 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7579 /* prevent pushed element from moving on in pushed direction */
7580 if (pushed_by_player && CAN_MOVE(element) &&
7581 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7582 !(element_info[element].move_pattern & direction))
7583 TurnRound(newx, newy);
7585 /* prevent elements on conveyor belt from moving on in last direction */
7586 if (pushed_by_conveyor && CAN_FALL(element) &&
7587 direction & MV_HORIZONTAL)
7588 MovDir[newx][newy] = 0;
7590 if (!pushed_by_player)
7592 int nextx = newx + dx, nexty = newy + dy;
7593 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7595 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7597 if (CAN_FALL(element) && direction == MV_DOWN)
7598 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7600 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7601 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7603 #if USE_FIX_IMPACT_COLLISION
7604 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7605 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7609 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7611 TestIfBadThingTouchesPlayer(newx, newy);
7612 TestIfBadThingTouchesFriend(newx, newy);
7614 if (!IS_CUSTOM_ELEMENT(element))
7615 TestIfBadThingTouchesOtherBadThing(newx, newy);
7617 else if (element == EL_PENGUIN)
7618 TestIfFriendTouchesBadThing(newx, newy);
7620 /* give the player one last chance (one more frame) to move away */
7621 if (CAN_FALL(element) && direction == MV_DOWN &&
7622 (last_line || (!IS_FREE(x, newy + 1) &&
7623 (!IS_PLAYER(x, newy + 1) ||
7624 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7627 if (pushed_by_player && !game.use_change_when_pushing_bug)
7629 int push_side = MV_DIR_OPPOSITE(direction);
7630 struct PlayerInfo *player = PLAYERINFO(x, y);
7632 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7633 player->index_bit, push_side);
7634 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7635 player->index_bit, push_side);
7638 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7639 MovDelay[newx][newy] = 1;
7641 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7643 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7646 if (ChangePage[newx][newy] != -1) /* delayed change */
7648 int page = ChangePage[newx][newy];
7649 struct ElementChangeInfo *change = &ei->change_page[page];
7651 ChangePage[newx][newy] = -1;
7653 if (change->can_change)
7655 if (ChangeElement(newx, newy, element, page))
7657 if (change->post_change_function)
7658 change->post_change_function(newx, newy);
7662 if (change->has_action)
7663 ExecuteCustomElementAction(newx, newy, element, page);
7667 TestIfElementHitsCustomElement(newx, newy, direction);
7668 TestIfPlayerTouchesCustomElement(newx, newy);
7669 TestIfElementTouchesCustomElement(newx, newy);
7671 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7672 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7673 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7674 MV_DIR_OPPOSITE(direction));
7677 int AmoebeNachbarNr(int ax, int ay)
7680 int element = Feld[ax][ay];
7682 static int xy[4][2] =
7690 for (i = 0; i < NUM_DIRECTIONS; i++)
7692 int x = ax + xy[i][0];
7693 int y = ay + xy[i][1];
7695 if (!IN_LEV_FIELD(x, y))
7698 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7699 group_nr = AmoebaNr[x][y];
7705 void AmoebenVereinigen(int ax, int ay)
7707 int i, x, y, xx, yy;
7708 int new_group_nr = AmoebaNr[ax][ay];
7709 static int xy[4][2] =
7717 if (new_group_nr == 0)
7720 for (i = 0; i < NUM_DIRECTIONS; i++)
7725 if (!IN_LEV_FIELD(x, y))
7728 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7729 Feld[x][y] == EL_BD_AMOEBA ||
7730 Feld[x][y] == EL_AMOEBA_DEAD) &&
7731 AmoebaNr[x][y] != new_group_nr)
7733 int old_group_nr = AmoebaNr[x][y];
7735 if (old_group_nr == 0)
7738 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7739 AmoebaCnt[old_group_nr] = 0;
7740 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7741 AmoebaCnt2[old_group_nr] = 0;
7743 SCAN_PLAYFIELD(xx, yy)
7745 if (AmoebaNr[xx][yy] == old_group_nr)
7746 AmoebaNr[xx][yy] = new_group_nr;
7752 void AmoebeUmwandeln(int ax, int ay)
7756 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7758 int group_nr = AmoebaNr[ax][ay];
7763 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7764 printf("AmoebeUmwandeln(): This should never happen!\n");
7769 SCAN_PLAYFIELD(x, y)
7771 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7774 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7778 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7779 SND_AMOEBA_TURNING_TO_GEM :
7780 SND_AMOEBA_TURNING_TO_ROCK));
7785 static int xy[4][2] =
7793 for (i = 0; i < NUM_DIRECTIONS; i++)
7798 if (!IN_LEV_FIELD(x, y))
7801 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7803 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7804 SND_AMOEBA_TURNING_TO_GEM :
7805 SND_AMOEBA_TURNING_TO_ROCK));
7812 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7815 int group_nr = AmoebaNr[ax][ay];
7816 boolean done = FALSE;
7821 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7822 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7827 SCAN_PLAYFIELD(x, y)
7829 if (AmoebaNr[x][y] == group_nr &&
7830 (Feld[x][y] == EL_AMOEBA_DEAD ||
7831 Feld[x][y] == EL_BD_AMOEBA ||
7832 Feld[x][y] == EL_AMOEBA_GROWING))
7835 Feld[x][y] = new_element;
7836 InitField(x, y, FALSE);
7837 DrawLevelField(x, y);
7843 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7844 SND_BD_AMOEBA_TURNING_TO_ROCK :
7845 SND_BD_AMOEBA_TURNING_TO_GEM));
7848 void AmoebeWaechst(int x, int y)
7850 static unsigned long sound_delay = 0;
7851 static unsigned long sound_delay_value = 0;
7853 if (!MovDelay[x][y]) /* start new growing cycle */
7857 if (DelayReached(&sound_delay, sound_delay_value))
7859 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7860 sound_delay_value = 30;
7864 if (MovDelay[x][y]) /* wait some time before growing bigger */
7867 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7869 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7870 6 - MovDelay[x][y]);
7872 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7875 if (!MovDelay[x][y])
7877 Feld[x][y] = Store[x][y];
7879 DrawLevelField(x, y);
7884 void AmoebaDisappearing(int x, int y)
7886 static unsigned long sound_delay = 0;
7887 static unsigned long sound_delay_value = 0;
7889 if (!MovDelay[x][y]) /* start new shrinking cycle */
7893 if (DelayReached(&sound_delay, sound_delay_value))
7894 sound_delay_value = 30;
7897 if (MovDelay[x][y]) /* wait some time before shrinking */
7900 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7902 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7903 6 - MovDelay[x][y]);
7905 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7908 if (!MovDelay[x][y])
7910 Feld[x][y] = EL_EMPTY;
7911 DrawLevelField(x, y);
7913 /* don't let mole enter this field in this cycle;
7914 (give priority to objects falling to this field from above) */
7920 void AmoebeAbleger(int ax, int ay)
7923 int element = Feld[ax][ay];
7924 int graphic = el2img(element);
7925 int newax = ax, neway = ay;
7926 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7927 static int xy[4][2] =
7935 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7937 Feld[ax][ay] = EL_AMOEBA_DEAD;
7938 DrawLevelField(ax, ay);
7942 if (IS_ANIMATED(graphic))
7943 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7945 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7946 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7948 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7951 if (MovDelay[ax][ay])
7955 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7958 int x = ax + xy[start][0];
7959 int y = ay + xy[start][1];
7961 if (!IN_LEV_FIELD(x, y))
7964 if (IS_FREE(x, y) ||
7965 CAN_GROW_INTO(Feld[x][y]) ||
7966 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7967 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7973 if (newax == ax && neway == ay)
7976 else /* normal or "filled" (BD style) amoeba */
7979 boolean waiting_for_player = FALSE;
7981 for (i = 0; i < NUM_DIRECTIONS; i++)
7983 int j = (start + i) % 4;
7984 int x = ax + xy[j][0];
7985 int y = ay + xy[j][1];
7987 if (!IN_LEV_FIELD(x, y))
7990 if (IS_FREE(x, y) ||
7991 CAN_GROW_INTO(Feld[x][y]) ||
7992 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7993 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7999 else if (IS_PLAYER(x, y))
8000 waiting_for_player = TRUE;
8003 if (newax == ax && neway == ay) /* amoeba cannot grow */
8005 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8007 Feld[ax][ay] = EL_AMOEBA_DEAD;
8008 DrawLevelField(ax, ay);
8009 AmoebaCnt[AmoebaNr[ax][ay]]--;
8011 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8013 if (element == EL_AMOEBA_FULL)
8014 AmoebeUmwandeln(ax, ay);
8015 else if (element == EL_BD_AMOEBA)
8016 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8021 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8023 /* amoeba gets larger by growing in some direction */
8025 int new_group_nr = AmoebaNr[ax][ay];
8028 if (new_group_nr == 0)
8030 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8031 printf("AmoebeAbleger(): This should never happen!\n");
8036 AmoebaNr[newax][neway] = new_group_nr;
8037 AmoebaCnt[new_group_nr]++;
8038 AmoebaCnt2[new_group_nr]++;
8040 /* if amoeba touches other amoeba(s) after growing, unify them */
8041 AmoebenVereinigen(newax, neway);
8043 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8045 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8051 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8052 (neway == lev_fieldy - 1 && newax != ax))
8054 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8055 Store[newax][neway] = element;
8057 else if (neway == ay || element == EL_EMC_DRIPPER)
8059 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8061 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8065 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8066 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8067 Store[ax][ay] = EL_AMOEBA_DROP;
8068 ContinueMoving(ax, ay);
8072 DrawLevelField(newax, neway);
8075 void Life(int ax, int ay)
8079 int element = Feld[ax][ay];
8080 int graphic = el2img(element);
8081 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8083 boolean changed = FALSE;
8085 if (IS_ANIMATED(graphic))
8086 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8091 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8092 MovDelay[ax][ay] = life_time;
8094 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8097 if (MovDelay[ax][ay])
8101 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8103 int xx = ax+x1, yy = ay+y1;
8106 if (!IN_LEV_FIELD(xx, yy))
8109 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8111 int x = xx+x2, y = yy+y2;
8113 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8116 if (((Feld[x][y] == element ||
8117 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8119 (IS_FREE(x, y) && Stop[x][y]))
8123 if (xx == ax && yy == ay) /* field in the middle */
8125 if (nachbarn < life_parameter[0] ||
8126 nachbarn > life_parameter[1])
8128 Feld[xx][yy] = EL_EMPTY;
8130 DrawLevelField(xx, yy);
8131 Stop[xx][yy] = TRUE;
8135 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8136 { /* free border field */
8137 if (nachbarn >= life_parameter[2] &&
8138 nachbarn <= life_parameter[3])
8140 Feld[xx][yy] = element;
8141 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8143 DrawLevelField(xx, yy);
8144 Stop[xx][yy] = TRUE;
8151 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8152 SND_GAME_OF_LIFE_GROWING);
8155 static void InitRobotWheel(int x, int y)
8157 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8160 static void RunRobotWheel(int x, int y)
8162 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8165 static void StopRobotWheel(int x, int y)
8167 if (ZX == x && ZY == y)
8171 static void InitTimegateWheel(int x, int y)
8173 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8176 static void RunTimegateWheel(int x, int y)
8178 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8181 static void InitMagicBallDelay(int x, int y)
8184 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8186 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8190 static void ActivateMagicBall(int bx, int by)
8194 if (level.ball_random)
8196 int pos_border = RND(8); /* select one of the eight border elements */
8197 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8198 int xx = pos_content % 3;
8199 int yy = pos_content / 3;
8204 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8205 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8209 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8211 int xx = x - bx + 1;
8212 int yy = y - by + 1;
8214 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8215 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8219 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8222 void CheckExit(int x, int y)
8224 if (local_player->gems_still_needed > 0 ||
8225 local_player->sokobanfields_still_needed > 0 ||
8226 local_player->lights_still_needed > 0)
8228 int element = Feld[x][y];
8229 int graphic = el2img(element);
8231 if (IS_ANIMATED(graphic))
8232 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8237 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8240 Feld[x][y] = EL_EXIT_OPENING;
8242 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8245 void CheckExitEM(int x, int y)
8247 if (local_player->gems_still_needed > 0 ||
8248 local_player->sokobanfields_still_needed > 0 ||
8249 local_player->lights_still_needed > 0)
8251 int element = Feld[x][y];
8252 int graphic = el2img(element);
8254 if (IS_ANIMATED(graphic))
8255 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8260 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8263 Feld[x][y] = EL_EM_EXIT_OPENING;
8265 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8268 void CheckExitSteel(int x, int y)
8270 if (local_player->gems_still_needed > 0 ||
8271 local_player->sokobanfields_still_needed > 0 ||
8272 local_player->lights_still_needed > 0)
8274 int element = Feld[x][y];
8275 int graphic = el2img(element);
8277 if (IS_ANIMATED(graphic))
8278 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8283 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8286 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8288 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8291 void CheckExitSteelEM(int x, int y)
8293 if (local_player->gems_still_needed > 0 ||
8294 local_player->sokobanfields_still_needed > 0 ||
8295 local_player->lights_still_needed > 0)
8297 int element = Feld[x][y];
8298 int graphic = el2img(element);
8300 if (IS_ANIMATED(graphic))
8301 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8306 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8309 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8311 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8314 void CheckExitSP(int x, int y)
8316 if (local_player->gems_still_needed > 0)
8318 int element = Feld[x][y];
8319 int graphic = el2img(element);
8321 if (IS_ANIMATED(graphic))
8322 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8327 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8330 Feld[x][y] = EL_SP_EXIT_OPENING;
8332 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8335 static void CloseAllOpenTimegates()
8339 SCAN_PLAYFIELD(x, y)
8341 int element = Feld[x][y];
8343 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8345 Feld[x][y] = EL_TIMEGATE_CLOSING;
8347 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8352 void DrawTwinkleOnField(int x, int y)
8354 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8357 if (Feld[x][y] == EL_BD_DIAMOND)
8360 if (MovDelay[x][y] == 0) /* next animation frame */
8361 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8363 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8367 if (setup.direct_draw && MovDelay[x][y])
8368 SetDrawtoField(DRAW_BUFFERED);
8370 DrawLevelElementAnimation(x, y, Feld[x][y]);
8372 if (MovDelay[x][y] != 0)
8374 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8375 10 - MovDelay[x][y]);
8377 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8379 if (setup.direct_draw)
8383 dest_x = FX + SCREENX(x) * TILEX;
8384 dest_y = FY + SCREENY(y) * TILEY;
8386 BlitBitmap(drawto_field, window,
8387 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8388 SetDrawtoField(DRAW_DIRECT);
8394 void MauerWaechst(int x, int y)
8398 if (!MovDelay[x][y]) /* next animation frame */
8399 MovDelay[x][y] = 3 * delay;
8401 if (MovDelay[x][y]) /* wait some time before next frame */
8405 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8407 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8408 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8410 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8413 if (!MovDelay[x][y])
8415 if (MovDir[x][y] == MV_LEFT)
8417 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8418 DrawLevelField(x - 1, y);
8420 else if (MovDir[x][y] == MV_RIGHT)
8422 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8423 DrawLevelField(x + 1, y);
8425 else if (MovDir[x][y] == MV_UP)
8427 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8428 DrawLevelField(x, y - 1);
8432 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8433 DrawLevelField(x, y + 1);
8436 Feld[x][y] = Store[x][y];
8438 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8439 DrawLevelField(x, y);
8444 void MauerAbleger(int ax, int ay)
8446 int element = Feld[ax][ay];
8447 int graphic = el2img(element);
8448 boolean oben_frei = FALSE, unten_frei = FALSE;
8449 boolean links_frei = FALSE, rechts_frei = FALSE;
8450 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8451 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8452 boolean new_wall = FALSE;
8454 if (IS_ANIMATED(graphic))
8455 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8457 if (!MovDelay[ax][ay]) /* start building new wall */
8458 MovDelay[ax][ay] = 6;
8460 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8463 if (MovDelay[ax][ay])
8467 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8469 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8471 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8473 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8476 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8477 element == EL_EXPANDABLE_WALL_ANY)
8481 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8482 Store[ax][ay-1] = element;
8483 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8484 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8485 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8486 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8491 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8492 Store[ax][ay+1] = element;
8493 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8494 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8495 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8496 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8501 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8502 element == EL_EXPANDABLE_WALL_ANY ||
8503 element == EL_EXPANDABLE_WALL ||
8504 element == EL_BD_EXPANDABLE_WALL)
8508 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8509 Store[ax-1][ay] = element;
8510 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8511 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8512 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8513 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8519 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8520 Store[ax+1][ay] = element;
8521 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8522 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8523 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8524 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8529 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8530 DrawLevelField(ax, ay);
8532 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8534 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8535 unten_massiv = TRUE;
8536 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8537 links_massiv = TRUE;
8538 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8539 rechts_massiv = TRUE;
8541 if (((oben_massiv && unten_massiv) ||
8542 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8543 element == EL_EXPANDABLE_WALL) &&
8544 ((links_massiv && rechts_massiv) ||
8545 element == EL_EXPANDABLE_WALL_VERTICAL))
8546 Feld[ax][ay] = EL_WALL;
8549 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8552 void MauerAblegerStahl(int ax, int ay)
8554 int element = Feld[ax][ay];
8555 int graphic = el2img(element);
8556 boolean oben_frei = FALSE, unten_frei = FALSE;
8557 boolean links_frei = FALSE, rechts_frei = FALSE;
8558 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8559 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8560 boolean new_wall = FALSE;
8562 if (IS_ANIMATED(graphic))
8563 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8565 if (!MovDelay[ax][ay]) /* start building new wall */
8566 MovDelay[ax][ay] = 6;
8568 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8571 if (MovDelay[ax][ay])
8575 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8577 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8579 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8581 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8584 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8585 element == EL_EXPANDABLE_STEELWALL_ANY)
8589 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8590 Store[ax][ay-1] = element;
8591 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8592 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8593 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8594 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8599 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8600 Store[ax][ay+1] = element;
8601 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8602 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8603 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8604 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8609 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8610 element == EL_EXPANDABLE_STEELWALL_ANY)
8614 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8615 Store[ax-1][ay] = element;
8616 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8617 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8618 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8619 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8625 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8626 Store[ax+1][ay] = element;
8627 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8628 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8629 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8630 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8635 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8637 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8638 unten_massiv = TRUE;
8639 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8640 links_massiv = TRUE;
8641 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8642 rechts_massiv = TRUE;
8644 if (((oben_massiv && unten_massiv) ||
8645 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8646 ((links_massiv && rechts_massiv) ||
8647 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8648 Feld[ax][ay] = EL_WALL;
8651 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8654 void CheckForDragon(int x, int y)
8657 boolean dragon_found = FALSE;
8658 static int xy[4][2] =
8666 for (i = 0; i < NUM_DIRECTIONS; i++)
8668 for (j = 0; j < 4; j++)
8670 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8672 if (IN_LEV_FIELD(xx, yy) &&
8673 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8675 if (Feld[xx][yy] == EL_DRAGON)
8676 dragon_found = TRUE;
8685 for (i = 0; i < NUM_DIRECTIONS; i++)
8687 for (j = 0; j < 3; j++)
8689 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8691 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8693 Feld[xx][yy] = EL_EMPTY;
8694 DrawLevelField(xx, yy);
8703 static void InitBuggyBase(int x, int y)
8705 int element = Feld[x][y];
8706 int activating_delay = FRAMES_PER_SECOND / 4;
8709 (element == EL_SP_BUGGY_BASE ?
8710 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8711 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8713 element == EL_SP_BUGGY_BASE_ACTIVE ?
8714 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8717 static void WarnBuggyBase(int x, int y)
8720 static int xy[4][2] =
8728 for (i = 0; i < NUM_DIRECTIONS; i++)
8730 int xx = x + xy[i][0];
8731 int yy = y + xy[i][1];
8733 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8735 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8742 static void InitTrap(int x, int y)
8744 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8747 static void ActivateTrap(int x, int y)
8749 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8752 static void ChangeActiveTrap(int x, int y)
8754 int graphic = IMG_TRAP_ACTIVE;
8756 /* if new animation frame was drawn, correct crumbled sand border */
8757 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8758 DrawLevelFieldCrumbledSand(x, y);
8761 static int getSpecialActionElement(int element, int number, int base_element)
8763 return (element != EL_EMPTY ? element :
8764 number != -1 ? base_element + number - 1 :
8768 static int getModifiedActionNumber(int value_old, int operator, int operand,
8769 int value_min, int value_max)
8771 int value_new = (operator == CA_MODE_SET ? operand :
8772 operator == CA_MODE_ADD ? value_old + operand :
8773 operator == CA_MODE_SUBTRACT ? value_old - operand :
8774 operator == CA_MODE_MULTIPLY ? value_old * operand :
8775 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8776 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8779 return (value_new < value_min ? value_min :
8780 value_new > value_max ? value_max :
8784 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8786 struct ElementInfo *ei = &element_info[element];
8787 struct ElementChangeInfo *change = &ei->change_page[page];
8788 int target_element = change->target_element;
8789 int action_type = change->action_type;
8790 int action_mode = change->action_mode;
8791 int action_arg = change->action_arg;
8794 if (!change->has_action)
8797 /* ---------- determine action paramater values -------------------------- */
8799 int level_time_value =
8800 (level.time > 0 ? TimeLeft :
8803 int action_arg_element =
8804 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8805 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8806 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8809 int action_arg_direction =
8810 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8811 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8812 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8813 change->actual_trigger_side :
8814 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8815 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8818 int action_arg_number_min =
8819 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8822 int action_arg_number_max =
8823 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8824 action_type == CA_SET_LEVEL_GEMS ? 999 :
8825 action_type == CA_SET_LEVEL_TIME ? 9999 :
8826 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8827 action_type == CA_SET_CE_VALUE ? 9999 :
8828 action_type == CA_SET_CE_SCORE ? 9999 :
8831 int action_arg_number_reset =
8832 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8833 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8834 action_type == CA_SET_LEVEL_TIME ? level.time :
8835 action_type == CA_SET_LEVEL_SCORE ? 0 :
8836 #if USE_NEW_CUSTOM_VALUE
8837 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8839 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8841 action_type == CA_SET_CE_SCORE ? 0 :
8844 int action_arg_number =
8845 (action_arg <= CA_ARG_MAX ? action_arg :
8846 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8847 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8848 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8849 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8850 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8851 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8852 #if USE_NEW_CUSTOM_VALUE
8853 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8855 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8857 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8858 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8859 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8860 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8861 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8862 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8863 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8864 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8865 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8866 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8867 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8870 int action_arg_number_old =
8871 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8872 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8873 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8874 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8875 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8878 int action_arg_number_new =
8879 getModifiedActionNumber(action_arg_number_old,
8880 action_mode, action_arg_number,
8881 action_arg_number_min, action_arg_number_max);
8883 int trigger_player_bits =
8884 (change->actual_trigger_player >= EL_PLAYER_1 &&
8885 change->actual_trigger_player <= EL_PLAYER_4 ?
8886 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8889 int action_arg_player_bits =
8890 (action_arg >= CA_ARG_PLAYER_1 &&
8891 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8892 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8895 /* ---------- execute action -------------------------------------------- */
8897 switch (action_type)
8904 /* ---------- level actions ------------------------------------------- */
8906 case CA_RESTART_LEVEL:
8908 game.restart_level = TRUE;
8913 case CA_SHOW_ENVELOPE:
8915 int element = getSpecialActionElement(action_arg_element,
8916 action_arg_number, EL_ENVELOPE_1);
8918 if (IS_ENVELOPE(element))
8919 local_player->show_envelope = element;
8924 case CA_SET_LEVEL_TIME:
8926 if (level.time > 0) /* only modify limited time value */
8928 TimeLeft = action_arg_number_new;
8930 DrawGameValue_Time(TimeLeft);
8932 if (!TimeLeft && setup.time_limit)
8933 for (i = 0; i < MAX_PLAYERS; i++)
8934 KillPlayer(&stored_player[i]);
8940 case CA_SET_LEVEL_SCORE:
8942 local_player->score = action_arg_number_new;
8944 DrawGameValue_Score(local_player->score);
8949 case CA_SET_LEVEL_GEMS:
8951 local_player->gems_still_needed = action_arg_number_new;
8953 DrawGameValue_Emeralds(local_player->gems_still_needed);
8958 #if !USE_PLAYER_GRAVITY
8959 case CA_SET_LEVEL_GRAVITY:
8961 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8962 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8963 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8969 case CA_SET_LEVEL_WIND:
8971 game.wind_direction = action_arg_direction;
8976 /* ---------- player actions ------------------------------------------ */
8978 case CA_MOVE_PLAYER:
8980 /* automatically move to the next field in specified direction */
8981 for (i = 0; i < MAX_PLAYERS; i++)
8982 if (trigger_player_bits & (1 << i))
8983 stored_player[i].programmed_action = action_arg_direction;
8988 case CA_EXIT_PLAYER:
8990 for (i = 0; i < MAX_PLAYERS; i++)
8991 if (action_arg_player_bits & (1 << i))
8992 PlayerWins(&stored_player[i]);
8997 case CA_KILL_PLAYER:
8999 for (i = 0; i < MAX_PLAYERS; i++)
9000 if (action_arg_player_bits & (1 << i))
9001 KillPlayer(&stored_player[i]);
9006 case CA_SET_PLAYER_KEYS:
9008 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9009 int element = getSpecialActionElement(action_arg_element,
9010 action_arg_number, EL_KEY_1);
9012 if (IS_KEY(element))
9014 for (i = 0; i < MAX_PLAYERS; i++)
9016 if (trigger_player_bits & (1 << i))
9018 stored_player[i].key[KEY_NR(element)] = key_state;
9020 DrawGameDoorValues();
9028 case CA_SET_PLAYER_SPEED:
9030 for (i = 0; i < MAX_PLAYERS; i++)
9032 if (trigger_player_bits & (1 << i))
9034 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9036 if (action_arg == CA_ARG_SPEED_FASTER &&
9037 stored_player[i].cannot_move)
9039 action_arg_number = STEPSIZE_VERY_SLOW;
9041 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9042 action_arg == CA_ARG_SPEED_FASTER)
9044 action_arg_number = 2;
9045 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9048 else if (action_arg == CA_ARG_NUMBER_RESET)
9050 action_arg_number = level.initial_player_stepsize[i];
9054 getModifiedActionNumber(move_stepsize,
9057 action_arg_number_min,
9058 action_arg_number_max);
9060 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9067 case CA_SET_PLAYER_SHIELD:
9069 for (i = 0; i < MAX_PLAYERS; i++)
9071 if (trigger_player_bits & (1 << i))
9073 if (action_arg == CA_ARG_SHIELD_OFF)
9075 stored_player[i].shield_normal_time_left = 0;
9076 stored_player[i].shield_deadly_time_left = 0;
9078 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9080 stored_player[i].shield_normal_time_left = 999999;
9082 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9084 stored_player[i].shield_normal_time_left = 999999;
9085 stored_player[i].shield_deadly_time_left = 999999;
9093 #if USE_PLAYER_GRAVITY
9094 case CA_SET_PLAYER_GRAVITY:
9096 for (i = 0; i < MAX_PLAYERS; i++)
9098 if (trigger_player_bits & (1 << i))
9100 stored_player[i].gravity =
9101 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9102 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9103 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9104 stored_player[i].gravity);
9112 case CA_SET_PLAYER_ARTWORK:
9114 for (i = 0; i < MAX_PLAYERS; i++)
9116 if (trigger_player_bits & (1 << i))
9118 int artwork_element = action_arg_element;
9120 if (action_arg == CA_ARG_ELEMENT_RESET)
9122 (level.use_artwork_element[i] ? level.artwork_element[i] :
9123 stored_player[i].element_nr);
9125 #if USE_GFX_RESET_PLAYER_ARTWORK
9126 if (stored_player[i].artwork_element != artwork_element)
9127 stored_player[i].Frame = 0;
9130 stored_player[i].artwork_element = artwork_element;
9132 SetPlayerWaiting(&stored_player[i], FALSE);
9134 /* set number of special actions for bored and sleeping animation */
9135 stored_player[i].num_special_action_bored =
9136 get_num_special_action(artwork_element,
9137 ACTION_BORING_1, ACTION_BORING_LAST);
9138 stored_player[i].num_special_action_sleeping =
9139 get_num_special_action(artwork_element,
9140 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9147 /* ---------- CE actions ---------------------------------------------- */
9149 case CA_SET_CE_VALUE:
9151 #if USE_NEW_CUSTOM_VALUE
9152 int last_ce_value = CustomValue[x][y];
9154 CustomValue[x][y] = action_arg_number_new;
9156 if (CustomValue[x][y] != last_ce_value)
9158 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9159 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9161 if (CustomValue[x][y] == 0)
9163 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9164 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9172 case CA_SET_CE_SCORE:
9174 #if USE_NEW_CUSTOM_VALUE
9175 int last_ce_score = ei->collect_score;
9177 ei->collect_score = action_arg_number_new;
9179 if (ei->collect_score != last_ce_score)
9181 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9182 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9184 if (ei->collect_score == 0)
9188 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9189 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9192 This is a very special case that seems to be a mixture between
9193 CheckElementChange() and CheckTriggeredElementChange(): while
9194 the first one only affects single elements that are triggered
9195 directly, the second one affects multiple elements in the playfield
9196 that are triggered indirectly by another element. This is a third
9197 case: Changing the CE score always affects multiple identical CEs,
9198 so every affected CE must be checked, not only the single CE for
9199 which the CE score was changed in the first place (as every instance
9200 of that CE shares the same CE score, and therefore also can change)!
9202 SCAN_PLAYFIELD(xx, yy)
9204 if (Feld[xx][yy] == element)
9205 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9206 CE_SCORE_GETS_ZERO);
9215 /* ---------- engine actions ------------------------------------------ */
9217 case CA_SET_ENGINE_SCAN_MODE:
9219 InitPlayfieldScanMode(action_arg);
9229 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9231 int old_element = Feld[x][y];
9232 int new_element = GetElementFromGroupElement(element);
9233 int previous_move_direction = MovDir[x][y];
9234 #if USE_NEW_CUSTOM_VALUE
9235 int last_ce_value = CustomValue[x][y];
9237 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9238 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9239 boolean add_player_onto_element = (new_element_is_player &&
9240 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9241 /* this breaks SnakeBite when a snake is
9242 halfway through a door that closes */
9243 /* NOW FIXED AT LEVEL INIT IN files.c */
9244 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9246 IS_WALKABLE(old_element));
9249 /* check if element under the player changes from accessible to unaccessible
9250 (needed for special case of dropping element which then changes) */
9251 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9252 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9260 if (!add_player_onto_element)
9262 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9263 RemoveMovingField(x, y);
9267 Feld[x][y] = new_element;
9269 #if !USE_GFX_RESET_GFX_ANIMATION
9270 ResetGfxAnimation(x, y);
9271 ResetRandomAnimationValue(x, y);
9274 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9275 MovDir[x][y] = previous_move_direction;
9277 #if USE_NEW_CUSTOM_VALUE
9278 if (element_info[new_element].use_last_ce_value)
9279 CustomValue[x][y] = last_ce_value;
9282 InitField_WithBug1(x, y, FALSE);
9284 new_element = Feld[x][y]; /* element may have changed */
9286 #if USE_GFX_RESET_GFX_ANIMATION
9287 ResetGfxAnimation(x, y);
9288 ResetRandomAnimationValue(x, y);
9291 DrawLevelField(x, y);
9293 if (GFX_CRUMBLED(new_element))
9294 DrawLevelFieldCrumbledSandNeighbours(x, y);
9298 /* check if element under the player changes from accessible to unaccessible
9299 (needed for special case of dropping element which then changes) */
9300 /* (must be checked after creating new element for walkable group elements) */
9301 #if USE_FIX_KILLED_BY_NON_WALKABLE
9302 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9303 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9310 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9311 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9320 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9321 if (new_element_is_player)
9322 RelocatePlayer(x, y, new_element);
9325 ChangeCount[x][y]++; /* count number of changes in the same frame */
9327 TestIfBadThingTouchesPlayer(x, y);
9328 TestIfPlayerTouchesCustomElement(x, y);
9329 TestIfElementTouchesCustomElement(x, y);
9332 static void CreateField(int x, int y, int element)
9334 CreateFieldExt(x, y, element, FALSE);
9337 static void CreateElementFromChange(int x, int y, int element)
9339 element = GET_VALID_RUNTIME_ELEMENT(element);
9341 #if USE_STOP_CHANGED_ELEMENTS
9342 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9344 int old_element = Feld[x][y];
9346 /* prevent changed element from moving in same engine frame
9347 unless both old and new element can either fall or move */
9348 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9349 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9354 CreateFieldExt(x, y, element, TRUE);
9357 static boolean ChangeElement(int x, int y, int element, int page)
9359 struct ElementInfo *ei = &element_info[element];
9360 struct ElementChangeInfo *change = &ei->change_page[page];
9361 int ce_value = CustomValue[x][y];
9362 int ce_score = ei->collect_score;
9364 int old_element = Feld[x][y];
9366 /* always use default change event to prevent running into a loop */
9367 if (ChangeEvent[x][y] == -1)
9368 ChangeEvent[x][y] = CE_DELAY;
9370 if (ChangeEvent[x][y] == CE_DELAY)
9372 /* reset actual trigger element, trigger player and action element */
9373 change->actual_trigger_element = EL_EMPTY;
9374 change->actual_trigger_player = EL_PLAYER_1;
9375 change->actual_trigger_side = CH_SIDE_NONE;
9376 change->actual_trigger_ce_value = 0;
9377 change->actual_trigger_ce_score = 0;
9380 /* do not change elements more than a specified maximum number of changes */
9381 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9384 ChangeCount[x][y]++; /* count number of changes in the same frame */
9386 if (change->explode)
9393 if (change->use_target_content)
9395 boolean complete_replace = TRUE;
9396 boolean can_replace[3][3];
9399 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9402 boolean is_walkable;
9403 boolean is_diggable;
9404 boolean is_collectible;
9405 boolean is_removable;
9406 boolean is_destructible;
9407 int ex = x + xx - 1;
9408 int ey = y + yy - 1;
9409 int content_element = change->target_content.e[xx][yy];
9412 can_replace[xx][yy] = TRUE;
9414 if (ex == x && ey == y) /* do not check changing element itself */
9417 if (content_element == EL_EMPTY_SPACE)
9419 can_replace[xx][yy] = FALSE; /* do not replace border with space */
9424 if (!IN_LEV_FIELD(ex, ey))
9426 can_replace[xx][yy] = FALSE;
9427 complete_replace = FALSE;
9434 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9435 e = MovingOrBlocked2Element(ex, ey);
9437 is_empty = (IS_FREE(ex, ey) ||
9438 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9440 is_walkable = (is_empty || IS_WALKABLE(e));
9441 is_diggable = (is_empty || IS_DIGGABLE(e));
9442 is_collectible = (is_empty || IS_COLLECTIBLE(e));
9443 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9444 is_removable = (is_diggable || is_collectible);
9446 can_replace[xx][yy] =
9447 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9448 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9449 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9450 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9451 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9452 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9453 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9455 if (!can_replace[xx][yy])
9456 complete_replace = FALSE;
9459 if (!change->only_if_complete || complete_replace)
9461 boolean something_has_changed = FALSE;
9463 if (change->only_if_complete && change->use_random_replace &&
9464 RND(100) < change->random_percentage)
9467 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9469 int ex = x + xx - 1;
9470 int ey = y + yy - 1;
9471 int content_element;
9473 if (can_replace[xx][yy] && (!change->use_random_replace ||
9474 RND(100) < change->random_percentage))
9476 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9477 RemoveMovingField(ex, ey);
9479 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9481 content_element = change->target_content.e[xx][yy];
9482 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9483 ce_value, ce_score);
9485 CreateElementFromChange(ex, ey, target_element);
9487 something_has_changed = TRUE;
9489 /* for symmetry reasons, freeze newly created border elements */
9490 if (ex != x || ey != y)
9491 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9495 if (something_has_changed)
9497 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9498 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9504 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9505 ce_value, ce_score);
9507 if (element == EL_DIAGONAL_GROWING ||
9508 element == EL_DIAGONAL_SHRINKING)
9510 target_element = Store[x][y];
9512 Store[x][y] = EL_EMPTY;
9515 CreateElementFromChange(x, y, target_element);
9517 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9518 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9521 /* this uses direct change before indirect change */
9522 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9527 #if USE_NEW_DELAYED_ACTION
9529 static void HandleElementChange(int x, int y, int page)
9531 int element = MovingOrBlocked2Element(x, y);
9532 struct ElementInfo *ei = &element_info[element];
9533 struct ElementChangeInfo *change = &ei->change_page[page];
9536 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9537 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9540 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9541 x, y, element, element_info[element].token_name);
9542 printf("HandleElementChange(): This should never happen!\n");
9547 /* this can happen with classic bombs on walkable, changing elements */
9548 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9551 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9552 ChangeDelay[x][y] = 0;
9558 if (ChangeDelay[x][y] == 0) /* initialize element change */
9560 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9562 if (change->can_change)
9565 /* !!! not clear why graphic animation should be reset at all here !!! */
9566 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9567 #if USE_GFX_RESET_WHEN_NOT_MOVING
9568 /* when a custom element is about to change (for example by change delay),
9569 do not reset graphic animation when the custom element is moving */
9570 if (!IS_MOVING(x, y))
9573 ResetGfxAnimation(x, y);
9574 ResetRandomAnimationValue(x, y);
9578 if (change->pre_change_function)
9579 change->pre_change_function(x, y);
9583 ChangeDelay[x][y]--;
9585 if (ChangeDelay[x][y] != 0) /* continue element change */
9587 if (change->can_change)
9589 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9591 if (IS_ANIMATED(graphic))
9592 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9594 if (change->change_function)
9595 change->change_function(x, y);
9598 else /* finish element change */
9600 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9602 page = ChangePage[x][y];
9603 ChangePage[x][y] = -1;
9605 change = &ei->change_page[page];
9608 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9610 ChangeDelay[x][y] = 1; /* try change after next move step */
9611 ChangePage[x][y] = page; /* remember page to use for change */
9616 if (change->can_change)
9618 if (ChangeElement(x, y, element, page))
9620 if (change->post_change_function)
9621 change->post_change_function(x, y);
9625 if (change->has_action)
9626 ExecuteCustomElementAction(x, y, element, page);
9632 static void HandleElementChange(int x, int y, int page)
9634 int element = MovingOrBlocked2Element(x, y);
9635 struct ElementInfo *ei = &element_info[element];
9636 struct ElementChangeInfo *change = &ei->change_page[page];
9639 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9642 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9643 x, y, element, element_info[element].token_name);
9644 printf("HandleElementChange(): This should never happen!\n");
9649 /* this can happen with classic bombs on walkable, changing elements */
9650 if (!CAN_CHANGE(element))
9653 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9654 ChangeDelay[x][y] = 0;
9660 if (ChangeDelay[x][y] == 0) /* initialize element change */
9662 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9664 ResetGfxAnimation(x, y);
9665 ResetRandomAnimationValue(x, y);
9667 if (change->pre_change_function)
9668 change->pre_change_function(x, y);
9671 ChangeDelay[x][y]--;
9673 if (ChangeDelay[x][y] != 0) /* continue element change */
9675 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9677 if (IS_ANIMATED(graphic))
9678 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9680 if (change->change_function)
9681 change->change_function(x, y);
9683 else /* finish element change */
9685 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9687 page = ChangePage[x][y];
9688 ChangePage[x][y] = -1;
9690 change = &ei->change_page[page];
9693 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9695 ChangeDelay[x][y] = 1; /* try change after next move step */
9696 ChangePage[x][y] = page; /* remember page to use for change */
9701 if (ChangeElement(x, y, element, page))
9703 if (change->post_change_function)
9704 change->post_change_function(x, y);
9711 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9712 int trigger_element,
9718 boolean change_done_any = FALSE;
9719 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9722 if (!(trigger_events[trigger_element][trigger_event]))
9726 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9727 trigger_event, recursion_loop_depth, recursion_loop_detected,
9728 recursion_loop_element, EL_NAME(recursion_loop_element));
9731 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9733 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9735 int element = EL_CUSTOM_START + i;
9736 boolean change_done = FALSE;
9739 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9740 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9743 for (p = 0; p < element_info[element].num_change_pages; p++)
9745 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9747 if (change->can_change_or_has_action &&
9748 change->has_event[trigger_event] &&
9749 change->trigger_side & trigger_side &&
9750 change->trigger_player & trigger_player &&
9751 change->trigger_page & trigger_page_bits &&
9752 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9754 change->actual_trigger_element = trigger_element;
9755 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9756 change->actual_trigger_side = trigger_side;
9757 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9758 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9760 if ((change->can_change && !change_done) || change->has_action)
9764 SCAN_PLAYFIELD(x, y)
9766 if (Feld[x][y] == element)
9768 if (change->can_change && !change_done)
9770 ChangeDelay[x][y] = 1;
9771 ChangeEvent[x][y] = trigger_event;
9773 HandleElementChange(x, y, p);
9775 #if USE_NEW_DELAYED_ACTION
9776 else if (change->has_action)
9778 ExecuteCustomElementAction(x, y, element, p);
9779 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9782 if (change->has_action)
9784 ExecuteCustomElementAction(x, y, element, p);
9785 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9791 if (change->can_change)
9794 change_done_any = TRUE;
9801 RECURSION_LOOP_DETECTION_END();
9803 return change_done_any;
9806 static boolean CheckElementChangeExt(int x, int y,
9808 int trigger_element,
9813 boolean change_done = FALSE;
9816 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9817 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9820 if (Feld[x][y] == EL_BLOCKED)
9822 Blocked2Moving(x, y, &x, &y);
9823 element = Feld[x][y];
9827 /* check if element has already changed */
9828 if (Feld[x][y] != element)
9831 /* check if element has already changed or is about to change after moving */
9832 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9833 Feld[x][y] != element) ||
9835 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9836 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9837 ChangePage[x][y] != -1)))
9842 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9843 trigger_event, recursion_loop_depth, recursion_loop_detected,
9844 recursion_loop_element, EL_NAME(recursion_loop_element));
9847 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9849 for (p = 0; p < element_info[element].num_change_pages; p++)
9851 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9853 /* check trigger element for all events where the element that is checked
9854 for changing interacts with a directly adjacent element -- this is
9855 different to element changes that affect other elements to change on the
9856 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9857 boolean check_trigger_element =
9858 (trigger_event == CE_TOUCHING_X ||
9859 trigger_event == CE_HITTING_X ||
9860 trigger_event == CE_HIT_BY_X ||
9862 /* this one was forgotten until 3.2.3 */
9863 trigger_event == CE_DIGGING_X);
9866 if (change->can_change_or_has_action &&
9867 change->has_event[trigger_event] &&
9868 change->trigger_side & trigger_side &&
9869 change->trigger_player & trigger_player &&
9870 (!check_trigger_element ||
9871 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9873 change->actual_trigger_element = trigger_element;
9874 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9875 change->actual_trigger_side = trigger_side;
9876 change->actual_trigger_ce_value = CustomValue[x][y];
9877 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9879 /* special case: trigger element not at (x,y) position for some events */
9880 if (check_trigger_element)
9892 { 0, 0 }, { 0, 0 }, { 0, 0 },
9896 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9897 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9899 change->actual_trigger_ce_value = CustomValue[xx][yy];
9900 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9903 if (change->can_change && !change_done)
9905 ChangeDelay[x][y] = 1;
9906 ChangeEvent[x][y] = trigger_event;
9908 HandleElementChange(x, y, p);
9912 #if USE_NEW_DELAYED_ACTION
9913 else if (change->has_action)
9915 ExecuteCustomElementAction(x, y, element, p);
9916 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9919 if (change->has_action)
9921 ExecuteCustomElementAction(x, y, element, p);
9922 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9928 RECURSION_LOOP_DETECTION_END();
9933 static void PlayPlayerSound(struct PlayerInfo *player)
9935 int jx = player->jx, jy = player->jy;
9936 int sound_element = player->artwork_element;
9937 int last_action = player->last_action_waiting;
9938 int action = player->action_waiting;
9940 if (player->is_waiting)
9942 if (action != last_action)
9943 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9945 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9949 if (action != last_action)
9950 StopSound(element_info[sound_element].sound[last_action]);
9952 if (last_action == ACTION_SLEEPING)
9953 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9957 static void PlayAllPlayersSound()
9961 for (i = 0; i < MAX_PLAYERS; i++)
9962 if (stored_player[i].active)
9963 PlayPlayerSound(&stored_player[i]);
9966 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9968 boolean last_waiting = player->is_waiting;
9969 int move_dir = player->MovDir;
9971 player->dir_waiting = move_dir;
9972 player->last_action_waiting = player->action_waiting;
9976 if (!last_waiting) /* not waiting -> waiting */
9978 player->is_waiting = TRUE;
9980 player->frame_counter_bored =
9982 game.player_boring_delay_fixed +
9983 GetSimpleRandom(game.player_boring_delay_random);
9984 player->frame_counter_sleeping =
9986 game.player_sleeping_delay_fixed +
9987 GetSimpleRandom(game.player_sleeping_delay_random);
9989 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9992 if (game.player_sleeping_delay_fixed +
9993 game.player_sleeping_delay_random > 0 &&
9994 player->anim_delay_counter == 0 &&
9995 player->post_delay_counter == 0 &&
9996 FrameCounter >= player->frame_counter_sleeping)
9997 player->is_sleeping = TRUE;
9998 else if (game.player_boring_delay_fixed +
9999 game.player_boring_delay_random > 0 &&
10000 FrameCounter >= player->frame_counter_bored)
10001 player->is_bored = TRUE;
10003 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10004 player->is_bored ? ACTION_BORING :
10007 if (player->is_sleeping && player->use_murphy)
10009 /* special case for sleeping Murphy when leaning against non-free tile */
10011 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10012 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10013 !IS_MOVING(player->jx - 1, player->jy)))
10014 move_dir = MV_LEFT;
10015 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10016 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10017 !IS_MOVING(player->jx + 1, player->jy)))
10018 move_dir = MV_RIGHT;
10020 player->is_sleeping = FALSE;
10022 player->dir_waiting = move_dir;
10025 if (player->is_sleeping)
10027 if (player->num_special_action_sleeping > 0)
10029 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10031 int last_special_action = player->special_action_sleeping;
10032 int num_special_action = player->num_special_action_sleeping;
10033 int special_action =
10034 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10035 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10036 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10037 last_special_action + 1 : ACTION_SLEEPING);
10038 int special_graphic =
10039 el_act_dir2img(player->artwork_element, special_action, move_dir);
10041 player->anim_delay_counter =
10042 graphic_info[special_graphic].anim_delay_fixed +
10043 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10044 player->post_delay_counter =
10045 graphic_info[special_graphic].post_delay_fixed +
10046 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10048 player->special_action_sleeping = special_action;
10051 if (player->anim_delay_counter > 0)
10053 player->action_waiting = player->special_action_sleeping;
10054 player->anim_delay_counter--;
10056 else if (player->post_delay_counter > 0)
10058 player->post_delay_counter--;
10062 else if (player->is_bored)
10064 if (player->num_special_action_bored > 0)
10066 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10068 int special_action =
10069 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10070 int special_graphic =
10071 el_act_dir2img(player->artwork_element, special_action, move_dir);
10073 player->anim_delay_counter =
10074 graphic_info[special_graphic].anim_delay_fixed +
10075 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10076 player->post_delay_counter =
10077 graphic_info[special_graphic].post_delay_fixed +
10078 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10080 player->special_action_bored = special_action;
10083 if (player->anim_delay_counter > 0)
10085 player->action_waiting = player->special_action_bored;
10086 player->anim_delay_counter--;
10088 else if (player->post_delay_counter > 0)
10090 player->post_delay_counter--;
10095 else if (last_waiting) /* waiting -> not waiting */
10097 player->is_waiting = FALSE;
10098 player->is_bored = FALSE;
10099 player->is_sleeping = FALSE;
10101 player->frame_counter_bored = -1;
10102 player->frame_counter_sleeping = -1;
10104 player->anim_delay_counter = 0;
10105 player->post_delay_counter = 0;
10107 player->dir_waiting = player->MovDir;
10108 player->action_waiting = ACTION_DEFAULT;
10110 player->special_action_bored = ACTION_DEFAULT;
10111 player->special_action_sleeping = ACTION_DEFAULT;
10115 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10117 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10118 int left = player_action & JOY_LEFT;
10119 int right = player_action & JOY_RIGHT;
10120 int up = player_action & JOY_UP;
10121 int down = player_action & JOY_DOWN;
10122 int button1 = player_action & JOY_BUTTON_1;
10123 int button2 = player_action & JOY_BUTTON_2;
10124 int dx = (left ? -1 : right ? 1 : 0);
10125 int dy = (up ? -1 : down ? 1 : 0);
10127 if (!player->active || tape.pausing)
10133 snapped = SnapField(player, dx, dy);
10137 dropped = DropElement(player);
10139 moved = MovePlayer(player, dx, dy);
10142 if (tape.single_step && tape.recording && !tape.pausing)
10144 if (button1 || (dropped && !moved))
10146 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10147 SnapField(player, 0, 0); /* stop snapping */
10151 SetPlayerWaiting(player, FALSE);
10153 return player_action;
10157 /* no actions for this player (no input at player's configured device) */
10159 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10160 SnapField(player, 0, 0);
10161 CheckGravityMovementWhenNotMoving(player);
10163 if (player->MovPos == 0)
10164 SetPlayerWaiting(player, TRUE);
10166 if (player->MovPos == 0) /* needed for tape.playing */
10167 player->is_moving = FALSE;
10169 player->is_dropping = FALSE;
10170 player->is_dropping_pressed = FALSE;
10171 player->drop_pressed_delay = 0;
10177 static void CheckLevelTime()
10181 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10183 if (level.native_em_level->lev->home == 0) /* all players at home */
10185 PlayerWins(local_player);
10187 AllPlayersGone = TRUE;
10189 level.native_em_level->lev->home = -1;
10192 if (level.native_em_level->ply[0]->alive == 0 &&
10193 level.native_em_level->ply[1]->alive == 0 &&
10194 level.native_em_level->ply[2]->alive == 0 &&
10195 level.native_em_level->ply[3]->alive == 0) /* all dead */
10196 AllPlayersGone = TRUE;
10199 if (TimeFrames >= FRAMES_PER_SECOND)
10204 for (i = 0; i < MAX_PLAYERS; i++)
10206 struct PlayerInfo *player = &stored_player[i];
10208 if (SHIELD_ON(player))
10210 player->shield_normal_time_left--;
10212 if (player->shield_deadly_time_left > 0)
10213 player->shield_deadly_time_left--;
10217 if (!local_player->LevelSolved && !level.use_step_counter)
10225 if (TimeLeft <= 10 && setup.time_limit)
10226 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10228 DrawGameValue_Time(TimeLeft);
10230 if (!TimeLeft && setup.time_limit)
10232 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10233 level.native_em_level->lev->killed_out_of_time = TRUE;
10235 for (i = 0; i < MAX_PLAYERS; i++)
10236 KillPlayer(&stored_player[i]);
10239 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10240 DrawGameValue_Time(TimePlayed);
10242 level.native_em_level->lev->time =
10243 (level.time == 0 ? TimePlayed : TimeLeft);
10246 if (tape.recording || tape.playing)
10247 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10251 void AdvanceFrameAndPlayerCounters(int player_nr)
10255 /* advance frame counters (global frame counter and time frame counter) */
10259 /* advance player counters (counters for move delay, move animation etc.) */
10260 for (i = 0; i < MAX_PLAYERS; i++)
10262 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10263 int move_delay_value = stored_player[i].move_delay_value;
10264 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10266 if (!advance_player_counters) /* not all players may be affected */
10269 #if USE_NEW_PLAYER_ANIM
10270 if (move_frames == 0) /* less than one move per game frame */
10272 int stepsize = TILEX / move_delay_value;
10273 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10274 int count = (stored_player[i].is_moving ?
10275 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10277 if (count % delay == 0)
10282 stored_player[i].Frame += move_frames;
10284 if (stored_player[i].MovPos != 0)
10285 stored_player[i].StepFrame += move_frames;
10287 if (stored_player[i].move_delay > 0)
10288 stored_player[i].move_delay--;
10290 /* due to bugs in previous versions, counter must count up, not down */
10291 if (stored_player[i].push_delay != -1)
10292 stored_player[i].push_delay++;
10294 if (stored_player[i].drop_delay > 0)
10295 stored_player[i].drop_delay--;
10297 if (stored_player[i].is_dropping_pressed)
10298 stored_player[i].drop_pressed_delay++;
10302 void StartGameActions(boolean init_network_game, boolean record_tape,
10305 unsigned long new_random_seed = InitRND(random_seed);
10308 TapeStartRecording(new_random_seed);
10310 #if defined(NETWORK_AVALIABLE)
10311 if (init_network_game)
10313 SendToServer_StartPlaying();
10324 static unsigned long game_frame_delay = 0;
10325 unsigned long game_frame_delay_value;
10326 byte *recorded_player_action;
10327 byte summarized_player_action = 0;
10328 byte tape_action[MAX_PLAYERS];
10331 /* detect endless loops, caused by custom element programming */
10332 if (recursion_loop_detected && recursion_loop_depth == 0)
10334 char *message = getStringCat3("Internal Error ! Element ",
10335 EL_NAME(recursion_loop_element),
10336 " caused endless loop ! Quit the game ?");
10338 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10339 EL_NAME(recursion_loop_element));
10341 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10343 recursion_loop_detected = FALSE; /* if game should be continued */
10350 if (game.restart_level)
10351 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10353 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10355 if (level.native_em_level->lev->home == 0) /* all players at home */
10357 PlayerWins(local_player);
10359 AllPlayersGone = TRUE;
10361 level.native_em_level->lev->home = -1;
10364 if (level.native_em_level->ply[0]->alive == 0 &&
10365 level.native_em_level->ply[1]->alive == 0 &&
10366 level.native_em_level->ply[2]->alive == 0 &&
10367 level.native_em_level->ply[3]->alive == 0) /* all dead */
10368 AllPlayersGone = TRUE;
10371 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10374 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10377 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10380 game_frame_delay_value =
10381 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10383 if (tape.playing && tape.warp_forward && !tape.pausing)
10384 game_frame_delay_value = 0;
10386 /* ---------- main game synchronization point ---------- */
10388 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10390 if (network_playing && !network_player_action_received)
10392 /* try to get network player actions in time */
10394 #if defined(NETWORK_AVALIABLE)
10395 /* last chance to get network player actions without main loop delay */
10396 HandleNetworking();
10399 /* game was quit by network peer */
10400 if (game_status != GAME_MODE_PLAYING)
10403 if (!network_player_action_received)
10404 return; /* failed to get network player actions in time */
10406 /* do not yet reset "network_player_action_received" (for tape.pausing) */
10412 /* at this point we know that we really continue executing the game */
10414 network_player_action_received = FALSE;
10416 /* when playing tape, read previously recorded player input from tape data */
10417 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10420 /* TapePlayAction() may return NULL when toggling to "pause before death" */
10425 if (tape.set_centered_player)
10427 game.centered_player_nr_next = tape.centered_player_nr_next;
10428 game.set_centered_player = TRUE;
10431 for (i = 0; i < MAX_PLAYERS; i++)
10433 summarized_player_action |= stored_player[i].action;
10435 if (!network_playing)
10436 stored_player[i].effective_action = stored_player[i].action;
10439 #if defined(NETWORK_AVALIABLE)
10440 if (network_playing)
10441 SendToServer_MovePlayer(summarized_player_action);
10444 if (!options.network && !setup.team_mode)
10445 local_player->effective_action = summarized_player_action;
10447 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10449 for (i = 0; i < MAX_PLAYERS; i++)
10450 stored_player[i].effective_action =
10451 (i == game.centered_player_nr ? summarized_player_action : 0);
10454 if (recorded_player_action != NULL)
10455 for (i = 0; i < MAX_PLAYERS; i++)
10456 stored_player[i].effective_action = recorded_player_action[i];
10458 for (i = 0; i < MAX_PLAYERS; i++)
10460 tape_action[i] = stored_player[i].effective_action;
10462 /* (this can only happen in the R'n'D game engine) */
10463 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10464 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10467 /* only record actions from input devices, but not programmed actions */
10468 if (tape.recording)
10469 TapeRecordAction(tape_action);
10471 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10473 GameActions_EM_Main();
10481 void GameActions_EM_Main()
10483 byte effective_action[MAX_PLAYERS];
10484 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10487 for (i = 0; i < MAX_PLAYERS; i++)
10488 effective_action[i] = stored_player[i].effective_action;
10490 GameActions_EM(effective_action, warp_mode);
10494 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10497 void GameActions_RND()
10499 int magic_wall_x = 0, magic_wall_y = 0;
10500 int i, x, y, element, graphic;
10502 InitPlayfieldScanModeVars();
10504 #if USE_ONE_MORE_CHANGE_PER_FRAME
10505 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10507 SCAN_PLAYFIELD(x, y)
10509 ChangeCount[x][y] = 0;
10510 ChangeEvent[x][y] = -1;
10515 if (game.set_centered_player)
10517 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10519 /* switching to "all players" only possible if all players fit to screen */
10520 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10522 game.centered_player_nr_next = game.centered_player_nr;
10523 game.set_centered_player = FALSE;
10526 /* do not switch focus to non-existing (or non-active) player */
10527 if (game.centered_player_nr_next >= 0 &&
10528 !stored_player[game.centered_player_nr_next].active)
10530 game.centered_player_nr_next = game.centered_player_nr;
10531 game.set_centered_player = FALSE;
10535 if (game.set_centered_player &&
10536 ScreenMovPos == 0) /* screen currently aligned at tile position */
10540 if (game.centered_player_nr_next == -1)
10542 setScreenCenteredToAllPlayers(&sx, &sy);
10546 sx = stored_player[game.centered_player_nr_next].jx;
10547 sy = stored_player[game.centered_player_nr_next].jy;
10550 game.centered_player_nr = game.centered_player_nr_next;
10551 game.set_centered_player = FALSE;
10553 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10554 DrawGameDoorValues();
10557 for (i = 0; i < MAX_PLAYERS; i++)
10559 int actual_player_action = stored_player[i].effective_action;
10562 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10563 - rnd_equinox_tetrachloride 048
10564 - rnd_equinox_tetrachloride_ii 096
10565 - rnd_emanuel_schmieg 002
10566 - doctor_sloan_ww 001, 020
10568 if (stored_player[i].MovPos == 0)
10569 CheckGravityMovement(&stored_player[i]);
10572 /* overwrite programmed action with tape action */
10573 if (stored_player[i].programmed_action)
10574 actual_player_action = stored_player[i].programmed_action;
10576 PlayerActions(&stored_player[i], actual_player_action);
10578 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10581 ScrollScreen(NULL, SCROLL_GO_ON);
10583 /* for backwards compatibility, the following code emulates a fixed bug that
10584 occured when pushing elements (causing elements that just made their last
10585 pushing step to already (if possible) make their first falling step in the
10586 same game frame, which is bad); this code is also needed to use the famous
10587 "spring push bug" which is used in older levels and might be wanted to be
10588 used also in newer levels, but in this case the buggy pushing code is only
10589 affecting the "spring" element and no other elements */
10591 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10593 for (i = 0; i < MAX_PLAYERS; i++)
10595 struct PlayerInfo *player = &stored_player[i];
10596 int x = player->jx;
10597 int y = player->jy;
10599 if (player->active && player->is_pushing && player->is_moving &&
10601 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10602 Feld[x][y] == EL_SPRING))
10604 ContinueMoving(x, y);
10606 /* continue moving after pushing (this is actually a bug) */
10607 if (!IS_MOVING(x, y))
10608 Stop[x][y] = FALSE;
10614 debug_print_timestamp(0, "start main loop profiling");
10617 SCAN_PLAYFIELD(x, y)
10619 ChangeCount[x][y] = 0;
10620 ChangeEvent[x][y] = -1;
10622 /* this must be handled before main playfield loop */
10623 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10626 if (MovDelay[x][y] <= 0)
10630 #if USE_NEW_SNAP_DELAY
10631 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10634 if (MovDelay[x][y] <= 0)
10637 DrawLevelField(x, y);
10639 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10645 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10647 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10648 printf("GameActions(): This should never happen!\n");
10650 ChangePage[x][y] = -1;
10654 Stop[x][y] = FALSE;
10655 if (WasJustMoving[x][y] > 0)
10656 WasJustMoving[x][y]--;
10657 if (WasJustFalling[x][y] > 0)
10658 WasJustFalling[x][y]--;
10659 if (CheckCollision[x][y] > 0)
10660 CheckCollision[x][y]--;
10661 if (CheckImpact[x][y] > 0)
10662 CheckImpact[x][y]--;
10666 /* reset finished pushing action (not done in ContinueMoving() to allow
10667 continuous pushing animation for elements with zero push delay) */
10668 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10670 ResetGfxAnimation(x, y);
10671 DrawLevelField(x, y);
10675 if (IS_BLOCKED(x, y))
10679 Blocked2Moving(x, y, &oldx, &oldy);
10680 if (!IS_MOVING(oldx, oldy))
10682 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10683 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10684 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10685 printf("GameActions(): This should never happen!\n");
10692 debug_print_timestamp(0, "- time for pre-main loop:");
10695 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
10696 SCAN_PLAYFIELD(x, y)
10698 element = Feld[x][y];
10699 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10704 int element2 = element;
10705 int graphic2 = graphic;
10707 int element2 = Feld[x][y];
10708 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10710 int last_gfx_frame = GfxFrame[x][y];
10712 if (graphic_info[graphic2].anim_global_sync)
10713 GfxFrame[x][y] = FrameCounter;
10714 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10715 GfxFrame[x][y] = CustomValue[x][y];
10716 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10717 GfxFrame[x][y] = element_info[element2].collect_score;
10718 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10719 GfxFrame[x][y] = ChangeDelay[x][y];
10721 if (redraw && GfxFrame[x][y] != last_gfx_frame)
10722 DrawLevelGraphicAnimation(x, y, graphic2);
10725 ResetGfxFrame(x, y, TRUE);
10729 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10730 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10731 ResetRandomAnimationValue(x, y);
10735 SetRandomAnimationValue(x, y);
10739 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10742 #endif // -------------------- !!! TEST ONLY !!! --------------------
10745 debug_print_timestamp(0, "- time for TEST loop: -->");
10748 SCAN_PLAYFIELD(x, y)
10750 element = Feld[x][y];
10751 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10753 ResetGfxFrame(x, y, TRUE);
10755 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10756 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10757 ResetRandomAnimationValue(x, y);
10759 SetRandomAnimationValue(x, y);
10761 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10763 if (IS_INACTIVE(element))
10765 if (IS_ANIMATED(graphic))
10766 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10771 /* this may take place after moving, so 'element' may have changed */
10772 if (IS_CHANGING(x, y) &&
10773 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10775 int page = element_info[element].event_page_nr[CE_DELAY];
10778 HandleElementChange(x, y, page);
10780 if (CAN_CHANGE(element))
10781 HandleElementChange(x, y, page);
10783 if (HAS_ACTION(element))
10784 ExecuteCustomElementAction(x, y, element, page);
10787 element = Feld[x][y];
10788 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10791 #if 0 // ---------------------------------------------------------------------
10793 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10797 element = Feld[x][y];
10798 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10800 if (IS_ANIMATED(graphic) &&
10801 !IS_MOVING(x, y) &&
10803 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10805 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10806 DrawTwinkleOnField(x, y);
10808 else if (IS_MOVING(x, y))
10809 ContinueMoving(x, y);
10816 case EL_EM_EXIT_OPEN:
10817 case EL_SP_EXIT_OPEN:
10818 case EL_STEEL_EXIT_OPEN:
10819 case EL_EM_STEEL_EXIT_OPEN:
10820 case EL_SP_TERMINAL:
10821 case EL_SP_TERMINAL_ACTIVE:
10822 case EL_EXTRA_TIME:
10823 case EL_SHIELD_NORMAL:
10824 case EL_SHIELD_DEADLY:
10825 if (IS_ANIMATED(graphic))
10826 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10829 case EL_DYNAMITE_ACTIVE:
10830 case EL_EM_DYNAMITE_ACTIVE:
10831 case EL_DYNABOMB_PLAYER_1_ACTIVE:
10832 case EL_DYNABOMB_PLAYER_2_ACTIVE:
10833 case EL_DYNABOMB_PLAYER_3_ACTIVE:
10834 case EL_DYNABOMB_PLAYER_4_ACTIVE:
10835 case EL_SP_DISK_RED_ACTIVE:
10836 CheckDynamite(x, y);
10839 case EL_AMOEBA_GROWING:
10840 AmoebeWaechst(x, y);
10843 case EL_AMOEBA_SHRINKING:
10844 AmoebaDisappearing(x, y);
10847 #if !USE_NEW_AMOEBA_CODE
10848 case EL_AMOEBA_WET:
10849 case EL_AMOEBA_DRY:
10850 case EL_AMOEBA_FULL:
10852 case EL_EMC_DRIPPER:
10853 AmoebeAbleger(x, y);
10857 case EL_GAME_OF_LIFE:
10862 case EL_EXIT_CLOSED:
10866 case EL_EM_EXIT_CLOSED:
10870 case EL_STEEL_EXIT_CLOSED:
10871 CheckExitSteel(x, y);
10874 case EL_EM_STEEL_EXIT_CLOSED:
10875 CheckExitSteelEM(x, y);
10878 case EL_SP_EXIT_CLOSED:
10882 case EL_EXPANDABLE_WALL_GROWING:
10883 case EL_EXPANDABLE_STEELWALL_GROWING:
10884 MauerWaechst(x, y);
10887 case EL_EXPANDABLE_WALL:
10888 case EL_EXPANDABLE_WALL_HORIZONTAL:
10889 case EL_EXPANDABLE_WALL_VERTICAL:
10890 case EL_EXPANDABLE_WALL_ANY:
10891 case EL_BD_EXPANDABLE_WALL:
10892 MauerAbleger(x, y);
10895 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10896 case EL_EXPANDABLE_STEELWALL_VERTICAL:
10897 case EL_EXPANDABLE_STEELWALL_ANY:
10898 MauerAblegerStahl(x, y);
10902 CheckForDragon(x, y);
10908 case EL_ELEMENT_SNAPPING:
10909 case EL_DIAGONAL_SHRINKING:
10910 case EL_DIAGONAL_GROWING:
10913 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10915 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10920 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10921 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10926 #else // ---------------------------------------------------------------------
10928 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10932 element = Feld[x][y];
10933 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10935 if (IS_ANIMATED(graphic) &&
10936 !IS_MOVING(x, y) &&
10938 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10940 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10941 DrawTwinkleOnField(x, y);
10943 else if ((element == EL_ACID ||
10944 element == EL_EXIT_OPEN ||
10945 element == EL_EM_EXIT_OPEN ||
10946 element == EL_SP_EXIT_OPEN ||
10947 element == EL_STEEL_EXIT_OPEN ||
10948 element == EL_EM_STEEL_EXIT_OPEN ||
10949 element == EL_SP_TERMINAL ||
10950 element == EL_SP_TERMINAL_ACTIVE ||
10951 element == EL_EXTRA_TIME ||
10952 element == EL_SHIELD_NORMAL ||
10953 element == EL_SHIELD_DEADLY) &&
10954 IS_ANIMATED(graphic))
10955 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10956 else if (IS_MOVING(x, y))
10957 ContinueMoving(x, y);
10958 else if (IS_ACTIVE_BOMB(element))
10959 CheckDynamite(x, y);
10960 else if (element == EL_AMOEBA_GROWING)
10961 AmoebeWaechst(x, y);
10962 else if (element == EL_AMOEBA_SHRINKING)
10963 AmoebaDisappearing(x, y);
10965 #if !USE_NEW_AMOEBA_CODE
10966 else if (IS_AMOEBALIVE(element))
10967 AmoebeAbleger(x, y);
10970 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10972 else if (element == EL_EXIT_CLOSED)
10974 else if (element == EL_EM_EXIT_CLOSED)
10976 else if (element == EL_STEEL_EXIT_CLOSED)
10977 CheckExitSteel(x, y);
10978 else if (element == EL_EM_STEEL_EXIT_CLOSED)
10979 CheckExitSteelEM(x, y);
10980 else if (element == EL_SP_EXIT_CLOSED)
10982 else if (element == EL_EXPANDABLE_WALL_GROWING ||
10983 element == EL_EXPANDABLE_STEELWALL_GROWING)
10984 MauerWaechst(x, y);
10985 else if (element == EL_EXPANDABLE_WALL ||
10986 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10987 element == EL_EXPANDABLE_WALL_VERTICAL ||
10988 element == EL_EXPANDABLE_WALL_ANY ||
10989 element == EL_BD_EXPANDABLE_WALL)
10990 MauerAbleger(x, y);
10991 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10992 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10993 element == EL_EXPANDABLE_STEELWALL_ANY)
10994 MauerAblegerStahl(x, y);
10995 else if (element == EL_FLAMES)
10996 CheckForDragon(x, y);
10997 else if (element == EL_EXPLOSION)
10998 ; /* drawing of correct explosion animation is handled separately */
10999 else if (element == EL_ELEMENT_SNAPPING ||
11000 element == EL_DIAGONAL_SHRINKING ||
11001 element == EL_DIAGONAL_GROWING)
11003 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11005 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11007 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11008 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11010 #endif // ---------------------------------------------------------------------
11012 if (IS_BELT_ACTIVE(element))
11013 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11015 if (game.magic_wall_active)
11017 int jx = local_player->jx, jy = local_player->jy;
11019 /* play the element sound at the position nearest to the player */
11020 if ((element == EL_MAGIC_WALL_FULL ||
11021 element == EL_MAGIC_WALL_ACTIVE ||
11022 element == EL_MAGIC_WALL_EMPTYING ||
11023 element == EL_BD_MAGIC_WALL_FULL ||
11024 element == EL_BD_MAGIC_WALL_ACTIVE ||
11025 element == EL_BD_MAGIC_WALL_EMPTYING ||
11026 element == EL_DC_MAGIC_WALL_FULL ||
11027 element == EL_DC_MAGIC_WALL_ACTIVE ||
11028 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11029 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11038 debug_print_timestamp(0, "- time for MAIN loop: -->");
11041 #if USE_NEW_AMOEBA_CODE
11042 /* new experimental amoeba growth stuff */
11043 if (!(FrameCounter % 8))
11045 static unsigned long random = 1684108901;
11047 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11049 x = RND(lev_fieldx);
11050 y = RND(lev_fieldy);
11051 element = Feld[x][y];
11053 if (!IS_PLAYER(x,y) &&
11054 (element == EL_EMPTY ||
11055 CAN_GROW_INTO(element) ||
11056 element == EL_QUICKSAND_EMPTY ||
11057 element == EL_QUICKSAND_FAST_EMPTY ||
11058 element == EL_ACID_SPLASH_LEFT ||
11059 element == EL_ACID_SPLASH_RIGHT))
11061 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11062 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11063 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11064 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11065 Feld[x][y] = EL_AMOEBA_DROP;
11068 random = random * 129 + 1;
11074 if (game.explosions_delayed)
11077 game.explosions_delayed = FALSE;
11079 SCAN_PLAYFIELD(x, y)
11081 element = Feld[x][y];
11083 if (ExplodeField[x][y])
11084 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11085 else if (element == EL_EXPLOSION)
11086 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11088 ExplodeField[x][y] = EX_TYPE_NONE;
11091 game.explosions_delayed = TRUE;
11094 if (game.magic_wall_active)
11096 if (!(game.magic_wall_time_left % 4))
11098 int element = Feld[magic_wall_x][magic_wall_y];
11100 if (element == EL_BD_MAGIC_WALL_FULL ||
11101 element == EL_BD_MAGIC_WALL_ACTIVE ||
11102 element == EL_BD_MAGIC_WALL_EMPTYING)
11103 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11104 else if (element == EL_DC_MAGIC_WALL_FULL ||
11105 element == EL_DC_MAGIC_WALL_ACTIVE ||
11106 element == EL_DC_MAGIC_WALL_EMPTYING)
11107 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11109 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11112 if (game.magic_wall_time_left > 0)
11114 game.magic_wall_time_left--;
11115 if (!game.magic_wall_time_left)
11117 SCAN_PLAYFIELD(x, y)
11119 element = Feld[x][y];
11121 if (element == EL_MAGIC_WALL_ACTIVE ||
11122 element == EL_MAGIC_WALL_FULL)
11124 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11125 DrawLevelField(x, y);
11127 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11128 element == EL_BD_MAGIC_WALL_FULL)
11130 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11131 DrawLevelField(x, y);
11133 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11134 element == EL_DC_MAGIC_WALL_FULL)
11136 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11137 DrawLevelField(x, y);
11141 game.magic_wall_active = FALSE;
11146 if (game.light_time_left > 0)
11148 game.light_time_left--;
11150 if (game.light_time_left == 0)
11151 RedrawAllLightSwitchesAndInvisibleElements();
11154 if (game.timegate_time_left > 0)
11156 game.timegate_time_left--;
11158 if (game.timegate_time_left == 0)
11159 CloseAllOpenTimegates();
11162 if (game.lenses_time_left > 0)
11164 game.lenses_time_left--;
11166 if (game.lenses_time_left == 0)
11167 RedrawAllInvisibleElementsForLenses();
11170 if (game.magnify_time_left > 0)
11172 game.magnify_time_left--;
11174 if (game.magnify_time_left == 0)
11175 RedrawAllInvisibleElementsForMagnifier();
11178 for (i = 0; i < MAX_PLAYERS; i++)
11180 struct PlayerInfo *player = &stored_player[i];
11182 if (SHIELD_ON(player))
11184 if (player->shield_deadly_time_left)
11185 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11186 else if (player->shield_normal_time_left)
11187 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11194 PlayAllPlayersSound();
11196 if (options.debug) /* calculate frames per second */
11198 static unsigned long fps_counter = 0;
11199 static int fps_frames = 0;
11200 unsigned long fps_delay_ms = Counter() - fps_counter;
11204 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11206 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11209 fps_counter = Counter();
11212 redraw_mask |= REDRAW_FPS;
11215 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11217 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11219 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11221 local_player->show_envelope = 0;
11225 debug_print_timestamp(0, "stop main loop profiling ");
11226 printf("----------------------------------------------------------\n");
11229 /* use random number generator in every frame to make it less predictable */
11230 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11234 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11236 int min_x = x, min_y = y, max_x = x, max_y = y;
11239 for (i = 0; i < MAX_PLAYERS; i++)
11241 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11243 if (!stored_player[i].active || &stored_player[i] == player)
11246 min_x = MIN(min_x, jx);
11247 min_y = MIN(min_y, jy);
11248 max_x = MAX(max_x, jx);
11249 max_y = MAX(max_y, jy);
11252 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11255 static boolean AllPlayersInVisibleScreen()
11259 for (i = 0; i < MAX_PLAYERS; i++)
11261 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11263 if (!stored_player[i].active)
11266 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11273 void ScrollLevel(int dx, int dy)
11276 static Bitmap *bitmap_db_field2 = NULL;
11277 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11284 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11285 /* only horizontal XOR vertical scroll direction allowed */
11286 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11291 if (bitmap_db_field2 == NULL)
11292 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11294 /* needed when blitting directly to same bitmap -- should not be needed with
11295 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11296 BlitBitmap(drawto_field, bitmap_db_field2,
11297 FX + TILEX * (dx == -1) - softscroll_offset,
11298 FY + TILEY * (dy == -1) - softscroll_offset,
11299 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11300 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11301 FX + TILEX * (dx == 1) - softscroll_offset,
11302 FY + TILEY * (dy == 1) - softscroll_offset);
11303 BlitBitmap(bitmap_db_field2, drawto_field,
11304 FX + TILEX * (dx == 1) - softscroll_offset,
11305 FY + TILEY * (dy == 1) - softscroll_offset,
11306 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11307 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11308 FX + TILEX * (dx == 1) - softscroll_offset,
11309 FY + TILEY * (dy == 1) - softscroll_offset);
11314 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11315 int xsize = (BX2 - BX1 + 1);
11316 int ysize = (BY2 - BY1 + 1);
11317 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11318 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11319 int step = (start < end ? +1 : -1);
11321 for (i = start; i != end; i += step)
11323 BlitBitmap(drawto_field, drawto_field,
11324 FX + TILEX * (dx != 0 ? i + step : 0),
11325 FY + TILEY * (dy != 0 ? i + step : 0),
11326 TILEX * (dx != 0 ? 1 : xsize),
11327 TILEY * (dy != 0 ? 1 : ysize),
11328 FX + TILEX * (dx != 0 ? i : 0),
11329 FY + TILEY * (dy != 0 ? i : 0));
11334 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11336 BlitBitmap(drawto_field, drawto_field,
11337 FX + TILEX * (dx == -1) - softscroll_offset,
11338 FY + TILEY * (dy == -1) - softscroll_offset,
11339 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11340 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11341 FX + TILEX * (dx == 1) - softscroll_offset,
11342 FY + TILEY * (dy == 1) - softscroll_offset);
11348 x = (dx == 1 ? BX1 : BX2);
11349 for (y = BY1; y <= BY2; y++)
11350 DrawScreenField(x, y);
11355 y = (dy == 1 ? BY1 : BY2);
11356 for (x = BX1; x <= BX2; x++)
11357 DrawScreenField(x, y);
11360 redraw_mask |= REDRAW_FIELD;
11363 static boolean canFallDown(struct PlayerInfo *player)
11365 int jx = player->jx, jy = player->jy;
11367 return (IN_LEV_FIELD(jx, jy + 1) &&
11368 (IS_FREE(jx, jy + 1) ||
11369 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11370 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11371 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11374 static boolean canPassField(int x, int y, int move_dir)
11376 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11377 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11378 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11379 int nextx = x + dx;
11380 int nexty = y + dy;
11381 int element = Feld[x][y];
11383 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11384 !CAN_MOVE(element) &&
11385 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11386 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11387 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11390 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11392 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11393 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11394 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11398 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11399 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11400 (IS_DIGGABLE(Feld[newx][newy]) ||
11401 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11402 canPassField(newx, newy, move_dir)));
11405 static void CheckGravityMovement(struct PlayerInfo *player)
11407 #if USE_PLAYER_GRAVITY
11408 if (player->gravity && !player->programmed_action)
11410 if (game.gravity && !player->programmed_action)
11413 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11414 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11415 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11416 int jx = player->jx, jy = player->jy;
11417 boolean player_is_moving_to_valid_field =
11418 (!player_is_snapping &&
11419 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11420 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11421 boolean player_can_fall_down = canFallDown(player);
11423 if (player_can_fall_down &&
11424 !player_is_moving_to_valid_field)
11425 player->programmed_action = MV_DOWN;
11429 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11431 return CheckGravityMovement(player);
11433 #if USE_PLAYER_GRAVITY
11434 if (player->gravity && !player->programmed_action)
11436 if (game.gravity && !player->programmed_action)
11439 int jx = player->jx, jy = player->jy;
11440 boolean field_under_player_is_free =
11441 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11442 boolean player_is_standing_on_valid_field =
11443 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11444 (IS_WALKABLE(Feld[jx][jy]) &&
11445 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11447 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11448 player->programmed_action = MV_DOWN;
11453 MovePlayerOneStep()
11454 -----------------------------------------------------------------------------
11455 dx, dy: direction (non-diagonal) to try to move the player to
11456 real_dx, real_dy: direction as read from input device (can be diagonal)
11459 boolean MovePlayerOneStep(struct PlayerInfo *player,
11460 int dx, int dy, int real_dx, int real_dy)
11462 int jx = player->jx, jy = player->jy;
11463 int new_jx = jx + dx, new_jy = jy + dy;
11464 #if !USE_FIXED_DONT_RUN_INTO
11468 boolean player_can_move = !player->cannot_move;
11470 if (!player->active || (!dx && !dy))
11471 return MP_NO_ACTION;
11473 player->MovDir = (dx < 0 ? MV_LEFT :
11474 dx > 0 ? MV_RIGHT :
11476 dy > 0 ? MV_DOWN : MV_NONE);
11478 if (!IN_LEV_FIELD(new_jx, new_jy))
11479 return MP_NO_ACTION;
11481 if (!player_can_move)
11483 if (player->MovPos == 0)
11485 player->is_moving = FALSE;
11486 player->is_digging = FALSE;
11487 player->is_collecting = FALSE;
11488 player->is_snapping = FALSE;
11489 player->is_pushing = FALSE;
11494 if (!options.network && game.centered_player_nr == -1 &&
11495 !AllPlayersInSight(player, new_jx, new_jy))
11496 return MP_NO_ACTION;
11498 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11499 return MP_NO_ACTION;
11502 #if !USE_FIXED_DONT_RUN_INTO
11503 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11505 /* (moved to DigField()) */
11506 if (player_can_move && DONT_RUN_INTO(element))
11508 if (element == EL_ACID && dx == 0 && dy == 1)
11510 SplashAcid(new_jx, new_jy);
11511 Feld[jx][jy] = EL_PLAYER_1;
11512 InitMovingField(jx, jy, MV_DOWN);
11513 Store[jx][jy] = EL_ACID;
11514 ContinueMoving(jx, jy);
11515 BuryPlayer(player);
11518 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11524 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11525 if (can_move != MP_MOVING)
11528 /* check if DigField() has caused relocation of the player */
11529 if (player->jx != jx || player->jy != jy)
11530 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11532 StorePlayer[jx][jy] = 0;
11533 player->last_jx = jx;
11534 player->last_jy = jy;
11535 player->jx = new_jx;
11536 player->jy = new_jy;
11537 StorePlayer[new_jx][new_jy] = player->element_nr;
11539 if (player->move_delay_value_next != -1)
11541 player->move_delay_value = player->move_delay_value_next;
11542 player->move_delay_value_next = -1;
11546 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11548 player->step_counter++;
11550 PlayerVisit[jx][jy] = FrameCounter;
11552 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11553 player->is_moving = TRUE;
11557 /* should better be called in MovePlayer(), but this breaks some tapes */
11558 ScrollPlayer(player, SCROLL_INIT);
11564 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11566 int jx = player->jx, jy = player->jy;
11567 int old_jx = jx, old_jy = jy;
11568 int moved = MP_NO_ACTION;
11570 if (!player->active)
11575 if (player->MovPos == 0)
11577 player->is_moving = FALSE;
11578 player->is_digging = FALSE;
11579 player->is_collecting = FALSE;
11580 player->is_snapping = FALSE;
11581 player->is_pushing = FALSE;
11587 if (player->move_delay > 0)
11590 player->move_delay = -1; /* set to "uninitialized" value */
11592 /* store if player is automatically moved to next field */
11593 player->is_auto_moving = (player->programmed_action != MV_NONE);
11595 /* remove the last programmed player action */
11596 player->programmed_action = 0;
11598 if (player->MovPos)
11600 /* should only happen if pre-1.2 tape recordings are played */
11601 /* this is only for backward compatibility */
11603 int original_move_delay_value = player->move_delay_value;
11606 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11610 /* scroll remaining steps with finest movement resolution */
11611 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11613 while (player->MovPos)
11615 ScrollPlayer(player, SCROLL_GO_ON);
11616 ScrollScreen(NULL, SCROLL_GO_ON);
11618 AdvanceFrameAndPlayerCounters(player->index_nr);
11624 player->move_delay_value = original_move_delay_value;
11627 player->is_active = FALSE;
11629 if (player->last_move_dir & MV_HORIZONTAL)
11631 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11632 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11636 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11637 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11640 #if USE_FIXED_BORDER_RUNNING_GFX
11641 if (!moved && !player->is_active)
11643 player->is_moving = FALSE;
11644 player->is_digging = FALSE;
11645 player->is_collecting = FALSE;
11646 player->is_snapping = FALSE;
11647 player->is_pushing = FALSE;
11655 if (moved & MP_MOVING && !ScreenMovPos &&
11656 (player->index_nr == game.centered_player_nr ||
11657 game.centered_player_nr == -1))
11659 if (moved & MP_MOVING && !ScreenMovPos &&
11660 (player == local_player || !options.network))
11663 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11664 int offset = (setup.scroll_delay ? 3 : 0);
11666 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11668 /* actual player has left the screen -- scroll in that direction */
11669 if (jx != old_jx) /* player has moved horizontally */
11670 scroll_x += (jx - old_jx);
11671 else /* player has moved vertically */
11672 scroll_y += (jy - old_jy);
11676 if (jx != old_jx) /* player has moved horizontally */
11678 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
11679 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11680 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11682 /* don't scroll over playfield boundaries */
11683 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11684 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11686 /* don't scroll more than one field at a time */
11687 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11689 /* don't scroll against the player's moving direction */
11690 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
11691 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11692 scroll_x = old_scroll_x;
11694 else /* player has moved vertically */
11696 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
11697 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11698 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11700 /* don't scroll over playfield boundaries */
11701 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11702 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11704 /* don't scroll more than one field at a time */
11705 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11707 /* don't scroll against the player's moving direction */
11708 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
11709 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11710 scroll_y = old_scroll_y;
11714 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11717 if (!options.network && game.centered_player_nr == -1 &&
11718 !AllPlayersInVisibleScreen())
11720 scroll_x = old_scroll_x;
11721 scroll_y = old_scroll_y;
11725 if (!options.network && !AllPlayersInVisibleScreen())
11727 scroll_x = old_scroll_x;
11728 scroll_y = old_scroll_y;
11733 ScrollScreen(player, SCROLL_INIT);
11734 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11739 player->StepFrame = 0;
11741 if (moved & MP_MOVING)
11743 if (old_jx != jx && old_jy == jy)
11744 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11745 else if (old_jx == jx && old_jy != jy)
11746 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11748 DrawLevelField(jx, jy); /* for "crumbled sand" */
11750 player->last_move_dir = player->MovDir;
11751 player->is_moving = TRUE;
11752 player->is_snapping = FALSE;
11753 player->is_switching = FALSE;
11754 player->is_dropping = FALSE;
11755 player->is_dropping_pressed = FALSE;
11756 player->drop_pressed_delay = 0;
11759 /* should better be called here than above, but this breaks some tapes */
11760 ScrollPlayer(player, SCROLL_INIT);
11765 CheckGravityMovementWhenNotMoving(player);
11767 player->is_moving = FALSE;
11769 /* at this point, the player is allowed to move, but cannot move right now
11770 (e.g. because of something blocking the way) -- ensure that the player
11771 is also allowed to move in the next frame (in old versions before 3.1.1,
11772 the player was forced to wait again for eight frames before next try) */
11774 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11775 player->move_delay = 0; /* allow direct movement in the next frame */
11778 if (player->move_delay == -1) /* not yet initialized by DigField() */
11779 player->move_delay = player->move_delay_value;
11781 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11783 TestIfPlayerTouchesBadThing(jx, jy);
11784 TestIfPlayerTouchesCustomElement(jx, jy);
11787 if (!player->active)
11788 RemovePlayer(player);
11793 void ScrollPlayer(struct PlayerInfo *player, int mode)
11795 int jx = player->jx, jy = player->jy;
11796 int last_jx = player->last_jx, last_jy = player->last_jy;
11797 int move_stepsize = TILEX / player->move_delay_value;
11799 #if USE_NEW_PLAYER_SPEED
11800 if (!player->active)
11803 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11806 if (!player->active || player->MovPos == 0)
11810 if (mode == SCROLL_INIT)
11812 player->actual_frame_counter = FrameCounter;
11813 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11815 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11816 Feld[last_jx][last_jy] == EL_EMPTY)
11818 int last_field_block_delay = 0; /* start with no blocking at all */
11819 int block_delay_adjustment = player->block_delay_adjustment;
11821 /* if player blocks last field, add delay for exactly one move */
11822 if (player->block_last_field)
11824 last_field_block_delay += player->move_delay_value;
11826 /* when blocking enabled, prevent moving up despite gravity */
11827 #if USE_PLAYER_GRAVITY
11828 if (player->gravity && player->MovDir == MV_UP)
11829 block_delay_adjustment = -1;
11831 if (game.gravity && player->MovDir == MV_UP)
11832 block_delay_adjustment = -1;
11836 /* add block delay adjustment (also possible when not blocking) */
11837 last_field_block_delay += block_delay_adjustment;
11839 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11840 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11843 #if USE_NEW_PLAYER_SPEED
11844 if (player->MovPos != 0) /* player has not yet reached destination */
11850 else if (!FrameReached(&player->actual_frame_counter, 1))
11853 #if USE_NEW_PLAYER_SPEED
11854 if (player->MovPos != 0)
11856 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11857 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11859 /* before DrawPlayer() to draw correct player graphic for this case */
11860 if (player->MovPos == 0)
11861 CheckGravityMovement(player);
11864 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11865 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11867 /* before DrawPlayer() to draw correct player graphic for this case */
11868 if (player->MovPos == 0)
11869 CheckGravityMovement(player);
11872 if (player->MovPos == 0) /* player reached destination field */
11874 if (player->move_delay_reset_counter > 0)
11876 player->move_delay_reset_counter--;
11878 if (player->move_delay_reset_counter == 0)
11880 /* continue with normal speed after quickly moving through gate */
11881 HALVE_PLAYER_SPEED(player);
11883 /* be able to make the next move without delay */
11884 player->move_delay = 0;
11888 player->last_jx = jx;
11889 player->last_jy = jy;
11891 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11892 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11893 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11894 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11895 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11896 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11898 DrawPlayer(player); /* needed here only to cleanup last field */
11899 RemovePlayer(player);
11901 if (local_player->friends_still_needed == 0 ||
11902 IS_SP_ELEMENT(Feld[jx][jy]))
11903 PlayerWins(player);
11906 /* this breaks one level: "machine", level 000 */
11908 int move_direction = player->MovDir;
11909 int enter_side = MV_DIR_OPPOSITE(move_direction);
11910 int leave_side = move_direction;
11911 int old_jx = last_jx;
11912 int old_jy = last_jy;
11913 int old_element = Feld[old_jx][old_jy];
11914 int new_element = Feld[jx][jy];
11916 if (IS_CUSTOM_ELEMENT(old_element))
11917 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11919 player->index_bit, leave_side);
11921 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11922 CE_PLAYER_LEAVES_X,
11923 player->index_bit, leave_side);
11925 if (IS_CUSTOM_ELEMENT(new_element))
11926 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11927 player->index_bit, enter_side);
11929 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11930 CE_PLAYER_ENTERS_X,
11931 player->index_bit, enter_side);
11933 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11934 CE_MOVE_OF_X, move_direction);
11937 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11939 TestIfPlayerTouchesBadThing(jx, jy);
11940 TestIfPlayerTouchesCustomElement(jx, jy);
11942 /* needed because pushed element has not yet reached its destination,
11943 so it would trigger a change event at its previous field location */
11944 if (!player->is_pushing)
11945 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11947 if (!player->active)
11948 RemovePlayer(player);
11951 if (!local_player->LevelSolved && level.use_step_counter)
11961 if (TimeLeft <= 10 && setup.time_limit)
11962 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11964 DrawGameValue_Time(TimeLeft);
11966 if (!TimeLeft && setup.time_limit)
11967 for (i = 0; i < MAX_PLAYERS; i++)
11968 KillPlayer(&stored_player[i]);
11970 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11971 DrawGameValue_Time(TimePlayed);
11974 if (tape.single_step && tape.recording && !tape.pausing &&
11975 !player->programmed_action)
11976 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11980 void ScrollScreen(struct PlayerInfo *player, int mode)
11982 static unsigned long screen_frame_counter = 0;
11984 if (mode == SCROLL_INIT)
11986 /* set scrolling step size according to actual player's moving speed */
11987 ScrollStepSize = TILEX / player->move_delay_value;
11989 screen_frame_counter = FrameCounter;
11990 ScreenMovDir = player->MovDir;
11991 ScreenMovPos = player->MovPos;
11992 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11995 else if (!FrameReached(&screen_frame_counter, 1))
12000 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12001 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12002 redraw_mask |= REDRAW_FIELD;
12005 ScreenMovDir = MV_NONE;
12008 void TestIfPlayerTouchesCustomElement(int x, int y)
12010 static int xy[4][2] =
12017 static int trigger_sides[4][2] =
12019 /* center side border side */
12020 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12021 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12022 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12023 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12025 static int touch_dir[4] =
12027 MV_LEFT | MV_RIGHT,
12032 int center_element = Feld[x][y]; /* should always be non-moving! */
12035 for (i = 0; i < NUM_DIRECTIONS; i++)
12037 int xx = x + xy[i][0];
12038 int yy = y + xy[i][1];
12039 int center_side = trigger_sides[i][0];
12040 int border_side = trigger_sides[i][1];
12041 int border_element;
12043 if (!IN_LEV_FIELD(xx, yy))
12046 if (IS_PLAYER(x, y))
12048 struct PlayerInfo *player = PLAYERINFO(x, y);
12050 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12051 border_element = Feld[xx][yy]; /* may be moving! */
12052 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12053 border_element = Feld[xx][yy];
12054 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12055 border_element = MovingOrBlocked2Element(xx, yy);
12057 continue; /* center and border element do not touch */
12059 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12060 player->index_bit, border_side);
12061 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12062 CE_PLAYER_TOUCHES_X,
12063 player->index_bit, border_side);
12065 else if (IS_PLAYER(xx, yy))
12067 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12069 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12071 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12072 continue; /* center and border element do not touch */
12075 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12076 player->index_bit, center_side);
12077 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12078 CE_PLAYER_TOUCHES_X,
12079 player->index_bit, center_side);
12085 #if USE_ELEMENT_TOUCHING_BUGFIX
12087 void TestIfElementTouchesCustomElement(int x, int y)
12089 static int xy[4][2] =
12096 static int trigger_sides[4][2] =
12098 /* center side border side */
12099 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12100 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12101 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12102 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12104 static int touch_dir[4] =
12106 MV_LEFT | MV_RIGHT,
12111 boolean change_center_element = FALSE;
12112 int center_element = Feld[x][y]; /* should always be non-moving! */
12113 int border_element_old[NUM_DIRECTIONS];
12116 for (i = 0; i < NUM_DIRECTIONS; i++)
12118 int xx = x + xy[i][0];
12119 int yy = y + xy[i][1];
12120 int border_element;
12122 border_element_old[i] = -1;
12124 if (!IN_LEV_FIELD(xx, yy))
12127 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12128 border_element = Feld[xx][yy]; /* may be moving! */
12129 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12130 border_element = Feld[xx][yy];
12131 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12132 border_element = MovingOrBlocked2Element(xx, yy);
12134 continue; /* center and border element do not touch */
12136 border_element_old[i] = border_element;
12139 for (i = 0; i < NUM_DIRECTIONS; i++)
12141 int xx = x + xy[i][0];
12142 int yy = y + xy[i][1];
12143 int center_side = trigger_sides[i][0];
12144 int border_element = border_element_old[i];
12146 if (border_element == -1)
12149 /* check for change of border element */
12150 CheckElementChangeBySide(xx, yy, border_element, center_element,
12151 CE_TOUCHING_X, center_side);
12154 for (i = 0; i < NUM_DIRECTIONS; i++)
12156 int border_side = trigger_sides[i][1];
12157 int border_element = border_element_old[i];
12159 if (border_element == -1)
12162 /* check for change of center element (but change it only once) */
12163 if (!change_center_element)
12164 change_center_element =
12165 CheckElementChangeBySide(x, y, center_element, border_element,
12166 CE_TOUCHING_X, border_side);
12172 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12174 static int xy[4][2] =
12181 static int trigger_sides[4][2] =
12183 /* center side border side */
12184 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12185 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12186 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12187 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12189 static int touch_dir[4] =
12191 MV_LEFT | MV_RIGHT,
12196 boolean change_center_element = FALSE;
12197 int center_element = Feld[x][y]; /* should always be non-moving! */
12200 for (i = 0; i < NUM_DIRECTIONS; i++)
12202 int xx = x + xy[i][0];
12203 int yy = y + xy[i][1];
12204 int center_side = trigger_sides[i][0];
12205 int border_side = trigger_sides[i][1];
12206 int border_element;
12208 if (!IN_LEV_FIELD(xx, yy))
12211 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12212 border_element = Feld[xx][yy]; /* may be moving! */
12213 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12214 border_element = Feld[xx][yy];
12215 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12216 border_element = MovingOrBlocked2Element(xx, yy);
12218 continue; /* center and border element do not touch */
12220 /* check for change of center element (but change it only once) */
12221 if (!change_center_element)
12222 change_center_element =
12223 CheckElementChangeBySide(x, y, center_element, border_element,
12224 CE_TOUCHING_X, border_side);
12226 /* check for change of border element */
12227 CheckElementChangeBySide(xx, yy, border_element, center_element,
12228 CE_TOUCHING_X, center_side);
12234 void TestIfElementHitsCustomElement(int x, int y, int direction)
12236 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12237 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12238 int hitx = x + dx, hity = y + dy;
12239 int hitting_element = Feld[x][y];
12240 int touched_element;
12242 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12245 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12246 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12248 if (IN_LEV_FIELD(hitx, hity))
12250 int opposite_direction = MV_DIR_OPPOSITE(direction);
12251 int hitting_side = direction;
12252 int touched_side = opposite_direction;
12253 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12254 MovDir[hitx][hity] != direction ||
12255 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12261 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12262 CE_HITTING_X, touched_side);
12264 CheckElementChangeBySide(hitx, hity, touched_element,
12265 hitting_element, CE_HIT_BY_X, hitting_side);
12267 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12268 CE_HIT_BY_SOMETHING, opposite_direction);
12272 /* "hitting something" is also true when hitting the playfield border */
12273 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12274 CE_HITTING_SOMETHING, direction);
12278 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12280 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12281 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12282 int hitx = x + dx, hity = y + dy;
12283 int hitting_element = Feld[x][y];
12284 int touched_element;
12286 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12287 !IS_FREE(hitx, hity) &&
12288 (!IS_MOVING(hitx, hity) ||
12289 MovDir[hitx][hity] != direction ||
12290 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12293 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12297 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12301 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12302 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12304 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12305 EP_CAN_SMASH_EVERYTHING, direction);
12307 if (IN_LEV_FIELD(hitx, hity))
12309 int opposite_direction = MV_DIR_OPPOSITE(direction);
12310 int hitting_side = direction;
12311 int touched_side = opposite_direction;
12313 int touched_element = MovingOrBlocked2Element(hitx, hity);
12316 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12317 MovDir[hitx][hity] != direction ||
12318 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12327 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12328 CE_SMASHED_BY_SOMETHING, opposite_direction);
12330 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12331 CE_OTHER_IS_SMASHING, touched_side);
12333 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12334 CE_OTHER_GETS_SMASHED, hitting_side);
12340 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12342 int i, kill_x = -1, kill_y = -1;
12344 int bad_element = -1;
12345 static int test_xy[4][2] =
12352 static int test_dir[4] =
12360 for (i = 0; i < NUM_DIRECTIONS; i++)
12362 int test_x, test_y, test_move_dir, test_element;
12364 test_x = good_x + test_xy[i][0];
12365 test_y = good_y + test_xy[i][1];
12367 if (!IN_LEV_FIELD(test_x, test_y))
12371 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12373 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12375 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12376 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12378 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12379 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12383 bad_element = test_element;
12389 if (kill_x != -1 || kill_y != -1)
12391 if (IS_PLAYER(good_x, good_y))
12393 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12395 if (player->shield_deadly_time_left > 0 &&
12396 !IS_INDESTRUCTIBLE(bad_element))
12397 Bang(kill_x, kill_y);
12398 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12399 KillPlayer(player);
12402 Bang(good_x, good_y);
12406 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12408 int i, kill_x = -1, kill_y = -1;
12409 int bad_element = Feld[bad_x][bad_y];
12410 static int test_xy[4][2] =
12417 static int touch_dir[4] =
12419 MV_LEFT | MV_RIGHT,
12424 static int test_dir[4] =
12432 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12435 for (i = 0; i < NUM_DIRECTIONS; i++)
12437 int test_x, test_y, test_move_dir, test_element;
12439 test_x = bad_x + test_xy[i][0];
12440 test_y = bad_y + test_xy[i][1];
12441 if (!IN_LEV_FIELD(test_x, test_y))
12445 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12447 test_element = Feld[test_x][test_y];
12449 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12450 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12452 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12453 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12455 /* good thing is player or penguin that does not move away */
12456 if (IS_PLAYER(test_x, test_y))
12458 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12460 if (bad_element == EL_ROBOT && player->is_moving)
12461 continue; /* robot does not kill player if he is moving */
12463 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12465 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12466 continue; /* center and border element do not touch */
12473 else if (test_element == EL_PENGUIN)
12482 if (kill_x != -1 || kill_y != -1)
12484 if (IS_PLAYER(kill_x, kill_y))
12486 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12488 if (player->shield_deadly_time_left > 0 &&
12489 !IS_INDESTRUCTIBLE(bad_element))
12490 Bang(bad_x, bad_y);
12491 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12492 KillPlayer(player);
12495 Bang(kill_x, kill_y);
12499 void TestIfPlayerTouchesBadThing(int x, int y)
12501 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12504 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12506 TestIfGoodThingHitsBadThing(x, y, move_dir);
12509 void TestIfBadThingTouchesPlayer(int x, int y)
12511 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12514 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12516 TestIfBadThingHitsGoodThing(x, y, move_dir);
12519 void TestIfFriendTouchesBadThing(int x, int y)
12521 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12524 void TestIfBadThingTouchesFriend(int x, int y)
12526 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12529 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12531 int i, kill_x = bad_x, kill_y = bad_y;
12532 static int xy[4][2] =
12540 for (i = 0; i < NUM_DIRECTIONS; i++)
12544 x = bad_x + xy[i][0];
12545 y = bad_y + xy[i][1];
12546 if (!IN_LEV_FIELD(x, y))
12549 element = Feld[x][y];
12550 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12551 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12559 if (kill_x != bad_x || kill_y != bad_y)
12560 Bang(bad_x, bad_y);
12563 void KillPlayer(struct PlayerInfo *player)
12565 int jx = player->jx, jy = player->jy;
12567 if (!player->active)
12570 /* the following code was introduced to prevent an infinite loop when calling
12572 -> CheckTriggeredElementChangeExt()
12573 -> ExecuteCustomElementAction()
12575 -> (infinitely repeating the above sequence of function calls)
12576 which occurs when killing the player while having a CE with the setting
12577 "kill player X when explosion of <player X>"; the solution using a new
12578 field "player->killed" was chosen for backwards compatibility, although
12579 clever use of the fields "player->active" etc. would probably also work */
12581 if (player->killed)
12585 player->killed = TRUE;
12587 /* remove accessible field at the player's position */
12588 Feld[jx][jy] = EL_EMPTY;
12590 /* deactivate shield (else Bang()/Explode() would not work right) */
12591 player->shield_normal_time_left = 0;
12592 player->shield_deadly_time_left = 0;
12595 BuryPlayer(player);
12598 static void KillPlayerUnlessEnemyProtected(int x, int y)
12600 if (!PLAYER_ENEMY_PROTECTED(x, y))
12601 KillPlayer(PLAYERINFO(x, y));
12604 static void KillPlayerUnlessExplosionProtected(int x, int y)
12606 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12607 KillPlayer(PLAYERINFO(x, y));
12610 void BuryPlayer(struct PlayerInfo *player)
12612 int jx = player->jx, jy = player->jy;
12614 if (!player->active)
12617 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12618 PlayLevelSound(jx, jy, SND_GAME_LOSING);
12620 player->GameOver = TRUE;
12621 RemovePlayer(player);
12624 void RemovePlayer(struct PlayerInfo *player)
12626 int jx = player->jx, jy = player->jy;
12627 int i, found = FALSE;
12629 player->present = FALSE;
12630 player->active = FALSE;
12632 if (!ExplodeField[jx][jy])
12633 StorePlayer[jx][jy] = 0;
12635 if (player->is_moving)
12636 DrawLevelField(player->last_jx, player->last_jy);
12638 for (i = 0; i < MAX_PLAYERS; i++)
12639 if (stored_player[i].active)
12643 AllPlayersGone = TRUE;
12649 #if USE_NEW_SNAP_DELAY
12650 static void setFieldForSnapping(int x, int y, int element, int direction)
12652 struct ElementInfo *ei = &element_info[element];
12653 int direction_bit = MV_DIR_TO_BIT(direction);
12654 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12655 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12656 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12658 Feld[x][y] = EL_ELEMENT_SNAPPING;
12659 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12661 ResetGfxAnimation(x, y);
12663 GfxElement[x][y] = element;
12664 GfxAction[x][y] = action;
12665 GfxDir[x][y] = direction;
12666 GfxFrame[x][y] = -1;
12671 =============================================================================
12672 checkDiagonalPushing()
12673 -----------------------------------------------------------------------------
12674 check if diagonal input device direction results in pushing of object
12675 (by checking if the alternative direction is walkable, diggable, ...)
12676 =============================================================================
12679 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12680 int x, int y, int real_dx, int real_dy)
12682 int jx, jy, dx, dy, xx, yy;
12684 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
12687 /* diagonal direction: check alternative direction */
12692 xx = jx + (dx == 0 ? real_dx : 0);
12693 yy = jy + (dy == 0 ? real_dy : 0);
12695 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12699 =============================================================================
12701 -----------------------------------------------------------------------------
12702 x, y: field next to player (non-diagonal) to try to dig to
12703 real_dx, real_dy: direction as read from input device (can be diagonal)
12704 =============================================================================
12707 int DigField(struct PlayerInfo *player,
12708 int oldx, int oldy, int x, int y,
12709 int real_dx, int real_dy, int mode)
12711 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12712 boolean player_was_pushing = player->is_pushing;
12713 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12714 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12715 int jx = oldx, jy = oldy;
12716 int dx = x - jx, dy = y - jy;
12717 int nextx = x + dx, nexty = y + dy;
12718 int move_direction = (dx == -1 ? MV_LEFT :
12719 dx == +1 ? MV_RIGHT :
12721 dy == +1 ? MV_DOWN : MV_NONE);
12722 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12723 int dig_side = MV_DIR_OPPOSITE(move_direction);
12724 int old_element = Feld[jx][jy];
12725 #if USE_FIXED_DONT_RUN_INTO
12726 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12732 if (is_player) /* function can also be called by EL_PENGUIN */
12734 if (player->MovPos == 0)
12736 player->is_digging = FALSE;
12737 player->is_collecting = FALSE;
12740 if (player->MovPos == 0) /* last pushing move finished */
12741 player->is_pushing = FALSE;
12743 if (mode == DF_NO_PUSH) /* player just stopped pushing */
12745 player->is_switching = FALSE;
12746 player->push_delay = -1;
12748 return MP_NO_ACTION;
12752 #if !USE_FIXED_DONT_RUN_INTO
12753 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12754 return MP_NO_ACTION;
12757 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12758 old_element = Back[jx][jy];
12760 /* in case of element dropped at player position, check background */
12761 else if (Back[jx][jy] != EL_EMPTY &&
12762 game.engine_version >= VERSION_IDENT(2,2,0,0))
12763 old_element = Back[jx][jy];
12765 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12766 return MP_NO_ACTION; /* field has no opening in this direction */
12768 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12769 return MP_NO_ACTION; /* field has no opening in this direction */
12771 #if USE_FIXED_DONT_RUN_INTO
12772 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12776 Feld[jx][jy] = player->artwork_element;
12777 InitMovingField(jx, jy, MV_DOWN);
12778 Store[jx][jy] = EL_ACID;
12779 ContinueMoving(jx, jy);
12780 BuryPlayer(player);
12782 return MP_DONT_RUN_INTO;
12786 #if USE_FIXED_DONT_RUN_INTO
12787 if (player_can_move && DONT_RUN_INTO(element))
12789 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12791 return MP_DONT_RUN_INTO;
12795 #if USE_FIXED_DONT_RUN_INTO
12796 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12797 return MP_NO_ACTION;
12800 #if !USE_FIXED_DONT_RUN_INTO
12801 element = Feld[x][y];
12804 collect_count = element_info[element].collect_count_initial;
12806 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12807 return MP_NO_ACTION;
12809 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12810 player_can_move = player_can_move_or_snap;
12812 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12813 game.engine_version >= VERSION_IDENT(2,2,0,0))
12815 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12816 player->index_bit, dig_side);
12817 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12818 player->index_bit, dig_side);
12820 if (element == EL_DC_LANDMINE)
12823 if (Feld[x][y] != element) /* field changed by snapping */
12826 return MP_NO_ACTION;
12829 #if USE_PLAYER_GRAVITY
12830 if (player->gravity && is_player && !player->is_auto_moving &&
12831 canFallDown(player) && move_direction != MV_DOWN &&
12832 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12833 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12835 if (game.gravity && is_player && !player->is_auto_moving &&
12836 canFallDown(player) && move_direction != MV_DOWN &&
12837 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12838 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12841 if (player_can_move &&
12842 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12844 int sound_element = SND_ELEMENT(element);
12845 int sound_action = ACTION_WALKING;
12847 if (IS_RND_GATE(element))
12849 if (!player->key[RND_GATE_NR(element)])
12850 return MP_NO_ACTION;
12852 else if (IS_RND_GATE_GRAY(element))
12854 if (!player->key[RND_GATE_GRAY_NR(element)])
12855 return MP_NO_ACTION;
12857 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12859 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12860 return MP_NO_ACTION;
12862 else if (element == EL_EXIT_OPEN ||
12863 element == EL_EM_EXIT_OPEN ||
12864 element == EL_STEEL_EXIT_OPEN ||
12865 element == EL_EM_STEEL_EXIT_OPEN ||
12866 element == EL_SP_EXIT_OPEN ||
12867 element == EL_SP_EXIT_OPENING)
12869 sound_action = ACTION_PASSING; /* player is passing exit */
12871 else if (element == EL_EMPTY)
12873 sound_action = ACTION_MOVING; /* nothing to walk on */
12876 /* play sound from background or player, whatever is available */
12877 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12878 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12880 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12882 else if (player_can_move &&
12883 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12885 if (!ACCESS_FROM(element, opposite_direction))
12886 return MP_NO_ACTION; /* field not accessible from this direction */
12888 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12889 return MP_NO_ACTION;
12891 if (IS_EM_GATE(element))
12893 if (!player->key[EM_GATE_NR(element)])
12894 return MP_NO_ACTION;
12896 else if (IS_EM_GATE_GRAY(element))
12898 if (!player->key[EM_GATE_GRAY_NR(element)])
12899 return MP_NO_ACTION;
12901 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12903 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12904 return MP_NO_ACTION;
12906 else if (IS_EMC_GATE(element))
12908 if (!player->key[EMC_GATE_NR(element)])
12909 return MP_NO_ACTION;
12911 else if (IS_EMC_GATE_GRAY(element))
12913 if (!player->key[EMC_GATE_GRAY_NR(element)])
12914 return MP_NO_ACTION;
12916 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12918 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12919 return MP_NO_ACTION;
12921 else if (element == EL_DC_GATE_WHITE ||
12922 element == EL_DC_GATE_WHITE_GRAY ||
12923 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12925 if (player->num_white_keys == 0)
12926 return MP_NO_ACTION;
12928 player->num_white_keys--;
12930 else if (IS_SP_PORT(element))
12932 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12933 element == EL_SP_GRAVITY_PORT_RIGHT ||
12934 element == EL_SP_GRAVITY_PORT_UP ||
12935 element == EL_SP_GRAVITY_PORT_DOWN)
12936 #if USE_PLAYER_GRAVITY
12937 player->gravity = !player->gravity;
12939 game.gravity = !game.gravity;
12941 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12942 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12943 element == EL_SP_GRAVITY_ON_PORT_UP ||
12944 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12945 #if USE_PLAYER_GRAVITY
12946 player->gravity = TRUE;
12948 game.gravity = TRUE;
12950 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12951 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12952 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12953 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12954 #if USE_PLAYER_GRAVITY
12955 player->gravity = FALSE;
12957 game.gravity = FALSE;
12961 /* automatically move to the next field with double speed */
12962 player->programmed_action = move_direction;
12964 if (player->move_delay_reset_counter == 0)
12966 player->move_delay_reset_counter = 2; /* two double speed steps */
12968 DOUBLE_PLAYER_SPEED(player);
12971 PlayLevelSoundAction(x, y, ACTION_PASSING);
12973 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12977 if (mode != DF_SNAP)
12979 GfxElement[x][y] = GFX_ELEMENT(element);
12980 player->is_digging = TRUE;
12983 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12985 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12986 player->index_bit, dig_side);
12988 if (mode == DF_SNAP)
12990 #if USE_NEW_SNAP_DELAY
12991 if (level.block_snap_field)
12992 setFieldForSnapping(x, y, element, move_direction);
12994 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12996 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12999 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13000 player->index_bit, dig_side);
13003 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13007 if (is_player && mode != DF_SNAP)
13009 GfxElement[x][y] = element;
13010 player->is_collecting = TRUE;
13013 if (element == EL_SPEED_PILL)
13015 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13017 else if (element == EL_EXTRA_TIME && level.time > 0)
13019 TimeLeft += level.extra_time;
13020 DrawGameValue_Time(TimeLeft);
13022 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13024 player->shield_normal_time_left += level.shield_normal_time;
13025 if (element == EL_SHIELD_DEADLY)
13026 player->shield_deadly_time_left += level.shield_deadly_time;
13028 else if (element == EL_DYNAMITE ||
13029 element == EL_EM_DYNAMITE ||
13030 element == EL_SP_DISK_RED)
13032 if (player->inventory_size < MAX_INVENTORY_SIZE)
13033 player->inventory_element[player->inventory_size++] = element;
13035 DrawGameDoorValues();
13037 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13039 player->dynabomb_count++;
13040 player->dynabombs_left++;
13042 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13044 player->dynabomb_size++;
13046 else if (element == EL_DYNABOMB_INCREASE_POWER)
13048 player->dynabomb_xl = TRUE;
13050 else if (IS_KEY(element))
13052 player->key[KEY_NR(element)] = TRUE;
13054 DrawGameDoorValues();
13056 else if (element == EL_DC_KEY_WHITE)
13058 player->num_white_keys++;
13060 /* display white keys? */
13061 /* DrawGameDoorValues(); */
13063 else if (IS_ENVELOPE(element))
13065 player->show_envelope = element;
13067 else if (element == EL_EMC_LENSES)
13069 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13071 RedrawAllInvisibleElementsForLenses();
13073 else if (element == EL_EMC_MAGNIFIER)
13075 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13077 RedrawAllInvisibleElementsForMagnifier();
13079 else if (IS_DROPPABLE(element) ||
13080 IS_THROWABLE(element)) /* can be collected and dropped */
13084 if (collect_count == 0)
13085 player->inventory_infinite_element = element;
13087 for (i = 0; i < collect_count; i++)
13088 if (player->inventory_size < MAX_INVENTORY_SIZE)
13089 player->inventory_element[player->inventory_size++] = element;
13091 DrawGameDoorValues();
13093 else if (collect_count > 0)
13095 local_player->gems_still_needed -= collect_count;
13096 if (local_player->gems_still_needed < 0)
13097 local_player->gems_still_needed = 0;
13099 DrawGameValue_Emeralds(local_player->gems_still_needed);
13102 RaiseScoreElement(element);
13103 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13106 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13107 player->index_bit, dig_side);
13109 if (mode == DF_SNAP)
13111 #if USE_NEW_SNAP_DELAY
13112 if (level.block_snap_field)
13113 setFieldForSnapping(x, y, element, move_direction);
13115 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13117 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13120 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13121 player->index_bit, dig_side);
13124 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13126 if (mode == DF_SNAP && element != EL_BD_ROCK)
13127 return MP_NO_ACTION;
13129 if (CAN_FALL(element) && dy)
13130 return MP_NO_ACTION;
13132 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13133 !(element == EL_SPRING && level.use_spring_bug))
13134 return MP_NO_ACTION;
13136 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13137 ((move_direction & MV_VERTICAL &&
13138 ((element_info[element].move_pattern & MV_LEFT &&
13139 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13140 (element_info[element].move_pattern & MV_RIGHT &&
13141 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13142 (move_direction & MV_HORIZONTAL &&
13143 ((element_info[element].move_pattern & MV_UP &&
13144 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13145 (element_info[element].move_pattern & MV_DOWN &&
13146 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13147 return MP_NO_ACTION;
13149 /* do not push elements already moving away faster than player */
13150 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13151 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13152 return MP_NO_ACTION;
13154 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13156 if (player->push_delay_value == -1 || !player_was_pushing)
13157 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13159 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13161 if (player->push_delay_value == -1)
13162 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13164 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13166 if (!player->is_pushing)
13167 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13170 player->is_pushing = TRUE;
13171 player->is_active = TRUE;
13173 if (!(IN_LEV_FIELD(nextx, nexty) &&
13174 (IS_FREE(nextx, nexty) ||
13175 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13176 IS_SB_ELEMENT(element)))))
13177 return MP_NO_ACTION;
13179 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13180 return MP_NO_ACTION;
13182 if (player->push_delay == -1) /* new pushing; restart delay */
13183 player->push_delay = 0;
13185 if (player->push_delay < player->push_delay_value &&
13186 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13187 element != EL_SPRING && element != EL_BALLOON)
13189 /* make sure that there is no move delay before next try to push */
13190 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13191 player->move_delay = 0;
13193 return MP_NO_ACTION;
13196 if (IS_SB_ELEMENT(element))
13198 if (element == EL_SOKOBAN_FIELD_FULL)
13200 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13201 local_player->sokobanfields_still_needed++;
13204 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13206 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13207 local_player->sokobanfields_still_needed--;
13210 Feld[x][y] = EL_SOKOBAN_OBJECT;
13212 if (Back[x][y] == Back[nextx][nexty])
13213 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13214 else if (Back[x][y] != 0)
13215 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13218 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13221 if (local_player->sokobanfields_still_needed == 0 &&
13222 game.emulation == EMU_SOKOBAN)
13224 PlayerWins(player);
13226 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13230 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13232 InitMovingField(x, y, move_direction);
13233 GfxAction[x][y] = ACTION_PUSHING;
13235 if (mode == DF_SNAP)
13236 ContinueMoving(x, y);
13238 MovPos[x][y] = (dx != 0 ? dx : dy);
13240 Pushed[x][y] = TRUE;
13241 Pushed[nextx][nexty] = TRUE;
13243 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13244 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13246 player->push_delay_value = -1; /* get new value later */
13248 /* check for element change _after_ element has been pushed */
13249 if (game.use_change_when_pushing_bug)
13251 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13252 player->index_bit, dig_side);
13253 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13254 player->index_bit, dig_side);
13257 else if (IS_SWITCHABLE(element))
13259 if (PLAYER_SWITCHING(player, x, y))
13261 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13262 player->index_bit, dig_side);
13267 player->is_switching = TRUE;
13268 player->switch_x = x;
13269 player->switch_y = y;
13271 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13273 if (element == EL_ROBOT_WHEEL)
13275 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13279 DrawLevelField(x, y);
13281 else if (element == EL_SP_TERMINAL)
13285 SCAN_PLAYFIELD(xx, yy)
13287 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13289 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13290 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13293 else if (IS_BELT_SWITCH(element))
13295 ToggleBeltSwitch(x, y);
13297 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13298 element == EL_SWITCHGATE_SWITCH_DOWN ||
13299 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13300 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13302 ToggleSwitchgateSwitch(x, y);
13304 else if (element == EL_LIGHT_SWITCH ||
13305 element == EL_LIGHT_SWITCH_ACTIVE)
13307 ToggleLightSwitch(x, y);
13309 else if (element == EL_TIMEGATE_SWITCH ||
13310 element == EL_DC_TIMEGATE_SWITCH)
13312 ActivateTimegateSwitch(x, y);
13314 else if (element == EL_BALLOON_SWITCH_LEFT ||
13315 element == EL_BALLOON_SWITCH_RIGHT ||
13316 element == EL_BALLOON_SWITCH_UP ||
13317 element == EL_BALLOON_SWITCH_DOWN ||
13318 element == EL_BALLOON_SWITCH_NONE ||
13319 element == EL_BALLOON_SWITCH_ANY)
13321 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13322 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13323 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13324 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13325 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13328 else if (element == EL_LAMP)
13330 Feld[x][y] = EL_LAMP_ACTIVE;
13331 local_player->lights_still_needed--;
13333 ResetGfxAnimation(x, y);
13334 DrawLevelField(x, y);
13336 else if (element == EL_TIME_ORB_FULL)
13338 Feld[x][y] = EL_TIME_ORB_EMPTY;
13340 if (level.time > 0 || level.use_time_orb_bug)
13342 TimeLeft += level.time_orb_time;
13343 DrawGameValue_Time(TimeLeft);
13346 ResetGfxAnimation(x, y);
13347 DrawLevelField(x, y);
13349 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13350 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13354 game.ball_state = !game.ball_state;
13356 SCAN_PLAYFIELD(xx, yy)
13358 int e = Feld[xx][yy];
13360 if (game.ball_state)
13362 if (e == EL_EMC_MAGIC_BALL)
13363 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13364 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13365 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13369 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13370 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13371 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13372 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13377 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13378 player->index_bit, dig_side);
13380 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13381 player->index_bit, dig_side);
13383 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13384 player->index_bit, dig_side);
13390 if (!PLAYER_SWITCHING(player, x, y))
13392 player->is_switching = TRUE;
13393 player->switch_x = x;
13394 player->switch_y = y;
13396 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13397 player->index_bit, dig_side);
13398 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13399 player->index_bit, dig_side);
13401 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13402 player->index_bit, dig_side);
13403 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13404 player->index_bit, dig_side);
13407 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13408 player->index_bit, dig_side);
13409 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13410 player->index_bit, dig_side);
13412 return MP_NO_ACTION;
13415 player->push_delay = -1;
13417 if (is_player) /* function can also be called by EL_PENGUIN */
13419 if (Feld[x][y] != element) /* really digged/collected something */
13421 player->is_collecting = !player->is_digging;
13422 player->is_active = TRUE;
13429 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13431 int jx = player->jx, jy = player->jy;
13432 int x = jx + dx, y = jy + dy;
13433 int snap_direction = (dx == -1 ? MV_LEFT :
13434 dx == +1 ? MV_RIGHT :
13436 dy == +1 ? MV_DOWN : MV_NONE);
13437 boolean can_continue_snapping = (level.continuous_snapping &&
13438 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13440 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13443 if (!player->active || !IN_LEV_FIELD(x, y))
13451 if (player->MovPos == 0)
13452 player->is_pushing = FALSE;
13454 player->is_snapping = FALSE;
13456 if (player->MovPos == 0)
13458 player->is_moving = FALSE;
13459 player->is_digging = FALSE;
13460 player->is_collecting = FALSE;
13466 #if USE_NEW_CONTINUOUS_SNAPPING
13467 /* prevent snapping with already pressed snap key when not allowed */
13468 if (player->is_snapping && !can_continue_snapping)
13471 if (player->is_snapping)
13475 player->MovDir = snap_direction;
13477 if (player->MovPos == 0)
13479 player->is_moving = FALSE;
13480 player->is_digging = FALSE;
13481 player->is_collecting = FALSE;
13484 player->is_dropping = FALSE;
13485 player->is_dropping_pressed = FALSE;
13486 player->drop_pressed_delay = 0;
13488 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13491 player->is_snapping = TRUE;
13492 player->is_active = TRUE;
13494 if (player->MovPos == 0)
13496 player->is_moving = FALSE;
13497 player->is_digging = FALSE;
13498 player->is_collecting = FALSE;
13501 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13502 DrawLevelField(player->last_jx, player->last_jy);
13504 DrawLevelField(x, y);
13509 boolean DropElement(struct PlayerInfo *player)
13511 int old_element, new_element;
13512 int dropx = player->jx, dropy = player->jy;
13513 int drop_direction = player->MovDir;
13514 int drop_side = drop_direction;
13515 int drop_element = (player->inventory_size > 0 ?
13516 player->inventory_element[player->inventory_size - 1] :
13517 player->inventory_infinite_element != EL_UNDEFINED ?
13518 player->inventory_infinite_element :
13519 player->dynabombs_left > 0 ?
13520 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13523 player->is_dropping_pressed = TRUE;
13525 /* do not drop an element on top of another element; when holding drop key
13526 pressed without moving, dropped element must move away before the next
13527 element can be dropped (this is especially important if the next element
13528 is dynamite, which can be placed on background for historical reasons) */
13529 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13532 if (IS_THROWABLE(drop_element))
13534 dropx += GET_DX_FROM_DIR(drop_direction);
13535 dropy += GET_DY_FROM_DIR(drop_direction);
13537 if (!IN_LEV_FIELD(dropx, dropy))
13541 old_element = Feld[dropx][dropy]; /* old element at dropping position */
13542 new_element = drop_element; /* default: no change when dropping */
13544 /* check if player is active, not moving and ready to drop */
13545 if (!player->active || player->MovPos || player->drop_delay > 0)
13548 /* check if player has anything that can be dropped */
13549 if (new_element == EL_UNDEFINED)
13552 /* check if drop key was pressed long enough for EM style dynamite */
13553 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13556 /* check if anything can be dropped at the current position */
13557 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13560 /* collected custom elements can only be dropped on empty fields */
13561 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13564 if (old_element != EL_EMPTY)
13565 Back[dropx][dropy] = old_element; /* store old element on this field */
13567 ResetGfxAnimation(dropx, dropy);
13568 ResetRandomAnimationValue(dropx, dropy);
13570 if (player->inventory_size > 0 ||
13571 player->inventory_infinite_element != EL_UNDEFINED)
13573 if (player->inventory_size > 0)
13575 player->inventory_size--;
13577 DrawGameDoorValues();
13579 if (new_element == EL_DYNAMITE)
13580 new_element = EL_DYNAMITE_ACTIVE;
13581 else if (new_element == EL_EM_DYNAMITE)
13582 new_element = EL_EM_DYNAMITE_ACTIVE;
13583 else if (new_element == EL_SP_DISK_RED)
13584 new_element = EL_SP_DISK_RED_ACTIVE;
13587 Feld[dropx][dropy] = new_element;
13589 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13590 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13591 el2img(Feld[dropx][dropy]), 0);
13593 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13595 /* needed if previous element just changed to "empty" in the last frame */
13596 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13598 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13599 player->index_bit, drop_side);
13600 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13602 player->index_bit, drop_side);
13604 TestIfElementTouchesCustomElement(dropx, dropy);
13606 else /* player is dropping a dyna bomb */
13608 player->dynabombs_left--;
13610 Feld[dropx][dropy] = new_element;
13612 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13613 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13614 el2img(Feld[dropx][dropy]), 0);
13616 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13619 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13620 InitField_WithBug1(dropx, dropy, FALSE);
13622 new_element = Feld[dropx][dropy]; /* element might have changed */
13624 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13625 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13627 int move_direction, nextx, nexty;
13629 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13630 MovDir[dropx][dropy] = drop_direction;
13632 move_direction = MovDir[dropx][dropy];
13633 nextx = dropx + GET_DX_FROM_DIR(move_direction);
13634 nexty = dropy + GET_DY_FROM_DIR(move_direction);
13636 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13638 #if USE_FIX_IMPACT_COLLISION
13639 /* do not cause impact style collision by dropping elements that can fall */
13640 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13642 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13646 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13647 player->is_dropping = TRUE;
13649 player->drop_pressed_delay = 0;
13650 player->is_dropping_pressed = FALSE;
13652 player->drop_x = dropx;
13653 player->drop_y = dropy;
13658 /* ------------------------------------------------------------------------- */
13659 /* game sound playing functions */
13660 /* ------------------------------------------------------------------------- */
13662 static int *loop_sound_frame = NULL;
13663 static int *loop_sound_volume = NULL;
13665 void InitPlayLevelSound()
13667 int num_sounds = getSoundListSize();
13669 checked_free(loop_sound_frame);
13670 checked_free(loop_sound_volume);
13672 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
13673 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13676 static void PlayLevelSound(int x, int y, int nr)
13678 int sx = SCREENX(x), sy = SCREENY(y);
13679 int volume, stereo_position;
13680 int max_distance = 8;
13681 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13683 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13684 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13687 if (!IN_LEV_FIELD(x, y) ||
13688 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13689 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13692 volume = SOUND_MAX_VOLUME;
13694 if (!IN_SCR_FIELD(sx, sy))
13696 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13697 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13699 volume -= volume * (dx > dy ? dx : dy) / max_distance;
13702 stereo_position = (SOUND_MAX_LEFT +
13703 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13704 (SCR_FIELDX + 2 * max_distance));
13706 if (IS_LOOP_SOUND(nr))
13708 /* This assures that quieter loop sounds do not overwrite louder ones,
13709 while restarting sound volume comparison with each new game frame. */
13711 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13714 loop_sound_volume[nr] = volume;
13715 loop_sound_frame[nr] = FrameCounter;
13718 PlaySoundExt(nr, volume, stereo_position, type);
13721 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13723 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13724 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13725 y < LEVELY(BY1) ? LEVELY(BY1) :
13726 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13730 static void PlayLevelSoundAction(int x, int y, int action)
13732 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13735 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13737 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13739 if (sound_effect != SND_UNDEFINED)
13740 PlayLevelSound(x, y, sound_effect);
13743 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13746 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13748 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13749 PlayLevelSound(x, y, sound_effect);
13752 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13754 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13756 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13757 PlayLevelSound(x, y, sound_effect);
13760 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13762 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13764 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13765 StopSound(sound_effect);
13768 static void PlayLevelMusic()
13770 if (levelset.music[level_nr] != MUS_UNDEFINED)
13771 PlayMusic(levelset.music[level_nr]); /* from config file */
13773 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13776 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13778 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13779 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13780 int x = xx - 1 - offset;
13781 int y = yy - 1 - offset;
13786 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13790 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13794 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13798 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13802 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13806 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13810 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13813 case SAMPLE_android_clone:
13814 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13817 case SAMPLE_android_move:
13818 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13821 case SAMPLE_spring:
13822 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13826 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13830 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13833 case SAMPLE_eater_eat:
13834 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13838 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13841 case SAMPLE_collect:
13842 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13845 case SAMPLE_diamond:
13846 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13849 case SAMPLE_squash:
13850 /* !!! CHECK THIS !!! */
13852 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13854 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13858 case SAMPLE_wonderfall:
13859 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13863 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13867 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13871 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13875 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13879 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13883 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13886 case SAMPLE_wonder:
13887 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13891 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13894 case SAMPLE_exit_open:
13895 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13898 case SAMPLE_exit_leave:
13899 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13902 case SAMPLE_dynamite:
13903 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13907 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13911 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13915 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13919 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13923 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13927 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13931 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13937 void ChangeTime(int value)
13939 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13943 /* EMC game engine uses value from time counter of RND game engine */
13944 level.native_em_level->lev->time = *time;
13946 DrawGameValue_Time(*time);
13949 void RaiseScore(int value)
13951 /* EMC game engine and RND game engine have separate score counters */
13952 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13953 &level.native_em_level->lev->score : &local_player->score);
13957 DrawGameValue_Score(*score);
13961 void RaiseScore(int value)
13963 local_player->score += value;
13965 DrawGameValue_Score(local_player->score);
13968 void RaiseScoreElement(int element)
13973 case EL_BD_DIAMOND:
13974 case EL_EMERALD_YELLOW:
13975 case EL_EMERALD_RED:
13976 case EL_EMERALD_PURPLE:
13977 case EL_SP_INFOTRON:
13978 RaiseScore(level.score[SC_EMERALD]);
13981 RaiseScore(level.score[SC_DIAMOND]);
13984 RaiseScore(level.score[SC_CRYSTAL]);
13987 RaiseScore(level.score[SC_PEARL]);
13990 case EL_BD_BUTTERFLY:
13991 case EL_SP_ELECTRON:
13992 RaiseScore(level.score[SC_BUG]);
13995 case EL_BD_FIREFLY:
13996 case EL_SP_SNIKSNAK:
13997 RaiseScore(level.score[SC_SPACESHIP]);
14000 case EL_DARK_YAMYAM:
14001 RaiseScore(level.score[SC_YAMYAM]);
14004 RaiseScore(level.score[SC_ROBOT]);
14007 RaiseScore(level.score[SC_PACMAN]);
14010 RaiseScore(level.score[SC_NUT]);
14013 case EL_EM_DYNAMITE:
14014 case EL_SP_DISK_RED:
14015 case EL_DYNABOMB_INCREASE_NUMBER:
14016 case EL_DYNABOMB_INCREASE_SIZE:
14017 case EL_DYNABOMB_INCREASE_POWER:
14018 RaiseScore(level.score[SC_DYNAMITE]);
14020 case EL_SHIELD_NORMAL:
14021 case EL_SHIELD_DEADLY:
14022 RaiseScore(level.score[SC_SHIELD]);
14024 case EL_EXTRA_TIME:
14025 RaiseScore(level.extra_time_score);
14039 case EL_DC_KEY_WHITE:
14040 RaiseScore(level.score[SC_KEY]);
14043 RaiseScore(element_info[element].collect_score);
14048 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14050 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14052 #if defined(NETWORK_AVALIABLE)
14053 if (options.network)
14054 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14060 game_status = GAME_MODE_MAIN;
14066 FadeOut(REDRAW_FIELD);
14068 game_status = GAME_MODE_MAIN;
14070 DrawAndFadeInMainMenu(REDRAW_FIELD);
14074 else /* continue playing the game */
14076 if (tape.playing && tape.deactivate_display)
14077 TapeDeactivateDisplayOff(TRUE);
14079 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14081 if (tape.playing && tape.deactivate_display)
14082 TapeDeactivateDisplayOn();
14086 void RequestQuitGame(boolean ask_if_really_quit)
14088 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14089 boolean skip_request = AllPlayersGone || quick_quit;
14091 RequestQuitGameExt(skip_request, quick_quit,
14092 "Do you really want to quit the game ?");
14096 /* ------------------------------------------------------------------------- */
14097 /* random generator functions */
14098 /* ------------------------------------------------------------------------- */
14100 unsigned int InitEngineRandom_RND(long seed)
14102 game.num_random_calls = 0;
14105 unsigned int rnd_seed = InitEngineRandom(seed);
14107 printf("::: START RND: %d\n", rnd_seed);
14112 return InitEngineRandom(seed);
14118 unsigned int RND(int max)
14122 game.num_random_calls++;
14124 return GetEngineRandom(max);
14131 /* ------------------------------------------------------------------------- */
14132 /* game engine snapshot handling functions */
14133 /* ------------------------------------------------------------------------- */
14135 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14137 struct EngineSnapshotInfo
14139 /* runtime values for custom element collect score */
14140 int collect_score[NUM_CUSTOM_ELEMENTS];
14142 /* runtime values for group element choice position */
14143 int choice_pos[NUM_GROUP_ELEMENTS];
14145 /* runtime values for belt position animations */
14146 int belt_graphic[4 * NUM_BELT_PARTS];
14147 int belt_anim_mode[4 * NUM_BELT_PARTS];
14150 struct EngineSnapshotNodeInfo
14157 static struct EngineSnapshotInfo engine_snapshot_rnd;
14158 static ListNode *engine_snapshot_list = NULL;
14159 static char *snapshot_level_identifier = NULL;
14160 static int snapshot_level_nr = -1;
14162 void FreeEngineSnapshot()
14164 while (engine_snapshot_list != NULL)
14165 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14168 setString(&snapshot_level_identifier, NULL);
14169 snapshot_level_nr = -1;
14172 static void SaveEngineSnapshotValues_RND()
14174 static int belt_base_active_element[4] =
14176 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14177 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14178 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14179 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14183 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14185 int element = EL_CUSTOM_START + i;
14187 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14190 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14192 int element = EL_GROUP_START + i;
14194 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14197 for (i = 0; i < 4; i++)
14199 for (j = 0; j < NUM_BELT_PARTS; j++)
14201 int element = belt_base_active_element[i] + j;
14202 int graphic = el2img(element);
14203 int anim_mode = graphic_info[graphic].anim_mode;
14205 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14206 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14211 static void LoadEngineSnapshotValues_RND()
14213 unsigned long num_random_calls = game.num_random_calls;
14216 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14218 int element = EL_CUSTOM_START + i;
14220 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14223 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14225 int element = EL_GROUP_START + i;
14227 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14230 for (i = 0; i < 4; i++)
14232 for (j = 0; j < NUM_BELT_PARTS; j++)
14234 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14235 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14237 graphic_info[graphic].anim_mode = anim_mode;
14241 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14243 InitRND(tape.random_seed);
14244 for (i = 0; i < num_random_calls; i++)
14248 if (game.num_random_calls != num_random_calls)
14250 Error(ERR_INFO, "number of random calls out of sync");
14251 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14252 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14253 Error(ERR_EXIT, "this should not happen -- please debug");
14257 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14259 struct EngineSnapshotNodeInfo *bi =
14260 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14262 bi->buffer_orig = buffer;
14263 bi->buffer_copy = checked_malloc(size);
14266 memcpy(bi->buffer_copy, buffer, size);
14268 addNodeToList(&engine_snapshot_list, NULL, bi);
14271 void SaveEngineSnapshot()
14273 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14275 if (level_editor_test_game) /* do not save snapshots from editor */
14278 /* copy some special values to a structure better suited for the snapshot */
14280 SaveEngineSnapshotValues_RND();
14281 SaveEngineSnapshotValues_EM();
14283 /* save values stored in special snapshot structure */
14285 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14286 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14288 /* save further RND engine values */
14290 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14291 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14292 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14294 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14295 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14296 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14297 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14299 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14300 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14301 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14302 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14303 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14305 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14306 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14307 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14309 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14311 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14313 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14314 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14316 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14317 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14318 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14319 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14320 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14321 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14322 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14323 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14324 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14325 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14326 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14327 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14328 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14329 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14330 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14331 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14332 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14333 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14335 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14336 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14338 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14339 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14340 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14342 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14343 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14345 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14346 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14347 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14348 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14349 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14351 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14352 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14354 /* save level identification information */
14356 setString(&snapshot_level_identifier, leveldir_current->identifier);
14357 snapshot_level_nr = level_nr;
14360 ListNode *node = engine_snapshot_list;
14363 while (node != NULL)
14365 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14370 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14374 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14376 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14379 void LoadEngineSnapshot()
14381 ListNode *node = engine_snapshot_list;
14383 if (engine_snapshot_list == NULL)
14386 while (node != NULL)
14388 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14393 /* restore special values from snapshot structure */
14395 LoadEngineSnapshotValues_RND();
14396 LoadEngineSnapshotValues_EM();
14399 boolean CheckEngineSnapshot()
14401 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14402 snapshot_level_nr == level_nr);
14406 /* ---------- new game button stuff ---------------------------------------- */
14408 /* graphic position values for game buttons */
14409 #define GAME_BUTTON_XSIZE 30
14410 #define GAME_BUTTON_YSIZE 30
14411 #define GAME_BUTTON_XPOS 5
14412 #define GAME_BUTTON_YPOS 215
14413 #define SOUND_BUTTON_XPOS 5
14414 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14416 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14417 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14418 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14419 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14420 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14421 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14428 } gamebutton_info[NUM_GAME_BUTTONS] =
14431 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14436 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14437 GAME_CTRL_ID_PAUSE,
14441 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14446 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14447 SOUND_CTRL_ID_MUSIC,
14448 "background music on/off"
14451 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
14452 SOUND_CTRL_ID_LOOPS,
14453 "sound loops on/off"
14456 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
14457 SOUND_CTRL_ID_SIMPLE,
14458 "normal sounds on/off"
14462 void CreateGameButtons()
14466 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14468 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14469 struct GadgetInfo *gi;
14472 unsigned long event_mask;
14473 int gd_xoffset, gd_yoffset;
14474 int gd_x1, gd_x2, gd_y1, gd_y2;
14477 gd_xoffset = gamebutton_info[i].x;
14478 gd_yoffset = gamebutton_info[i].y;
14479 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14480 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14482 if (id == GAME_CTRL_ID_STOP ||
14483 id == GAME_CTRL_ID_PAUSE ||
14484 id == GAME_CTRL_ID_PLAY)
14486 button_type = GD_TYPE_NORMAL_BUTTON;
14488 event_mask = GD_EVENT_RELEASED;
14489 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14490 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14494 button_type = GD_TYPE_CHECK_BUTTON;
14496 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14497 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14498 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14499 event_mask = GD_EVENT_PRESSED;
14500 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
14501 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14504 gi = CreateGadget(GDI_CUSTOM_ID, id,
14505 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14506 GDI_X, DX + gd_xoffset,
14507 GDI_Y, DY + gd_yoffset,
14508 GDI_WIDTH, GAME_BUTTON_XSIZE,
14509 GDI_HEIGHT, GAME_BUTTON_YSIZE,
14510 GDI_TYPE, button_type,
14511 GDI_STATE, GD_BUTTON_UNPRESSED,
14512 GDI_CHECKED, checked,
14513 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14514 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14515 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14516 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14517 GDI_EVENT_MASK, event_mask,
14518 GDI_CALLBACK_ACTION, HandleGameButtons,
14522 Error(ERR_EXIT, "cannot create gadget");
14524 game_gadget[id] = gi;
14528 void FreeGameButtons()
14532 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14533 FreeGadget(game_gadget[i]);
14536 static void MapGameButtons()
14540 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14541 MapGadget(game_gadget[i]);
14544 void UnmapGameButtons()
14548 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14549 UnmapGadget(game_gadget[i]);
14552 static void HandleGameButtons(struct GadgetInfo *gi)
14554 int id = gi->custom_id;
14556 if (game_status != GAME_MODE_PLAYING)
14561 case GAME_CTRL_ID_STOP:
14565 RequestQuitGame(TRUE);
14568 case GAME_CTRL_ID_PAUSE:
14569 if (options.network)
14571 #if defined(NETWORK_AVALIABLE)
14573 SendToServer_ContinuePlaying();
14575 SendToServer_PausePlaying();
14579 TapeTogglePause(TAPE_TOGGLE_MANUAL);
14582 case GAME_CTRL_ID_PLAY:
14585 #if defined(NETWORK_AVALIABLE)
14586 if (options.network)
14587 SendToServer_ContinuePlaying();
14591 tape.pausing = FALSE;
14592 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14597 case SOUND_CTRL_ID_MUSIC:
14598 if (setup.sound_music)
14600 setup.sound_music = FALSE;
14603 else if (audio.music_available)
14605 setup.sound = setup.sound_music = TRUE;
14607 SetAudioMode(setup.sound);
14613 case SOUND_CTRL_ID_LOOPS:
14614 if (setup.sound_loops)
14615 setup.sound_loops = FALSE;
14616 else if (audio.loops_available)
14618 setup.sound = setup.sound_loops = TRUE;
14619 SetAudioMode(setup.sound);
14623 case SOUND_CTRL_ID_SIMPLE:
14624 if (setup.sound_simple)
14625 setup.sound_simple = FALSE;
14626 else if (audio.sound_available)
14628 setup.sound = setup.sound_simple = TRUE;
14629 SetAudioMode(setup.sound);