1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
90 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME (PANEL_XPOS(game.panel.time))
111 #define YY_TIME (PANEL_YPOS(game.panel.time))
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1 (DX + XX_LEVEL1)
115 #define DX_LEVEL2 (DX + XX_LEVEL2)
116 #define DX_LEVEL (DX + XX_LEVEL)
117 #define DY_LEVEL (DY + YY_LEVEL)
118 #define DX_EMERALDS (DX + XX_EMERALDS)
119 #define DY_EMERALDS (DY + YY_EMERALDS)
120 #define DX_DYNAMITE (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE (DY + YY_DYNAMITE)
122 #define DX_KEYS (DX + XX_KEYS)
123 #define DY_KEYS (DY + YY_KEYS)
124 #define DX_SCORE (DX + XX_SCORE)
125 #define DY_SCORE (DY + YY_SCORE)
126 #define DX_TIME1 (DX + XX_TIME1)
127 #define DX_TIME2 (DX + XX_TIME2)
128 #define DX_TIME (DX + XX_TIME)
129 #define DY_TIME (DY + YY_TIME)
132 /* game panel display and control definitions */
134 #define GAME_CONTROL_LEVEL 0
135 #define GAME_CONTROL_GEMS 1
136 #define GAME_CONTROL_INVENTORY 2
137 #define GAME_CONTROL_KEY_1 3
138 #define GAME_CONTROL_KEY_2 4
139 #define GAME_CONTROL_KEY_3 5
140 #define GAME_CONTROL_KEY_4 6
141 #define GAME_CONTROL_KEY_5 7
142 #define GAME_CONTROL_KEY_6 8
143 #define GAME_CONTROL_KEY_7 9
144 #define GAME_CONTROL_KEY_8 10
145 #define GAME_CONTROL_KEY_WHITE 11
146 #define GAME_CONTROL_KEY_WHITE_COUNT 12
147 #define GAME_CONTROL_SCORE 13
148 #define GAME_CONTROL_TIME 14
149 #define GAME_CONTROL_TIME_HH 15
150 #define GAME_CONTROL_TIME_MM 16
151 #define GAME_CONTROL_TIME_SS 17
152 #define GAME_CONTROL_DROP_NEXT_1 18
153 #define GAME_CONTROL_DROP_NEXT_2 19
154 #define GAME_CONTROL_DROP_NEXT_3 20
155 #define GAME_CONTROL_DROP_NEXT_4 21
156 #define GAME_CONTROL_DROP_NEXT_5 22
157 #define GAME_CONTROL_DROP_NEXT_6 23
158 #define GAME_CONTROL_DROP_NEXT_7 24
159 #define GAME_CONTROL_DROP_NEXT_8 25
160 #define GAME_CONTROL_SHIELD_NORMAL 26
161 #define GAME_CONTROL_SHIELD_NORMAL_TIME 27
162 #define GAME_CONTROL_SHIELD_DEADLY 28
163 #define GAME_CONTROL_SHIELD_DEADLY_TIME 29
164 #define GAME_CONTROL_EXIT 30
165 #define GAME_CONTROL_EM_EXIT 31
166 #define GAME_CONTROL_SP_EXIT 32
167 #define GAME_CONTROL_STEEL_EXIT 33
168 #define GAME_CONTROL_EM_STEEL_EXIT 34
169 #define GAME_CONTROL_EMC_MAGIC_BALL 35
170 #define GAME_CONTROL_EMC_MAGIC_BALL_TIME 36
171 #define GAME_CONTROL_LIGHT_SWITCH 37
172 #define GAME_CONTROL_LIGHT_SWITCH_TIME 38
173 #define GAME_CONTROL_TIMEGATE_SWITCH 39
174 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME 40
175 #define GAME_CONTROL_SWITCHGATE_SWITCH 41
176 #define GAME_CONTROL_EMC_LENSES 42
177 #define GAME_CONTROL_EMC_LENSES_TIME 43
178 #define GAME_CONTROL_EMC_MAGNIFIER 44
179 #define GAME_CONTROL_EMC_MAGNIFIER_TIME 45
180 #define GAME_CONTROL_BALLOON_SWITCH 46
181 #define GAME_CONTROL_DYNABOMB_NUMBER 47
182 #define GAME_CONTROL_DYNABOMB_SIZE 48
183 #define GAME_CONTROL_DYNABOMB_POWER 49
184 #define GAME_CONTROL_PENGUINS 50
185 #define GAME_CONTROL_SOKOBAN_OBJECTS 51
186 #define GAME_CONTROL_SOKOBAN_FIELDS 52
187 #define GAME_CONTROL_ROBOT_WHEEL 53
188 #define GAME_CONTROL_CONVEYOR_BELT_1 54
189 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH 55
190 #define GAME_CONTROL_CONVEYOR_BELT_2 56
191 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH 57
192 #define GAME_CONTROL_CONVEYOR_BELT_3 58
193 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH 59
194 #define GAME_CONTROL_CONVEYOR_BELT_4 60
195 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH 61
196 #define GAME_CONTROL_MAGIC_WALL 62
197 #define GAME_CONTROL_MAGIC_WALL_TIME 63
198 #define GAME_CONTROL_BD_MAGIC_WALL 64
199 #define GAME_CONTROL_DC_MAGIC_WALL 65
200 #define GAME_CONTROL_PLAYER_NAME 66
201 #define GAME_CONTROL_LEVEL_NAME 67
202 #define GAME_CONTROL_LEVEL_AUTHOR 68
204 struct GameControlInfo
208 struct TextPosInfo *pos_text;
213 static struct GameControlInfo game_controls[] =
226 GAME_CONTROL_INVENTORY,
227 &game.panel.inventory,
246 GAME_CONTROL_TIME_HH,
251 GAME_CONTROL_TIME_MM,
256 GAME_CONTROL_TIME_SS,
261 GAME_CONTROL_DROP_NEXT_1,
262 &game.panel.drop_next_1,
266 GAME_CONTROL_DROP_NEXT_2,
267 &game.panel.drop_next_2,
271 GAME_CONTROL_DROP_NEXT_3,
272 &game.panel.drop_next_3,
276 GAME_CONTROL_DROP_NEXT_4,
277 &game.panel.drop_next_4,
281 GAME_CONTROL_DROP_NEXT_5,
282 &game.panel.drop_next_5,
286 GAME_CONTROL_DROP_NEXT_6,
287 &game.panel.drop_next_6,
291 GAME_CONTROL_DROP_NEXT_7,
292 &game.panel.drop_next_7,
296 GAME_CONTROL_DROP_NEXT_8,
297 &game.panel.drop_next_8,
301 GAME_CONTROL_EMC_KEYS,
302 &game.panel.emc_keys,
346 GAME_CONTROL_KEY_WHITE,
347 &game.panel.key_white,
351 GAME_CONTROL_KEY_WHITE_COUNT,
352 &game.panel.key_white_count,
356 GAME_CONTROL_SHIELD_NORMAL,
357 &game.panel.shield_normal,
361 GAME_CONTROL_SHIELD_NORMAL_TIME,
362 &game.panel.shield_normal_time,
366 GAME_CONTROL_SHIELD_DEADLY,
367 &game.panel.shield_deadly,
371 GAME_CONTROL_SHIELD_DEADLY_TIME,
372 &game.panel.shield_deadly_time,
381 GAME_CONTROL_EM_EXIT,
386 GAME_CONTROL_SP_EXIT,
391 GAME_CONTROL_STEEL_EXIT,
392 &game.panel.steel_exit,
396 GAME_CONTROL_EM_STEEL_EXIT,
397 &game.panel.em_steel_exit,
401 GAME_CONTROL_EMC_MAGIC_BALL,
402 &game.panel.emc_magic_ball,
406 GAME_CONTROL_EMC_MAGIC_BALL_TIME,
407 &game.panel.emc_magic_ball_time,
411 GAME_CONTROL_LIGHT_SWITCH,
412 &game.panel.light_switch,
416 GAME_CONTROL_LIGHT_SWITCH_TIME,
417 &game.panel.light_switch_time,
421 GAME_CONTROL_TIMEGATE_SWITCH,
422 &game.panel.timegate_switch,
426 GAME_CONTROL_TIMEGATE_SWITCH_TIME,
427 &game.panel.timegate_switch_time,
431 GAME_CONTROL_SWITCHGATE_SWITCH,
432 &game.panel.switchgate_switch,
436 GAME_CONTROL_EMC_LENSES,
437 &game.panel.emc_lenses,
441 GAME_CONTROL_EMC_LENSES_TIME,
442 &game.panel.emc_lenses_time,
446 GAME_CONTROL_EMC_MAGNIFIER,
447 &game.panel.emc_magnifier,
451 GAME_CONTROL_EMC_MAGNIFIER_TIME,
452 &game.panel.emc_magnifier_time,
456 GAME_CONTROL_BALLOON_SWITCH,
457 &game.panel.balloon_switch,
461 GAME_CONTROL_DYNABOMB_NUMBER,
462 &game.panel.dynabomb_number,
466 GAME_CONTROL_DYNABOMB_SIZE,
467 &game.panel.dynabomb_size,
471 GAME_CONTROL_DYNABOMB_POWER,
472 &game.panel.dynabomb_power,
476 GAME_CONTROL_PENGUINS,
477 &game.panel.penguins,
481 GAME_CONTROL_SOKOBAN_OBJECTS,
482 &game.panel.sokoban_objects,
486 GAME_CONTROL_SOKOBAN_FIELDS,
487 &game.panel.sokoban_fields,
491 GAME_CONTROL_ROBOT_WHEEL,
492 &game.panel.robot_wheel,
496 GAME_CONTROL_CONVEYOR_BELT_1,
497 &game.panel.conveyor_belt_1,
501 GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
502 &game.panel.conveyor_belt_1_switch,
506 GAME_CONTROL_CONVEYOR_BELT_2,
507 &game.panel.conveyor_belt_2,
511 GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
512 &game.panel.conveyor_belt_2_switch,
516 GAME_CONTROL_CONVEYOR_BELT_3,
517 &game.panel.conveyor_belt_3,
521 GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
522 &game.panel.conveyor_belt_3_switch,
526 GAME_CONTROL_CONVEYOR_BELT_4,
527 &game.panel.conveyor_belt_4,
531 GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
532 &game.panel.conveyor_belt_4_switch,
536 GAME_CONTROL_MAGIC_WALL,
537 &game.panel.magic_wall,
541 GAME_CONTROL_MAGIC_WALL_TIME,
542 &game.panel.magic_wall_time,
546 GAME_CONTROL_BD_MAGIC_WALL,
547 &game.panel.bd_magic_wall,
551 GAME_CONTROL_DC_MAGIC_WALL,
552 &game.panel.dc_magic_wall,
556 GAME_CONTROL_PLAYER_NAME,
557 &game.panel.player_name,
561 GAME_CONTROL_LEVEL_NAME,
562 &game.panel.level_name,
566 GAME_CONTROL_LEVEL_AUTHOR,
567 &game.panel.level_author,
581 /* values for delayed check of falling and moving elements and for collision */
582 #define CHECK_DELAY_MOVING 3
583 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
584 #define CHECK_DELAY_COLLISION 2
585 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
587 /* values for initial player move delay (initial delay counter value) */
588 #define INITIAL_MOVE_DELAY_OFF -1
589 #define INITIAL_MOVE_DELAY_ON 0
591 /* values for player movement speed (which is in fact a delay value) */
592 #define MOVE_DELAY_MIN_SPEED 32
593 #define MOVE_DELAY_NORMAL_SPEED 8
594 #define MOVE_DELAY_HIGH_SPEED 4
595 #define MOVE_DELAY_MAX_SPEED 1
597 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
598 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
600 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
601 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
603 /* values for other actions */
604 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
605 #define MOVE_STEPSIZE_MIN (1)
606 #define MOVE_STEPSIZE_MAX (TILEX)
608 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
609 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
611 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
613 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
614 RND(element_info[e].push_delay_random))
615 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
616 RND(element_info[e].drop_delay_random))
617 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
618 RND(element_info[e].move_delay_random))
619 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
620 (element_info[e].move_delay_random))
621 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
622 RND(element_info[e].ce_value_random_initial))
623 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
624 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
625 RND((c)->delay_random * (c)->delay_frames))
626 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
627 RND((c)->delay_random))
630 #define GET_VALID_RUNTIME_ELEMENT(e) \
631 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
633 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
634 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
635 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
636 (be) + (e) - EL_SELF)
638 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
639 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
640 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
641 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
642 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
643 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
644 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
645 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
646 RESOLVED_REFERENCE_ELEMENT(be, e) : \
649 #define CAN_GROW_INTO(e) \
650 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
652 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
653 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
656 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
657 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
658 (CAN_MOVE_INTO_ACID(e) && \
659 Feld[x][y] == EL_ACID) || \
662 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
663 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
664 (CAN_MOVE_INTO_ACID(e) && \
665 Feld[x][y] == EL_ACID) || \
668 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
669 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
671 (CAN_MOVE_INTO_ACID(e) && \
672 Feld[x][y] == EL_ACID) || \
673 (DONT_COLLIDE_WITH(e) && \
675 !PLAYER_ENEMY_PROTECTED(x, y))))
677 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
678 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
680 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
681 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
683 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
684 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
686 #define ANDROID_CAN_CLONE_FIELD(x, y) \
687 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
688 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
690 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
691 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
693 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
694 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
696 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
697 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
699 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
700 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
702 #define PIG_CAN_ENTER_FIELD(e, x, y) \
703 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
705 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
706 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
707 Feld[x][y] == EL_EM_EXIT_OPEN || \
708 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
709 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
710 IS_FOOD_PENGUIN(Feld[x][y])))
711 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
712 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
714 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
715 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
717 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
718 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
720 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
721 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
722 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
724 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
726 #define CE_ENTER_FIELD_COND(e, x, y) \
727 (!IS_PLAYER(x, y) && \
728 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
730 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
731 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
733 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
734 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
736 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
737 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
738 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
739 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
741 /* game button identifiers */
742 #define GAME_CTRL_ID_STOP 0
743 #define GAME_CTRL_ID_PAUSE 1
744 #define GAME_CTRL_ID_PLAY 2
745 #define SOUND_CTRL_ID_MUSIC 3
746 #define SOUND_CTRL_ID_LOOPS 4
747 #define SOUND_CTRL_ID_SIMPLE 5
749 #define NUM_GAME_BUTTONS 6
752 /* forward declaration for internal use */
754 static void CreateField(int, int, int);
756 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
757 static void AdvanceFrameAndPlayerCounters(int);
759 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
760 static boolean MovePlayer(struct PlayerInfo *, int, int);
761 static void ScrollPlayer(struct PlayerInfo *, int);
762 static void ScrollScreen(struct PlayerInfo *, int);
764 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
766 static void InitBeltMovement(void);
767 static void CloseAllOpenTimegates(void);
768 static void CheckGravityMovement(struct PlayerInfo *);
769 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
770 static void KillPlayerUnlessEnemyProtected(int, int);
771 static void KillPlayerUnlessExplosionProtected(int, int);
773 static void TestIfPlayerTouchesCustomElement(int, int);
774 static void TestIfElementTouchesCustomElement(int, int);
775 static void TestIfElementHitsCustomElement(int, int, int);
777 static void TestIfElementSmashesCustomElement(int, int, int);
780 static void HandleElementChange(int, int, int);
781 static void ExecuteCustomElementAction(int, int, int, int);
782 static boolean ChangeElement(int, int, int, int);
784 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
785 #define CheckTriggeredElementChange(x, y, e, ev) \
786 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
787 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
788 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
789 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
790 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
791 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
792 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
794 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
795 #define CheckElementChange(x, y, e, te, ev) \
796 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
797 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
798 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
799 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
800 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
802 static void PlayLevelSound(int, int, int);
803 static void PlayLevelSoundNearest(int, int, int);
804 static void PlayLevelSoundAction(int, int, int);
805 static void PlayLevelSoundElementAction(int, int, int, int);
806 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
807 static void PlayLevelSoundActionIfLoop(int, int, int);
808 static void StopLevelSoundActionIfLoop(int, int, int);
809 static void PlayLevelMusic();
811 static void MapGameButtons();
812 static void HandleGameButtons(struct GadgetInfo *);
814 int AmoebeNachbarNr(int, int);
815 void AmoebeUmwandeln(int, int);
816 void ContinueMoving(int, int);
818 void InitMovDir(int, int);
819 void InitAmoebaNr(int, int);
820 int NewHiScore(void);
822 void TestIfGoodThingHitsBadThing(int, int, int);
823 void TestIfBadThingHitsGoodThing(int, int, int);
824 void TestIfPlayerTouchesBadThing(int, int);
825 void TestIfPlayerRunsIntoBadThing(int, int, int);
826 void TestIfBadThingTouchesPlayer(int, int);
827 void TestIfBadThingRunsIntoPlayer(int, int, int);
828 void TestIfFriendTouchesBadThing(int, int);
829 void TestIfBadThingTouchesFriend(int, int);
830 void TestIfBadThingTouchesOtherBadThing(int, int);
832 void KillPlayer(struct PlayerInfo *);
833 void BuryPlayer(struct PlayerInfo *);
834 void RemovePlayer(struct PlayerInfo *);
836 boolean SnapField(struct PlayerInfo *, int, int);
837 boolean DropElement(struct PlayerInfo *);
839 static int getInvisibleActiveFromInvisibleElement(int);
840 static int getInvisibleFromInvisibleActiveElement(int);
842 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
844 /* for detection of endless loops, caused by custom element programming */
845 /* (using maximal playfield width x 10 is just a rough approximation) */
846 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
848 #define RECURSION_LOOP_DETECTION_START(e, rc) \
850 if (recursion_loop_detected) \
853 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
855 recursion_loop_detected = TRUE; \
856 recursion_loop_element = (e); \
859 recursion_loop_depth++; \
862 #define RECURSION_LOOP_DETECTION_END() \
864 recursion_loop_depth--; \
867 static int recursion_loop_depth;
868 static boolean recursion_loop_detected;
869 static boolean recursion_loop_element;
872 /* ------------------------------------------------------------------------- */
873 /* definition of elements that automatically change to other elements after */
874 /* a specified time, eventually calling a function when changing */
875 /* ------------------------------------------------------------------------- */
877 /* forward declaration for changer functions */
878 static void InitBuggyBase(int, int);
879 static void WarnBuggyBase(int, int);
881 static void InitTrap(int, int);
882 static void ActivateTrap(int, int);
883 static void ChangeActiveTrap(int, int);
885 static void InitRobotWheel(int, int);
886 static void RunRobotWheel(int, int);
887 static void StopRobotWheel(int, int);
889 static void InitTimegateWheel(int, int);
890 static void RunTimegateWheel(int, int);
892 static void InitMagicBallDelay(int, int);
893 static void ActivateMagicBall(int, int);
895 struct ChangingElementInfo
900 void (*pre_change_function)(int x, int y);
901 void (*change_function)(int x, int y);
902 void (*post_change_function)(int x, int y);
905 static struct ChangingElementInfo change_delay_list[] =
940 EL_STEEL_EXIT_OPENING,
948 EL_STEEL_EXIT_CLOSING,
949 EL_STEEL_EXIT_CLOSED,
976 EL_EM_STEEL_EXIT_OPENING,
977 EL_EM_STEEL_EXIT_OPEN,
984 EL_EM_STEEL_EXIT_CLOSING,
988 EL_EM_STEEL_EXIT_CLOSED,
1012 EL_SWITCHGATE_OPENING,
1020 EL_SWITCHGATE_CLOSING,
1021 EL_SWITCHGATE_CLOSED,
1028 EL_TIMEGATE_OPENING,
1036 EL_TIMEGATE_CLOSING,
1045 EL_ACID_SPLASH_LEFT,
1053 EL_ACID_SPLASH_RIGHT,
1062 EL_SP_BUGGY_BASE_ACTIVATING,
1069 EL_SP_BUGGY_BASE_ACTIVATING,
1070 EL_SP_BUGGY_BASE_ACTIVE,
1077 EL_SP_BUGGY_BASE_ACTIVE,
1101 EL_ROBOT_WHEEL_ACTIVE,
1109 EL_TIMEGATE_SWITCH_ACTIVE,
1117 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1118 EL_DC_TIMEGATE_SWITCH,
1125 EL_EMC_MAGIC_BALL_ACTIVE,
1126 EL_EMC_MAGIC_BALL_ACTIVE,
1133 EL_EMC_SPRING_BUMPER_ACTIVE,
1134 EL_EMC_SPRING_BUMPER,
1141 EL_DIAGONAL_SHRINKING,
1149 EL_DIAGONAL_GROWING,
1170 int push_delay_fixed, push_delay_random;
1174 { EL_SPRING, 0, 0 },
1175 { EL_BALLOON, 0, 0 },
1177 { EL_SOKOBAN_OBJECT, 2, 0 },
1178 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1179 { EL_SATELLITE, 2, 0 },
1180 { EL_SP_DISK_YELLOW, 2, 0 },
1182 { EL_UNDEFINED, 0, 0 },
1190 move_stepsize_list[] =
1192 { EL_AMOEBA_DROP, 2 },
1193 { EL_AMOEBA_DROPPING, 2 },
1194 { EL_QUICKSAND_FILLING, 1 },
1195 { EL_QUICKSAND_EMPTYING, 1 },
1196 { EL_QUICKSAND_FAST_FILLING, 2 },
1197 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1198 { EL_MAGIC_WALL_FILLING, 2 },
1199 { EL_MAGIC_WALL_EMPTYING, 2 },
1200 { EL_BD_MAGIC_WALL_FILLING, 2 },
1201 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1202 { EL_DC_MAGIC_WALL_FILLING, 2 },
1203 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1205 { EL_UNDEFINED, 0 },
1213 collect_count_list[] =
1216 { EL_BD_DIAMOND, 1 },
1217 { EL_EMERALD_YELLOW, 1 },
1218 { EL_EMERALD_RED, 1 },
1219 { EL_EMERALD_PURPLE, 1 },
1221 { EL_SP_INFOTRON, 1 },
1225 { EL_UNDEFINED, 0 },
1233 access_direction_list[] =
1235 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1236 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1237 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1238 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1239 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1240 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1241 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1242 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1243 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1244 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1245 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1247 { EL_SP_PORT_LEFT, MV_RIGHT },
1248 { EL_SP_PORT_RIGHT, MV_LEFT },
1249 { EL_SP_PORT_UP, MV_DOWN },
1250 { EL_SP_PORT_DOWN, MV_UP },
1251 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1252 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1253 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1254 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1255 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1256 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1257 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1258 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1259 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1260 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1261 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1262 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1263 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1264 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1265 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1267 { EL_UNDEFINED, MV_NONE }
1270 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1272 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1273 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1274 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1275 IS_JUST_CHANGING(x, y))
1277 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1279 /* static variables for playfield scan mode (scanning forward or backward) */
1280 static int playfield_scan_start_x = 0;
1281 static int playfield_scan_start_y = 0;
1282 static int playfield_scan_delta_x = 1;
1283 static int playfield_scan_delta_y = 1;
1285 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1286 (y) >= 0 && (y) <= lev_fieldy - 1; \
1287 (y) += playfield_scan_delta_y) \
1288 for ((x) = playfield_scan_start_x; \
1289 (x) >= 0 && (x) <= lev_fieldx - 1; \
1290 (x) += playfield_scan_delta_x)
1293 void DEBUG_SetMaximumDynamite()
1297 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1298 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1299 local_player->inventory_element[local_player->inventory_size++] =
1304 static void InitPlayfieldScanModeVars()
1306 if (game.use_reverse_scan_direction)
1308 playfield_scan_start_x = lev_fieldx - 1;
1309 playfield_scan_start_y = lev_fieldy - 1;
1311 playfield_scan_delta_x = -1;
1312 playfield_scan_delta_y = -1;
1316 playfield_scan_start_x = 0;
1317 playfield_scan_start_y = 0;
1319 playfield_scan_delta_x = 1;
1320 playfield_scan_delta_y = 1;
1324 static void InitPlayfieldScanMode(int mode)
1326 game.use_reverse_scan_direction =
1327 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1329 InitPlayfieldScanModeVars();
1332 static int get_move_delay_from_stepsize(int move_stepsize)
1335 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1337 /* make sure that stepsize value is always a power of 2 */
1338 move_stepsize = (1 << log_2(move_stepsize));
1340 return TILEX / move_stepsize;
1343 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1346 int player_nr = player->index_nr;
1347 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1348 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1350 /* do no immediately change move delay -- the player might just be moving */
1351 player->move_delay_value_next = move_delay;
1353 /* information if player can move must be set separately */
1354 player->cannot_move = cannot_move;
1358 player->move_delay = game.initial_move_delay[player_nr];
1359 player->move_delay_value = game.initial_move_delay_value[player_nr];
1361 player->move_delay_value_next = -1;
1363 player->move_delay_reset_counter = 0;
1367 void GetPlayerConfig()
1369 GameFrameDelay = setup.game_frame_delay;
1371 if (!audio.sound_available)
1372 setup.sound_simple = FALSE;
1374 if (!audio.loops_available)
1375 setup.sound_loops = FALSE;
1377 if (!audio.music_available)
1378 setup.sound_music = FALSE;
1380 if (!video.fullscreen_available)
1381 setup.fullscreen = FALSE;
1383 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1385 SetAudioMode(setup.sound);
1389 int GetElementFromGroupElement(int element)
1391 if (IS_GROUP_ELEMENT(element))
1393 struct ElementGroupInfo *group = element_info[element].group;
1394 int last_anim_random_frame = gfx.anim_random_frame;
1397 if (group->choice_mode == ANIM_RANDOM)
1398 gfx.anim_random_frame = RND(group->num_elements_resolved);
1400 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1401 group->choice_mode, 0,
1404 if (group->choice_mode == ANIM_RANDOM)
1405 gfx.anim_random_frame = last_anim_random_frame;
1407 group->choice_pos++;
1409 element = group->element_resolved[element_pos];
1415 static void InitPlayerField(int x, int y, int element, boolean init_game)
1417 if (element == EL_SP_MURPHY)
1421 if (stored_player[0].present)
1423 Feld[x][y] = EL_SP_MURPHY_CLONE;
1429 stored_player[0].use_murphy = TRUE;
1431 if (!level.use_artwork_element[0])
1432 stored_player[0].artwork_element = EL_SP_MURPHY;
1435 Feld[x][y] = EL_PLAYER_1;
1441 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1442 int jx = player->jx, jy = player->jy;
1444 player->present = TRUE;
1446 player->block_last_field = (element == EL_SP_MURPHY ?
1447 level.sp_block_last_field :
1448 level.block_last_field);
1450 /* ---------- initialize player's last field block delay --------------- */
1452 /* always start with reliable default value (no adjustment needed) */
1453 player->block_delay_adjustment = 0;
1455 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1456 if (player->block_last_field && element == EL_SP_MURPHY)
1457 player->block_delay_adjustment = 1;
1459 /* special case 2: in game engines before 3.1.1, blocking was different */
1460 if (game.use_block_last_field_bug)
1461 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1463 if (!options.network || player->connected)
1465 player->active = TRUE;
1467 /* remove potentially duplicate players */
1468 if (StorePlayer[jx][jy] == Feld[x][y])
1469 StorePlayer[jx][jy] = 0;
1471 StorePlayer[x][y] = Feld[x][y];
1475 printf("Player %d activated.\n", player->element_nr);
1476 printf("[Local player is %d and currently %s.]\n",
1477 local_player->element_nr,
1478 local_player->active ? "active" : "not active");
1482 Feld[x][y] = EL_EMPTY;
1484 player->jx = player->last_jx = x;
1485 player->jy = player->last_jy = y;
1489 static void InitField(int x, int y, boolean init_game)
1491 int element = Feld[x][y];
1500 InitPlayerField(x, y, element, init_game);
1503 case EL_SOKOBAN_FIELD_PLAYER:
1504 element = Feld[x][y] = EL_PLAYER_1;
1505 InitField(x, y, init_game);
1507 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1508 InitField(x, y, init_game);
1511 case EL_SOKOBAN_FIELD_EMPTY:
1512 local_player->sokobanfields_still_needed++;
1516 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1517 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1518 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1519 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1520 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1521 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1522 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1523 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1524 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1525 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1534 case EL_SPACESHIP_RIGHT:
1535 case EL_SPACESHIP_UP:
1536 case EL_SPACESHIP_LEFT:
1537 case EL_SPACESHIP_DOWN:
1538 case EL_BD_BUTTERFLY:
1539 case EL_BD_BUTTERFLY_RIGHT:
1540 case EL_BD_BUTTERFLY_UP:
1541 case EL_BD_BUTTERFLY_LEFT:
1542 case EL_BD_BUTTERFLY_DOWN:
1544 case EL_BD_FIREFLY_RIGHT:
1545 case EL_BD_FIREFLY_UP:
1546 case EL_BD_FIREFLY_LEFT:
1547 case EL_BD_FIREFLY_DOWN:
1548 case EL_PACMAN_RIGHT:
1550 case EL_PACMAN_LEFT:
1551 case EL_PACMAN_DOWN:
1553 case EL_YAMYAM_LEFT:
1554 case EL_YAMYAM_RIGHT:
1556 case EL_YAMYAM_DOWN:
1557 case EL_DARK_YAMYAM:
1560 case EL_SP_SNIKSNAK:
1561 case EL_SP_ELECTRON:
1570 case EL_AMOEBA_FULL:
1575 case EL_AMOEBA_DROP:
1576 if (y == lev_fieldy - 1)
1578 Feld[x][y] = EL_AMOEBA_GROWING;
1579 Store[x][y] = EL_AMOEBA_WET;
1583 case EL_DYNAMITE_ACTIVE:
1584 case EL_SP_DISK_RED_ACTIVE:
1585 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1586 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1587 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1588 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1589 MovDelay[x][y] = 96;
1592 case EL_EM_DYNAMITE_ACTIVE:
1593 MovDelay[x][y] = 32;
1597 local_player->lights_still_needed++;
1601 local_player->friends_still_needed++;
1606 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1609 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1610 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1611 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1612 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1613 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1614 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1615 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1616 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1617 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1618 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1619 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1620 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1623 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1624 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1625 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1627 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1629 game.belt_dir[belt_nr] = belt_dir;
1630 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1632 else /* more than one switch -- set it like the first switch */
1634 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1639 #if !USE_BOTH_SWITCHGATE_SWITCHES
1640 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1642 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1645 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1647 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1651 case EL_LIGHT_SWITCH_ACTIVE:
1653 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1656 case EL_INVISIBLE_STEELWALL:
1657 case EL_INVISIBLE_WALL:
1658 case EL_INVISIBLE_SAND:
1659 if (game.light_time_left > 0 ||
1660 game.lenses_time_left > 0)
1661 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1664 case EL_EMC_MAGIC_BALL:
1665 if (game.ball_state)
1666 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1669 case EL_EMC_MAGIC_BALL_SWITCH:
1670 if (game.ball_state)
1671 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1675 if (IS_CUSTOM_ELEMENT(element))
1677 if (CAN_MOVE(element))
1680 #if USE_NEW_CUSTOM_VALUE
1681 if (!element_info[element].use_last_ce_value || init_game)
1682 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1685 else if (IS_GROUP_ELEMENT(element))
1687 Feld[x][y] = GetElementFromGroupElement(element);
1689 InitField(x, y, init_game);
1696 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1699 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1701 InitField(x, y, init_game);
1703 /* not needed to call InitMovDir() -- already done by InitField()! */
1704 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1705 CAN_MOVE(Feld[x][y]))
1709 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1711 int old_element = Feld[x][y];
1713 InitField(x, y, init_game);
1715 /* not needed to call InitMovDir() -- already done by InitField()! */
1716 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1717 CAN_MOVE(old_element) &&
1718 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1721 /* this case is in fact a combination of not less than three bugs:
1722 first, it calls InitMovDir() for elements that can move, although this is
1723 already done by InitField(); then, it checks the element that was at this
1724 field _before_ the call to InitField() (which can change it); lastly, it
1725 was not called for "mole with direction" elements, which were treated as
1726 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1732 void DrawGameValue_Emeralds(int value)
1734 struct TextPosInfo *pos = &game.panel.gems;
1736 int font_nr = pos->font;
1738 int font_nr = FONT_TEXT_2;
1740 int font_width = getFontWidth(font_nr);
1741 int chars = pos->chars;
1743 if (PANEL_DEACTIVATED(pos))
1746 pos->width = chars * font_width;
1748 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1751 void DrawGameValue_Dynamite(int value)
1753 struct TextPosInfo *pos = &game.panel.inventory;
1755 int font_nr = pos->font;
1757 int font_nr = FONT_TEXT_2;
1759 int font_width = getFontWidth(font_nr);
1760 int chars = pos->chars;
1762 if (PANEL_DEACTIVATED(pos))
1765 pos->width = chars * font_width;
1767 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1770 void DrawGameValue_Score(int value)
1772 struct TextPosInfo *pos = &game.panel.score;
1774 int font_nr = pos->font;
1776 int font_nr = FONT_TEXT_2;
1778 int font_width = getFontWidth(font_nr);
1779 int chars = pos->chars;
1781 if (PANEL_DEACTIVATED(pos))
1784 pos->width = chars * font_width;
1786 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1789 void DrawGameValue_Time(int value)
1791 struct TextPosInfo *pos = &game.panel.time;
1792 static int last_value = -1;
1795 int chars = pos->chars;
1797 int font1_nr = pos->font;
1798 int font2_nr = pos->font_alt;
1800 int font1_nr = FONT_TEXT_2;
1801 int font2_nr = FONT_TEXT_1;
1803 int font_nr = font1_nr;
1804 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1806 if (PANEL_DEACTIVATED(pos))
1809 if (use_dynamic_chars) /* use dynamic number of chars */
1811 chars = (value < 1000 ? chars1 : chars2);
1812 font_nr = (value < 1000 ? font1_nr : font2_nr);
1815 /* clear background if value just changed its size (dynamic chars only) */
1816 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
1818 int width1 = chars1 * getFontWidth(font1_nr);
1819 int width2 = chars2 * getFontWidth(font2_nr);
1820 int max_width = MAX(width1, width2);
1821 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1823 pos->width = max_width;
1825 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1826 max_width, max_height);
1829 pos->width = chars * getFontWidth(font_nr);
1831 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1836 void DrawGameValue_Level(int value)
1838 struct TextPosInfo *pos = &game.panel.level_number;
1841 int chars = pos->chars;
1843 int font1_nr = pos->font;
1844 int font2_nr = pos->font_alt;
1846 int font1_nr = FONT_TEXT_2;
1847 int font2_nr = FONT_TEXT_1;
1849 int font_nr = font1_nr;
1850 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1852 if (PANEL_DEACTIVATED(pos))
1855 if (use_dynamic_chars) /* use dynamic number of chars */
1857 chars = (level_nr < 100 ? chars1 : chars2);
1858 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1861 pos->width = chars * getFontWidth(font_nr);
1863 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1866 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1869 struct TextPosInfo *pos = &game.panel.keys;
1872 int base_key_graphic = EL_KEY_1;
1877 if (PANEL_DEACTIVATED(pos))
1882 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1883 base_key_graphic = EL_EM_KEY_1;
1887 pos->width = 4 * MINI_TILEX;
1891 for (i = 0; i < MAX_NUM_KEYS; i++)
1893 /* currently only 4 of 8 possible keys are displayed */
1894 for (i = 0; i < STD_NUM_KEYS; i++)
1898 struct TextPosInfo *pos = &game.panel.key[i];
1900 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
1901 int src_y = DOOR_GFX_PAGEY1 + 123;
1903 int dst_x = PANEL_XPOS(pos);
1904 int dst_y = PANEL_YPOS(pos);
1906 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1907 int dst_y = PANEL_YPOS(pos);
1911 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
1912 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
1914 int graphic = el2edimg(element);
1918 if (PANEL_DEACTIVATED(pos))
1923 /* masked blit with tiles from half-size scaled bitmap does not work yet
1924 (no mask bitmap created for these sizes after loading and scaling) --
1925 solution: load without creating mask, scale, then create final mask */
1927 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1928 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1933 int graphic = el2edimg(base_key_graphic + i);
1938 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1940 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1941 dst_x - src_x, dst_y - src_y);
1942 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
1948 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1950 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1951 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1954 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1956 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1957 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1965 void DrawGameValue_Emeralds(int value)
1967 int font_nr = FONT_TEXT_2;
1968 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1970 if (PANEL_DEACTIVATED(game.panel.gems))
1973 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1976 void DrawGameValue_Dynamite(int value)
1978 int font_nr = FONT_TEXT_2;
1979 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1981 if (PANEL_DEACTIVATED(game.panel.inventory))
1984 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1987 void DrawGameValue_Score(int value)
1989 int font_nr = FONT_TEXT_2;
1990 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1992 if (PANEL_DEACTIVATED(game.panel.score))
1995 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1998 void DrawGameValue_Time(int value)
2000 int font1_nr = FONT_TEXT_2;
2002 int font2_nr = FONT_TEXT_1;
2004 int font2_nr = FONT_LEVEL_NUMBER;
2006 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2007 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2009 if (PANEL_DEACTIVATED(game.panel.time))
2012 /* clear background if value just changed its size */
2013 if (value == 999 || value == 1000)
2014 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2017 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2019 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2022 void DrawGameValue_Level(int value)
2024 int font1_nr = FONT_TEXT_2;
2026 int font2_nr = FONT_TEXT_1;
2028 int font2_nr = FONT_LEVEL_NUMBER;
2031 if (PANEL_DEACTIVATED(game.panel.level))
2035 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2037 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2040 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2042 int base_key_graphic = EL_KEY_1;
2045 if (PANEL_DEACTIVATED(game.panel.keys))
2048 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2049 base_key_graphic = EL_EM_KEY_1;
2051 /* currently only 4 of 8 possible keys are displayed */
2052 for (i = 0; i < STD_NUM_KEYS; i++)
2054 int x = XX_KEYS + i * MINI_TILEX;
2058 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2060 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2061 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2067 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2070 int key[MAX_NUM_KEYS];
2073 /* prevent EM engine from updating time/score values parallel to GameWon() */
2074 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2075 local_player->LevelSolved)
2078 for (i = 0; i < MAX_NUM_KEYS; i++)
2079 key[i] = key_bits & (1 << i);
2081 DrawGameValue_Level(level_nr);
2083 DrawGameValue_Emeralds(emeralds);
2084 DrawGameValue_Dynamite(dynamite);
2085 DrawGameValue_Score(score);
2086 DrawGameValue_Time(time);
2088 DrawGameValue_Keys(key);
2091 void DrawGameDoorValues()
2093 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2094 int dynamite_value = 0;
2095 int score_value = (local_player->LevelSolved ? local_player->score_final :
2096 local_player->score);
2097 int gems_value = local_player->gems_still_needed;
2101 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2103 DrawGameDoorValues_EM();
2108 if (game.centered_player_nr == -1)
2110 for (i = 0; i < MAX_PLAYERS; i++)
2112 for (j = 0; j < MAX_NUM_KEYS; j++)
2113 if (stored_player[i].key[j])
2114 key_bits |= (1 << j);
2116 dynamite_value += stored_player[i].inventory_size;
2121 int player_nr = game.centered_player_nr;
2123 for (i = 0; i < MAX_NUM_KEYS; i++)
2124 if (stored_player[player_nr].key[i])
2125 key_bits |= (1 << i);
2127 dynamite_value = stored_player[player_nr].inventory_size;
2130 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2136 =============================================================================
2138 -----------------------------------------------------------------------------
2139 initialize game engine due to level / tape version number
2140 =============================================================================
2143 static void InitGameEngine()
2145 int i, j, k, l, x, y;
2147 /* set game engine from tape file when re-playing, else from level file */
2148 game.engine_version = (tape.playing ? tape.engine_version :
2149 level.game_version);
2151 /* ---------------------------------------------------------------------- */
2152 /* set flags for bugs and changes according to active game engine version */
2153 /* ---------------------------------------------------------------------- */
2156 Summary of bugfix/change:
2157 Fixed handling for custom elements that change when pushed by the player.
2159 Fixed/changed in version:
2163 Before 3.1.0, custom elements that "change when pushing" changed directly
2164 after the player started pushing them (until then handled in "DigField()").
2165 Since 3.1.0, these custom elements are not changed until the "pushing"
2166 move of the element is finished (now handled in "ContinueMoving()").
2168 Affected levels/tapes:
2169 The first condition is generally needed for all levels/tapes before version
2170 3.1.0, which might use the old behaviour before it was changed; known tapes
2171 that are affected are some tapes from the level set "Walpurgis Gardens" by
2173 The second condition is an exception from the above case and is needed for
2174 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2175 above (including some development versions of 3.1.0), but before it was
2176 known that this change would break tapes like the above and was fixed in
2177 3.1.1, so that the changed behaviour was active although the engine version
2178 while recording maybe was before 3.1.0. There is at least one tape that is
2179 affected by this exception, which is the tape for the one-level set "Bug
2180 Machine" by Juergen Bonhagen.
2183 game.use_change_when_pushing_bug =
2184 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2186 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2187 tape.game_version < VERSION_IDENT(3,1,1,0)));
2190 Summary of bugfix/change:
2191 Fixed handling for blocking the field the player leaves when moving.
2193 Fixed/changed in version:
2197 Before 3.1.1, when "block last field when moving" was enabled, the field
2198 the player is leaving when moving was blocked for the time of the move,
2199 and was directly unblocked afterwards. This resulted in the last field
2200 being blocked for exactly one less than the number of frames of one player
2201 move. Additionally, even when blocking was disabled, the last field was
2202 blocked for exactly one frame.
2203 Since 3.1.1, due to changes in player movement handling, the last field
2204 is not blocked at all when blocking is disabled. When blocking is enabled,
2205 the last field is blocked for exactly the number of frames of one player
2206 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2207 last field is blocked for exactly one more than the number of frames of
2210 Affected levels/tapes:
2211 (!!! yet to be determined -- probably many !!!)
2214 game.use_block_last_field_bug =
2215 (game.engine_version < VERSION_IDENT(3,1,1,0));
2218 Summary of bugfix/change:
2219 Changed behaviour of CE changes with multiple changes per single frame.
2221 Fixed/changed in version:
2225 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2226 This resulted in race conditions where CEs seem to behave strange in some
2227 situations (where triggered CE changes were just skipped because there was
2228 already a CE change on that tile in the playfield in that engine frame).
2229 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2230 (The number of changes per frame must be limited in any case, because else
2231 it is easily possible to define CE changes that would result in an infinite
2232 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2233 should be set large enough so that it would only be reached in cases where
2234 the corresponding CE change conditions run into a loop. Therefore, it seems
2235 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2236 maximal number of change pages for custom elements.)
2238 Affected levels/tapes:
2242 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2243 game.max_num_changes_per_frame = 1;
2245 game.max_num_changes_per_frame =
2246 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2249 /* ---------------------------------------------------------------------- */
2251 /* default scan direction: scan playfield from top/left to bottom/right */
2252 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2254 /* dynamically adjust element properties according to game engine version */
2255 InitElementPropertiesEngine(game.engine_version);
2258 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2259 printf(" tape version == %06d [%s] [file: %06d]\n",
2260 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2262 printf(" => game.engine_version == %06d\n", game.engine_version);
2265 /* ---------- initialize player's initial move delay --------------------- */
2267 /* dynamically adjust player properties according to level information */
2268 for (i = 0; i < MAX_PLAYERS; i++)
2269 game.initial_move_delay_value[i] =
2270 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2272 /* dynamically adjust player properties according to game engine version */
2273 for (i = 0; i < MAX_PLAYERS; i++)
2274 game.initial_move_delay[i] =
2275 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2276 game.initial_move_delay_value[i] : 0);
2278 /* ---------- initialize player's initial push delay --------------------- */
2280 /* dynamically adjust player properties according to game engine version */
2281 game.initial_push_delay_value =
2282 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2284 /* ---------- initialize changing elements ------------------------------- */
2286 /* initialize changing elements information */
2287 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2289 struct ElementInfo *ei = &element_info[i];
2291 /* this pointer might have been changed in the level editor */
2292 ei->change = &ei->change_page[0];
2294 if (!IS_CUSTOM_ELEMENT(i))
2296 ei->change->target_element = EL_EMPTY_SPACE;
2297 ei->change->delay_fixed = 0;
2298 ei->change->delay_random = 0;
2299 ei->change->delay_frames = 1;
2302 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2304 ei->has_change_event[j] = FALSE;
2306 ei->event_page_nr[j] = 0;
2307 ei->event_page[j] = &ei->change_page[0];
2311 /* add changing elements from pre-defined list */
2312 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2314 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2315 struct ElementInfo *ei = &element_info[ch_delay->element];
2317 ei->change->target_element = ch_delay->target_element;
2318 ei->change->delay_fixed = ch_delay->change_delay;
2320 ei->change->pre_change_function = ch_delay->pre_change_function;
2321 ei->change->change_function = ch_delay->change_function;
2322 ei->change->post_change_function = ch_delay->post_change_function;
2324 ei->change->can_change = TRUE;
2325 ei->change->can_change_or_has_action = TRUE;
2327 ei->has_change_event[CE_DELAY] = TRUE;
2329 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2330 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2333 /* ---------- initialize internal run-time variables ------------- */
2335 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2337 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2339 for (j = 0; j < ei->num_change_pages; j++)
2341 ei->change_page[j].can_change_or_has_action =
2342 (ei->change_page[j].can_change |
2343 ei->change_page[j].has_action);
2347 /* add change events from custom element configuration */
2348 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2350 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2352 for (j = 0; j < ei->num_change_pages; j++)
2354 if (!ei->change_page[j].can_change_or_has_action)
2357 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2359 /* only add event page for the first page found with this event */
2360 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2362 ei->has_change_event[k] = TRUE;
2364 ei->event_page_nr[k] = j;
2365 ei->event_page[k] = &ei->change_page[j];
2371 /* ---------- initialize run-time trigger player and element ------------- */
2373 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2375 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2377 for (j = 0; j < ei->num_change_pages; j++)
2379 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2380 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2381 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2382 ei->change_page[j].actual_trigger_ce_value = 0;
2383 ei->change_page[j].actual_trigger_ce_score = 0;
2387 /* ---------- initialize trigger events ---------------------------------- */
2389 /* initialize trigger events information */
2390 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2391 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2392 trigger_events[i][j] = FALSE;
2394 /* add trigger events from element change event properties */
2395 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2397 struct ElementInfo *ei = &element_info[i];
2399 for (j = 0; j < ei->num_change_pages; j++)
2401 if (!ei->change_page[j].can_change_or_has_action)
2404 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2406 int trigger_element = ei->change_page[j].trigger_element;
2408 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2410 if (ei->change_page[j].has_event[k])
2412 if (IS_GROUP_ELEMENT(trigger_element))
2414 struct ElementGroupInfo *group =
2415 element_info[trigger_element].group;
2417 for (l = 0; l < group->num_elements_resolved; l++)
2418 trigger_events[group->element_resolved[l]][k] = TRUE;
2420 else if (trigger_element == EL_ANY_ELEMENT)
2421 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2422 trigger_events[l][k] = TRUE;
2424 trigger_events[trigger_element][k] = TRUE;
2431 /* ---------- initialize push delay -------------------------------------- */
2433 /* initialize push delay values to default */
2434 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2436 if (!IS_CUSTOM_ELEMENT(i))
2438 /* set default push delay values (corrected since version 3.0.7-1) */
2439 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2441 element_info[i].push_delay_fixed = 2;
2442 element_info[i].push_delay_random = 8;
2446 element_info[i].push_delay_fixed = 8;
2447 element_info[i].push_delay_random = 8;
2452 /* set push delay value for certain elements from pre-defined list */
2453 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2455 int e = push_delay_list[i].element;
2457 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2458 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2461 /* set push delay value for Supaplex elements for newer engine versions */
2462 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2464 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2466 if (IS_SP_ELEMENT(i))
2468 /* set SP push delay to just enough to push under a falling zonk */
2469 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2471 element_info[i].push_delay_fixed = delay;
2472 element_info[i].push_delay_random = 0;
2477 /* ---------- initialize move stepsize ----------------------------------- */
2479 /* initialize move stepsize values to default */
2480 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2481 if (!IS_CUSTOM_ELEMENT(i))
2482 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2484 /* set move stepsize value for certain elements from pre-defined list */
2485 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2487 int e = move_stepsize_list[i].element;
2489 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2492 /* ---------- initialize collect score ----------------------------------- */
2494 /* initialize collect score values for custom elements from initial value */
2495 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2496 if (IS_CUSTOM_ELEMENT(i))
2497 element_info[i].collect_score = element_info[i].collect_score_initial;
2499 /* ---------- initialize collect count ----------------------------------- */
2501 /* initialize collect count values for non-custom elements */
2502 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2503 if (!IS_CUSTOM_ELEMENT(i))
2504 element_info[i].collect_count_initial = 0;
2506 /* add collect count values for all elements from pre-defined list */
2507 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2508 element_info[collect_count_list[i].element].collect_count_initial =
2509 collect_count_list[i].count;
2511 /* ---------- initialize access direction -------------------------------- */
2513 /* initialize access direction values to default (access from every side) */
2514 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2515 if (!IS_CUSTOM_ELEMENT(i))
2516 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2518 /* set access direction value for certain elements from pre-defined list */
2519 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2520 element_info[access_direction_list[i].element].access_direction =
2521 access_direction_list[i].direction;
2523 /* ---------- initialize explosion content ------------------------------- */
2524 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2526 if (IS_CUSTOM_ELEMENT(i))
2529 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2531 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2533 element_info[i].content.e[x][y] =
2534 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2535 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2536 i == EL_PLAYER_3 ? EL_EMERALD :
2537 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2538 i == EL_MOLE ? EL_EMERALD_RED :
2539 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2540 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2541 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2542 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2543 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2544 i == EL_WALL_EMERALD ? EL_EMERALD :
2545 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2546 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2547 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2548 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2549 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2550 i == EL_WALL_PEARL ? EL_PEARL :
2551 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2556 /* ---------- initialize recursion detection ------------------------------ */
2557 recursion_loop_depth = 0;
2558 recursion_loop_detected = FALSE;
2559 recursion_loop_element = EL_UNDEFINED;
2562 int get_num_special_action(int element, int action_first, int action_last)
2564 int num_special_action = 0;
2567 for (i = action_first; i <= action_last; i++)
2569 boolean found = FALSE;
2571 for (j = 0; j < NUM_DIRECTIONS; j++)
2572 if (el_act_dir2img(element, i, j) !=
2573 el_act_dir2img(element, ACTION_DEFAULT, j))
2577 num_special_action++;
2582 return num_special_action;
2587 =============================================================================
2589 -----------------------------------------------------------------------------
2590 initialize and start new game
2591 =============================================================================
2596 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2597 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2598 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2600 boolean do_fading = (game_status == GAME_MODE_MAIN);
2604 game_status = GAME_MODE_PLAYING;
2608 /* don't play tapes over network */
2609 network_playing = (options.network && !tape.playing);
2611 for (i = 0; i < MAX_PLAYERS; i++)
2613 struct PlayerInfo *player = &stored_player[i];
2615 player->index_nr = i;
2616 player->index_bit = (1 << i);
2617 player->element_nr = EL_PLAYER_1 + i;
2619 player->present = FALSE;
2620 player->active = FALSE;
2621 player->killed = FALSE;
2624 player->effective_action = 0;
2625 player->programmed_action = 0;
2628 player->score_final = 0;
2630 player->gems_still_needed = level.gems_needed;
2631 player->sokobanfields_still_needed = 0;
2632 player->lights_still_needed = 0;
2633 player->friends_still_needed = 0;
2635 for (j = 0; j < MAX_NUM_KEYS; j++)
2636 player->key[j] = FALSE;
2638 player->num_white_keys = 0;
2640 player->dynabomb_count = 0;
2641 player->dynabomb_size = 1;
2642 player->dynabombs_left = 0;
2643 player->dynabomb_xl = FALSE;
2645 player->MovDir = MV_NONE;
2648 player->GfxDir = MV_NONE;
2649 player->GfxAction = ACTION_DEFAULT;
2651 player->StepFrame = 0;
2653 player->use_murphy = FALSE;
2654 player->artwork_element =
2655 (level.use_artwork_element[i] ? level.artwork_element[i] :
2656 player->element_nr);
2658 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2659 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2661 player->gravity = level.initial_player_gravity[i];
2663 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2665 player->actual_frame_counter = 0;
2667 player->step_counter = 0;
2669 player->last_move_dir = MV_NONE;
2671 player->is_active = FALSE;
2673 player->is_waiting = FALSE;
2674 player->is_moving = FALSE;
2675 player->is_auto_moving = FALSE;
2676 player->is_digging = FALSE;
2677 player->is_snapping = FALSE;
2678 player->is_collecting = FALSE;
2679 player->is_pushing = FALSE;
2680 player->is_switching = FALSE;
2681 player->is_dropping = FALSE;
2682 player->is_dropping_pressed = FALSE;
2684 player->is_bored = FALSE;
2685 player->is_sleeping = FALSE;
2687 player->frame_counter_bored = -1;
2688 player->frame_counter_sleeping = -1;
2690 player->anim_delay_counter = 0;
2691 player->post_delay_counter = 0;
2693 player->dir_waiting = MV_NONE;
2694 player->action_waiting = ACTION_DEFAULT;
2695 player->last_action_waiting = ACTION_DEFAULT;
2696 player->special_action_bored = ACTION_DEFAULT;
2697 player->special_action_sleeping = ACTION_DEFAULT;
2699 player->switch_x = -1;
2700 player->switch_y = -1;
2702 player->drop_x = -1;
2703 player->drop_y = -1;
2705 player->show_envelope = 0;
2707 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2709 player->push_delay = -1; /* initialized when pushing starts */
2710 player->push_delay_value = game.initial_push_delay_value;
2712 player->drop_delay = 0;
2713 player->drop_pressed_delay = 0;
2715 player->last_jx = -1;
2716 player->last_jy = -1;
2720 player->shield_normal_time_left = 0;
2721 player->shield_deadly_time_left = 0;
2723 player->inventory_infinite_element = EL_UNDEFINED;
2724 player->inventory_size = 0;
2726 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2727 SnapField(player, 0, 0);
2729 player->LevelSolved = FALSE;
2730 player->GameOver = FALSE;
2732 player->LevelSolved_GameWon = FALSE;
2733 player->LevelSolved_GameEnd = FALSE;
2734 player->LevelSolved_PanelOff = FALSE;
2735 player->LevelSolved_SaveTape = FALSE;
2736 player->LevelSolved_SaveScore = FALSE;
2739 network_player_action_received = FALSE;
2741 #if defined(NETWORK_AVALIABLE)
2742 /* initial null action */
2743 if (network_playing)
2744 SendToServer_MovePlayer(MV_NONE);
2753 TimeLeft = level.time;
2756 ScreenMovDir = MV_NONE;
2760 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2762 AllPlayersGone = FALSE;
2764 game.yamyam_content_nr = 0;
2765 game.magic_wall_active = FALSE;
2766 game.magic_wall_time_left = 0;
2767 game.light_time_left = 0;
2768 game.timegate_time_left = 0;
2769 game.switchgate_pos = 0;
2770 game.wind_direction = level.wind_direction_initial;
2772 #if !USE_PLAYER_GRAVITY
2773 game.gravity = FALSE;
2774 game.explosions_delayed = TRUE;
2777 game.lenses_time_left = 0;
2778 game.magnify_time_left = 0;
2780 game.ball_state = level.ball_state_initial;
2781 game.ball_content_nr = 0;
2783 game.envelope_active = FALSE;
2785 /* set focus to local player for network games, else to all players */
2786 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2787 game.centered_player_nr_next = game.centered_player_nr;
2788 game.set_centered_player = FALSE;
2790 if (network_playing && tape.recording)
2792 /* store client dependent player focus when recording network games */
2793 tape.centered_player_nr_next = game.centered_player_nr_next;
2794 tape.set_centered_player = TRUE;
2797 for (i = 0; i < NUM_BELTS; i++)
2799 game.belt_dir[i] = MV_NONE;
2800 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2803 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2804 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2806 SCAN_PLAYFIELD(x, y)
2808 Feld[x][y] = level.field[x][y];
2809 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2810 ChangeDelay[x][y] = 0;
2811 ChangePage[x][y] = -1;
2812 #if USE_NEW_CUSTOM_VALUE
2813 CustomValue[x][y] = 0; /* initialized in InitField() */
2815 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2817 WasJustMoving[x][y] = 0;
2818 WasJustFalling[x][y] = 0;
2819 CheckCollision[x][y] = 0;
2820 CheckImpact[x][y] = 0;
2822 Pushed[x][y] = FALSE;
2824 ChangeCount[x][y] = 0;
2825 ChangeEvent[x][y] = -1;
2827 ExplodePhase[x][y] = 0;
2828 ExplodeDelay[x][y] = 0;
2829 ExplodeField[x][y] = EX_TYPE_NONE;
2831 RunnerVisit[x][y] = 0;
2832 PlayerVisit[x][y] = 0;
2835 GfxRandom[x][y] = INIT_GFX_RANDOM();
2836 GfxElement[x][y] = EL_UNDEFINED;
2837 GfxAction[x][y] = ACTION_DEFAULT;
2838 GfxDir[x][y] = MV_NONE;
2841 SCAN_PLAYFIELD(x, y)
2843 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2845 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2847 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2850 InitField(x, y, TRUE);
2855 for (i = 0; i < MAX_PLAYERS; i++)
2857 struct PlayerInfo *player = &stored_player[i];
2859 /* set number of special actions for bored and sleeping animation */
2860 player->num_special_action_bored =
2861 get_num_special_action(player->artwork_element,
2862 ACTION_BORING_1, ACTION_BORING_LAST);
2863 player->num_special_action_sleeping =
2864 get_num_special_action(player->artwork_element,
2865 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2868 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2869 emulate_sb ? EMU_SOKOBAN :
2870 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2872 #if USE_NEW_ALL_SLIPPERY
2873 /* initialize type of slippery elements */
2874 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2876 if (!IS_CUSTOM_ELEMENT(i))
2878 /* default: elements slip down either to the left or right randomly */
2879 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2881 /* SP style elements prefer to slip down on the left side */
2882 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2883 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2885 /* BD style elements prefer to slip down on the left side */
2886 if (game.emulation == EMU_BOULDERDASH)
2887 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2892 /* initialize explosion and ignition delay */
2893 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2895 if (!IS_CUSTOM_ELEMENT(i))
2898 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2899 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2900 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2901 int last_phase = (num_phase + 1) * delay;
2902 int half_phase = (num_phase / 2) * delay;
2904 element_info[i].explosion_delay = last_phase - 1;
2905 element_info[i].ignition_delay = half_phase;
2907 if (i == EL_BLACK_ORB)
2908 element_info[i].ignition_delay = 1;
2912 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2913 element_info[i].explosion_delay = 1;
2915 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2916 element_info[i].ignition_delay = 1;
2920 /* correct non-moving belts to start moving left */
2921 for (i = 0; i < NUM_BELTS; i++)
2922 if (game.belt_dir[i] == MV_NONE)
2923 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2925 /* check if any connected player was not found in playfield */
2926 for (i = 0; i < MAX_PLAYERS; i++)
2928 struct PlayerInfo *player = &stored_player[i];
2930 if (player->connected && !player->present)
2932 for (j = 0; j < MAX_PLAYERS; j++)
2934 struct PlayerInfo *some_player = &stored_player[j];
2935 int jx = some_player->jx, jy = some_player->jy;
2937 /* assign first free player found that is present in the playfield */
2938 if (some_player->present && !some_player->connected)
2940 player->present = TRUE;
2941 player->active = TRUE;
2943 some_player->present = FALSE;
2944 some_player->active = FALSE;
2946 player->artwork_element = some_player->artwork_element;
2948 player->block_last_field = some_player->block_last_field;
2949 player->block_delay_adjustment = some_player->block_delay_adjustment;
2951 StorePlayer[jx][jy] = player->element_nr;
2952 player->jx = player->last_jx = jx;
2953 player->jy = player->last_jy = jy;
2963 /* when playing a tape, eliminate all players who do not participate */
2965 for (i = 0; i < MAX_PLAYERS; i++)
2967 if (stored_player[i].active && !tape.player_participates[i])
2969 struct PlayerInfo *player = &stored_player[i];
2970 int jx = player->jx, jy = player->jy;
2972 player->active = FALSE;
2973 StorePlayer[jx][jy] = 0;
2974 Feld[jx][jy] = EL_EMPTY;
2978 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2980 /* when in single player mode, eliminate all but the first active player */
2982 for (i = 0; i < MAX_PLAYERS; i++)
2984 if (stored_player[i].active)
2986 for (j = i + 1; j < MAX_PLAYERS; j++)
2988 if (stored_player[j].active)
2990 struct PlayerInfo *player = &stored_player[j];
2991 int jx = player->jx, jy = player->jy;
2993 player->active = FALSE;
2994 player->present = FALSE;
2996 StorePlayer[jx][jy] = 0;
2997 Feld[jx][jy] = EL_EMPTY;
3004 /* when recording the game, store which players take part in the game */
3007 for (i = 0; i < MAX_PLAYERS; i++)
3008 if (stored_player[i].active)
3009 tape.player_participates[i] = TRUE;
3014 for (i = 0; i < MAX_PLAYERS; i++)
3016 struct PlayerInfo *player = &stored_player[i];
3018 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3023 if (local_player == player)
3024 printf("Player %d is local player.\n", i+1);
3028 if (BorderElement == EL_EMPTY)
3031 SBX_Right = lev_fieldx - SCR_FIELDX;
3033 SBY_Lower = lev_fieldy - SCR_FIELDY;
3038 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3040 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3043 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3044 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3046 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3047 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3049 /* if local player not found, look for custom element that might create
3050 the player (make some assumptions about the right custom element) */
3051 if (!local_player->present)
3053 int start_x = 0, start_y = 0;
3054 int found_rating = 0;
3055 int found_element = EL_UNDEFINED;
3056 int player_nr = local_player->index_nr;
3058 SCAN_PLAYFIELD(x, y)
3060 int element = Feld[x][y];
3065 if (level.use_start_element[player_nr] &&
3066 level.start_element[player_nr] == element &&
3073 found_element = element;
3076 if (!IS_CUSTOM_ELEMENT(element))
3079 if (CAN_CHANGE(element))
3081 for (i = 0; i < element_info[element].num_change_pages; i++)
3083 /* check for player created from custom element as single target */
3084 content = element_info[element].change_page[i].target_element;
3085 is_player = ELEM_IS_PLAYER(content);
3087 if (is_player && (found_rating < 3 || element < found_element))
3093 found_element = element;
3098 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3100 /* check for player created from custom element as explosion content */
3101 content = element_info[element].content.e[xx][yy];
3102 is_player = ELEM_IS_PLAYER(content);
3104 if (is_player && (found_rating < 2 || element < found_element))
3106 start_x = x + xx - 1;
3107 start_y = y + yy - 1;
3110 found_element = element;
3113 if (!CAN_CHANGE(element))
3116 for (i = 0; i < element_info[element].num_change_pages; i++)
3118 /* check for player created from custom element as extended target */
3120 element_info[element].change_page[i].target_content.e[xx][yy];
3122 is_player = ELEM_IS_PLAYER(content);
3124 if (is_player && (found_rating < 1 || element < found_element))
3126 start_x = x + xx - 1;
3127 start_y = y + yy - 1;
3130 found_element = element;
3136 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3137 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3140 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3141 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3146 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3147 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3148 local_player->jx - MIDPOSX);
3150 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3151 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3152 local_player->jy - MIDPOSY);
3157 if (!game.restart_level)
3158 CloseDoor(DOOR_CLOSE_1);
3161 if (level_editor_test_game)
3162 FadeSkipNextFadeIn();
3166 if (level_editor_test_game)
3167 fading = fading_none;
3169 fading = menu.destination;
3173 FadeOut(REDRAW_FIELD);
3176 FadeOut(REDRAW_FIELD);
3179 /* !!! FIX THIS (START) !!! */
3180 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3182 InitGameEngine_EM();
3184 /* blit playfield from scroll buffer to normal back buffer for fading in */
3185 BlitScreenToBitmap_EM(backbuffer);
3192 /* after drawing the level, correct some elements */
3193 if (game.timegate_time_left == 0)
3194 CloseAllOpenTimegates();
3196 /* blit playfield from scroll buffer to normal back buffer for fading in */
3197 if (setup.soft_scrolling)
3198 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3200 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3202 /* !!! FIX THIS (END) !!! */
3205 FadeIn(REDRAW_FIELD);
3208 FadeIn(REDRAW_FIELD);
3213 if (!game.restart_level)
3215 /* copy default game door content to main double buffer */
3216 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3217 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3220 SetPanelBackground();
3221 SetDrawBackgroundMask(REDRAW_DOOR_1);
3223 DrawGameDoorValues();
3225 if (!game.restart_level)
3229 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3230 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3231 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3235 /* copy actual game door content to door double buffer for OpenDoor() */
3236 BlitBitmap(drawto, bitmap_db_door,
3237 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3239 OpenDoor(DOOR_OPEN_ALL);
3241 PlaySound(SND_GAME_STARTING);
3243 if (setup.sound_music)
3246 KeyboardAutoRepeatOffUnlessAutoplay();
3250 for (i = 0; i < MAX_PLAYERS; i++)
3251 printf("Player %d %sactive.\n",
3252 i + 1, (stored_player[i].active ? "" : "not "));
3263 game.restart_level = FALSE;
3266 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3268 /* this is used for non-R'n'D game engines to update certain engine values */
3270 /* needed to determine if sounds are played within the visible screen area */
3271 scroll_x = actual_scroll_x;
3272 scroll_y = actual_scroll_y;
3275 void InitMovDir(int x, int y)
3277 int i, element = Feld[x][y];
3278 static int xy[4][2] =
3285 static int direction[3][4] =
3287 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3288 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3289 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3298 Feld[x][y] = EL_BUG;
3299 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3302 case EL_SPACESHIP_RIGHT:
3303 case EL_SPACESHIP_UP:
3304 case EL_SPACESHIP_LEFT:
3305 case EL_SPACESHIP_DOWN:
3306 Feld[x][y] = EL_SPACESHIP;
3307 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3310 case EL_BD_BUTTERFLY_RIGHT:
3311 case EL_BD_BUTTERFLY_UP:
3312 case EL_BD_BUTTERFLY_LEFT:
3313 case EL_BD_BUTTERFLY_DOWN:
3314 Feld[x][y] = EL_BD_BUTTERFLY;
3315 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3318 case EL_BD_FIREFLY_RIGHT:
3319 case EL_BD_FIREFLY_UP:
3320 case EL_BD_FIREFLY_LEFT:
3321 case EL_BD_FIREFLY_DOWN:
3322 Feld[x][y] = EL_BD_FIREFLY;
3323 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3326 case EL_PACMAN_RIGHT:
3328 case EL_PACMAN_LEFT:
3329 case EL_PACMAN_DOWN:
3330 Feld[x][y] = EL_PACMAN;
3331 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3334 case EL_YAMYAM_LEFT:
3335 case EL_YAMYAM_RIGHT:
3337 case EL_YAMYAM_DOWN:
3338 Feld[x][y] = EL_YAMYAM;
3339 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3342 case EL_SP_SNIKSNAK:
3343 MovDir[x][y] = MV_UP;
3346 case EL_SP_ELECTRON:
3347 MovDir[x][y] = MV_LEFT;
3354 Feld[x][y] = EL_MOLE;
3355 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3359 if (IS_CUSTOM_ELEMENT(element))
3361 struct ElementInfo *ei = &element_info[element];
3362 int move_direction_initial = ei->move_direction_initial;
3363 int move_pattern = ei->move_pattern;
3365 if (move_direction_initial == MV_START_PREVIOUS)
3367 if (MovDir[x][y] != MV_NONE)
3370 move_direction_initial = MV_START_AUTOMATIC;
3373 if (move_direction_initial == MV_START_RANDOM)
3374 MovDir[x][y] = 1 << RND(4);
3375 else if (move_direction_initial & MV_ANY_DIRECTION)
3376 MovDir[x][y] = move_direction_initial;
3377 else if (move_pattern == MV_ALL_DIRECTIONS ||
3378 move_pattern == MV_TURNING_LEFT ||
3379 move_pattern == MV_TURNING_RIGHT ||
3380 move_pattern == MV_TURNING_LEFT_RIGHT ||
3381 move_pattern == MV_TURNING_RIGHT_LEFT ||
3382 move_pattern == MV_TURNING_RANDOM)
3383 MovDir[x][y] = 1 << RND(4);
3384 else if (move_pattern == MV_HORIZONTAL)
3385 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3386 else if (move_pattern == MV_VERTICAL)
3387 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3388 else if (move_pattern & MV_ANY_DIRECTION)
3389 MovDir[x][y] = element_info[element].move_pattern;
3390 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3391 move_pattern == MV_ALONG_RIGHT_SIDE)
3393 /* use random direction as default start direction */
3394 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3395 MovDir[x][y] = 1 << RND(4);
3397 for (i = 0; i < NUM_DIRECTIONS; i++)
3399 int x1 = x + xy[i][0];
3400 int y1 = y + xy[i][1];
3402 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3404 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3405 MovDir[x][y] = direction[0][i];
3407 MovDir[x][y] = direction[1][i];
3416 MovDir[x][y] = 1 << RND(4);
3418 if (element != EL_BUG &&
3419 element != EL_SPACESHIP &&
3420 element != EL_BD_BUTTERFLY &&
3421 element != EL_BD_FIREFLY)
3424 for (i = 0; i < NUM_DIRECTIONS; i++)
3426 int x1 = x + xy[i][0];
3427 int y1 = y + xy[i][1];
3429 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3431 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3433 MovDir[x][y] = direction[0][i];
3436 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3437 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3439 MovDir[x][y] = direction[1][i];
3448 GfxDir[x][y] = MovDir[x][y];
3451 void InitAmoebaNr(int x, int y)
3454 int group_nr = AmoebeNachbarNr(x, y);
3458 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3460 if (AmoebaCnt[i] == 0)
3468 AmoebaNr[x][y] = group_nr;
3469 AmoebaCnt[group_nr]++;
3470 AmoebaCnt2[group_nr]++;
3473 static void PlayerWins(struct PlayerInfo *player)
3475 player->LevelSolved = TRUE;
3476 player->GameOver = TRUE;
3478 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3479 level.native_em_level->lev->score : player->score);
3484 static int time, time_final;
3485 static int score, score_final;
3486 static int game_over_delay_1 = 0;
3487 static int game_over_delay_2 = 0;
3488 int game_over_delay_value_1 = 50;
3489 int game_over_delay_value_2 = 50;
3491 if (!local_player->LevelSolved_GameWon)
3495 /* do not start end game actions before the player stops moving (to exit) */
3496 if (local_player->MovPos)
3499 local_player->LevelSolved_GameWon = TRUE;
3500 local_player->LevelSolved_SaveTape = tape.recording;
3501 local_player->LevelSolved_SaveScore = !tape.playing;
3503 if (tape.auto_play) /* tape might already be stopped here */
3504 tape.auto_play_level_solved = TRUE;
3510 game_over_delay_1 = game_over_delay_value_1;
3511 game_over_delay_2 = game_over_delay_value_2;
3513 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3514 score = score_final = local_player->score_final;
3519 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3521 else if (level.time == 0 && TimePlayed < 999)
3524 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3527 local_player->score_final = score_final;
3529 if (level_editor_test_game)
3532 score = score_final;
3534 DrawGameValue_Time(time);
3535 DrawGameValue_Score(score);
3538 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3540 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3542 /* close exit door after last player */
3543 if ((AllPlayersGone &&
3544 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3545 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3546 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3547 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3548 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3550 int element = Feld[ExitX][ExitY];
3553 if (element == EL_EM_EXIT_OPEN ||
3554 element == EL_EM_STEEL_EXIT_OPEN)
3561 Feld[ExitX][ExitY] =
3562 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3563 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3564 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3565 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3566 EL_EM_STEEL_EXIT_CLOSING);
3568 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3572 /* player disappears */
3573 DrawLevelField(ExitX, ExitY);
3576 for (i = 0; i < MAX_PLAYERS; i++)
3578 struct PlayerInfo *player = &stored_player[i];
3580 if (player->present)
3582 RemovePlayer(player);
3584 /* player disappears */
3585 DrawLevelField(player->jx, player->jy);
3590 PlaySound(SND_GAME_WINNING);
3593 if (game_over_delay_1 > 0)
3595 game_over_delay_1--;
3600 if (time != time_final)
3602 int time_to_go = ABS(time_final - time);
3603 int time_count_dir = (time < time_final ? +1 : -1);
3604 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3606 time += time_count_steps * time_count_dir;
3607 score += time_count_steps * level.score[SC_TIME_BONUS];
3609 DrawGameValue_Time(time);
3610 DrawGameValue_Score(score);
3612 if (time == time_final)
3613 StopSound(SND_GAME_LEVELTIME_BONUS);
3614 else if (setup.sound_loops)
3615 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3617 PlaySound(SND_GAME_LEVELTIME_BONUS);
3622 local_player->LevelSolved_PanelOff = TRUE;
3624 if (game_over_delay_2 > 0)
3626 game_over_delay_2--;
3639 boolean raise_level = FALSE;
3641 local_player->LevelSolved_GameEnd = TRUE;
3643 CloseDoor(DOOR_CLOSE_1);
3645 if (local_player->LevelSolved_SaveTape)
3652 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3654 SaveTape(tape.level_nr); /* ask to save tape */
3658 if (level_editor_test_game)
3660 game_status = GAME_MODE_MAIN;
3663 DrawAndFadeInMainMenu(REDRAW_FIELD);
3671 if (!local_player->LevelSolved_SaveScore)
3674 FadeOut(REDRAW_FIELD);
3677 game_status = GAME_MODE_MAIN;
3679 DrawAndFadeInMainMenu(REDRAW_FIELD);
3684 if (level_nr == leveldir_current->handicap_level)
3686 leveldir_current->handicap_level++;
3687 SaveLevelSetup_SeriesInfo();
3690 if (level_nr < leveldir_current->last_level)
3691 raise_level = TRUE; /* advance to next level */
3693 if ((hi_pos = NewHiScore()) >= 0)
3695 game_status = GAME_MODE_SCORES;
3697 DrawHallOfFame(hi_pos);
3708 FadeOut(REDRAW_FIELD);
3711 game_status = GAME_MODE_MAIN;
3719 DrawAndFadeInMainMenu(REDRAW_FIELD);
3728 LoadScore(level_nr);
3730 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3731 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3734 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3736 if (local_player->score_final > highscore[k].Score)
3738 /* player has made it to the hall of fame */
3740 if (k < MAX_SCORE_ENTRIES - 1)
3742 int m = MAX_SCORE_ENTRIES - 1;
3745 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3746 if (strEqual(setup.player_name, highscore[l].Name))
3748 if (m == k) /* player's new highscore overwrites his old one */
3752 for (l = m; l > k; l--)
3754 strcpy(highscore[l].Name, highscore[l - 1].Name);
3755 highscore[l].Score = highscore[l - 1].Score;
3762 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3763 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3764 highscore[k].Score = local_player->score_final;
3770 else if (!strncmp(setup.player_name, highscore[k].Name,
3771 MAX_PLAYER_NAME_LEN))
3772 break; /* player already there with a higher score */
3778 SaveScore(level_nr);
3783 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3785 int element = Feld[x][y];
3786 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3787 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3788 int horiz_move = (dx != 0);
3789 int sign = (horiz_move ? dx : dy);
3790 int step = sign * element_info[element].move_stepsize;
3792 /* special values for move stepsize for spring and things on conveyor belt */
3795 if (CAN_FALL(element) &&
3796 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3797 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3798 else if (element == EL_SPRING)
3799 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3805 inline static int getElementMoveStepsize(int x, int y)
3807 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3810 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3812 if (player->GfxAction != action || player->GfxDir != dir)
3815 printf("Player frame reset! (%d => %d, %d => %d)\n",
3816 player->GfxAction, action, player->GfxDir, dir);
3819 player->GfxAction = action;
3820 player->GfxDir = dir;
3822 player->StepFrame = 0;
3826 #if USE_GFX_RESET_GFX_ANIMATION
3827 static void ResetGfxFrame(int x, int y, boolean redraw)
3829 int element = Feld[x][y];
3830 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3831 int last_gfx_frame = GfxFrame[x][y];
3833 if (graphic_info[graphic].anim_global_sync)
3834 GfxFrame[x][y] = FrameCounter;
3835 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3836 GfxFrame[x][y] = CustomValue[x][y];
3837 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3838 GfxFrame[x][y] = element_info[element].collect_score;
3839 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3840 GfxFrame[x][y] = ChangeDelay[x][y];
3842 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3843 DrawLevelGraphicAnimation(x, y, graphic);
3847 static void ResetGfxAnimation(int x, int y)
3849 GfxAction[x][y] = ACTION_DEFAULT;
3850 GfxDir[x][y] = MovDir[x][y];
3853 #if USE_GFX_RESET_GFX_ANIMATION
3854 ResetGfxFrame(x, y, FALSE);
3858 static void ResetRandomAnimationValue(int x, int y)
3860 GfxRandom[x][y] = INIT_GFX_RANDOM();
3863 void InitMovingField(int x, int y, int direction)
3865 int element = Feld[x][y];
3866 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3867 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3870 boolean is_moving_before, is_moving_after;
3872 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3875 /* check if element was/is moving or being moved before/after mode change */
3878 is_moving_before = (WasJustMoving[x][y] != 0);
3880 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3881 is_moving_before = WasJustMoving[x][y];
3884 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3886 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3888 /* reset animation only for moving elements which change direction of moving
3889 or which just started or stopped moving
3890 (else CEs with property "can move" / "not moving" are reset each frame) */
3891 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3893 if (is_moving_before != is_moving_after ||
3894 direction != MovDir[x][y])
3895 ResetGfxAnimation(x, y);
3897 if ((is_moving_before || is_moving_after) && !continues_moving)
3898 ResetGfxAnimation(x, y);
3901 if (!continues_moving)
3902 ResetGfxAnimation(x, y);
3905 MovDir[x][y] = direction;
3906 GfxDir[x][y] = direction;
3908 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3909 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3910 direction == MV_DOWN && CAN_FALL(element) ?
3911 ACTION_FALLING : ACTION_MOVING);
3913 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3914 ACTION_FALLING : ACTION_MOVING);
3917 /* this is needed for CEs with property "can move" / "not moving" */
3919 if (is_moving_after)
3921 if (Feld[newx][newy] == EL_EMPTY)
3922 Feld[newx][newy] = EL_BLOCKED;
3924 MovDir[newx][newy] = MovDir[x][y];
3926 #if USE_NEW_CUSTOM_VALUE
3927 CustomValue[newx][newy] = CustomValue[x][y];
3930 GfxFrame[newx][newy] = GfxFrame[x][y];
3931 GfxRandom[newx][newy] = GfxRandom[x][y];
3932 GfxAction[newx][newy] = GfxAction[x][y];
3933 GfxDir[newx][newy] = GfxDir[x][y];
3937 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3939 int direction = MovDir[x][y];
3940 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3941 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3947 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3949 int oldx = x, oldy = y;
3950 int direction = MovDir[x][y];
3952 if (direction == MV_LEFT)
3954 else if (direction == MV_RIGHT)
3956 else if (direction == MV_UP)
3958 else if (direction == MV_DOWN)
3961 *comes_from_x = oldx;
3962 *comes_from_y = oldy;
3965 int MovingOrBlocked2Element(int x, int y)
3967 int element = Feld[x][y];
3969 if (element == EL_BLOCKED)
3973 Blocked2Moving(x, y, &oldx, &oldy);
3974 return Feld[oldx][oldy];
3980 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3982 /* like MovingOrBlocked2Element(), but if element is moving
3983 and (x,y) is the field the moving element is just leaving,
3984 return EL_BLOCKED instead of the element value */
3985 int element = Feld[x][y];
3987 if (IS_MOVING(x, y))
3989 if (element == EL_BLOCKED)
3993 Blocked2Moving(x, y, &oldx, &oldy);
3994 return Feld[oldx][oldy];
4003 static void RemoveField(int x, int y)
4005 Feld[x][y] = EL_EMPTY;
4011 #if USE_NEW_CUSTOM_VALUE
4012 CustomValue[x][y] = 0;
4016 ChangeDelay[x][y] = 0;
4017 ChangePage[x][y] = -1;
4018 Pushed[x][y] = FALSE;
4021 ExplodeField[x][y] = EX_TYPE_NONE;
4024 GfxElement[x][y] = EL_UNDEFINED;
4025 GfxAction[x][y] = ACTION_DEFAULT;
4026 GfxDir[x][y] = MV_NONE;
4029 void RemoveMovingField(int x, int y)
4031 int oldx = x, oldy = y, newx = x, newy = y;
4032 int element = Feld[x][y];
4033 int next_element = EL_UNDEFINED;
4035 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4038 if (IS_MOVING(x, y))
4040 Moving2Blocked(x, y, &newx, &newy);
4042 if (Feld[newx][newy] != EL_BLOCKED)
4044 /* element is moving, but target field is not free (blocked), but
4045 already occupied by something different (example: acid pool);
4046 in this case, only remove the moving field, but not the target */
4048 RemoveField(oldx, oldy);
4050 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4052 DrawLevelField(oldx, oldy);
4057 else if (element == EL_BLOCKED)
4059 Blocked2Moving(x, y, &oldx, &oldy);
4060 if (!IS_MOVING(oldx, oldy))
4064 if (element == EL_BLOCKED &&
4065 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4066 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4067 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4068 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4069 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4070 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4071 next_element = get_next_element(Feld[oldx][oldy]);
4073 RemoveField(oldx, oldy);
4074 RemoveField(newx, newy);
4076 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4078 if (next_element != EL_UNDEFINED)
4079 Feld[oldx][oldy] = next_element;
4081 DrawLevelField(oldx, oldy);
4082 DrawLevelField(newx, newy);
4085 void DrawDynamite(int x, int y)
4087 int sx = SCREENX(x), sy = SCREENY(y);
4088 int graphic = el2img(Feld[x][y]);
4091 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4094 if (IS_WALKABLE_INSIDE(Back[x][y]))
4098 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4099 else if (Store[x][y])
4100 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4102 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4104 if (Back[x][y] || Store[x][y])
4105 DrawGraphicThruMask(sx, sy, graphic, frame);
4107 DrawGraphic(sx, sy, graphic, frame);
4110 void CheckDynamite(int x, int y)
4112 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4116 if (MovDelay[x][y] != 0)
4119 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4125 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4130 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4132 boolean num_checked_players = 0;
4135 for (i = 0; i < MAX_PLAYERS; i++)
4137 if (stored_player[i].active)
4139 int sx = stored_player[i].jx;
4140 int sy = stored_player[i].jy;
4142 if (num_checked_players == 0)
4149 *sx1 = MIN(*sx1, sx);
4150 *sy1 = MIN(*sy1, sy);
4151 *sx2 = MAX(*sx2, sx);
4152 *sy2 = MAX(*sy2, sy);
4155 num_checked_players++;
4160 static boolean checkIfAllPlayersFitToScreen_RND()
4162 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4164 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4166 return (sx2 - sx1 < SCR_FIELDX &&
4167 sy2 - sy1 < SCR_FIELDY);
4170 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4172 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4174 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4176 *sx = (sx1 + sx2) / 2;
4177 *sy = (sy1 + sy2) / 2;
4180 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4181 boolean center_screen, boolean quick_relocation)
4183 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4184 boolean no_delay = (tape.warp_forward);
4185 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4186 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4188 if (quick_relocation)
4190 int offset = (setup.scroll_delay ? 3 : 0);
4192 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4194 if (!level.shifted_relocation || center_screen)
4196 /* quick relocation (without scrolling), with centering of screen */
4198 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4199 x > SBX_Right + MIDPOSX ? SBX_Right :
4202 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4203 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4208 /* quick relocation (without scrolling), but do not center screen */
4210 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4211 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4214 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4215 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4218 int offset_x = x + (scroll_x - center_scroll_x);
4219 int offset_y = y + (scroll_y - center_scroll_y);
4221 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4222 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4223 offset_x - MIDPOSX);
4225 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4226 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4227 offset_y - MIDPOSY);
4232 /* quick relocation (without scrolling), inside visible screen area */
4234 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4235 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4236 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4238 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4239 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4240 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4242 /* don't scroll over playfield boundaries */
4243 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4244 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4246 /* don't scroll over playfield boundaries */
4247 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4248 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4251 RedrawPlayfield(TRUE, 0,0,0,0);
4256 int scroll_xx, scroll_yy;
4258 if (!level.shifted_relocation || center_screen)
4260 /* visible relocation (with scrolling), with centering of screen */
4262 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4263 x > SBX_Right + MIDPOSX ? SBX_Right :
4266 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4267 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4272 /* visible relocation (with scrolling), but do not center screen */
4274 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4275 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4278 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4279 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4282 int offset_x = x + (scroll_x - center_scroll_x);
4283 int offset_y = y + (scroll_y - center_scroll_y);
4285 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4286 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4287 offset_x - MIDPOSX);
4289 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4290 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4291 offset_y - MIDPOSY);
4296 /* visible relocation (with scrolling), with centering of screen */
4298 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4299 x > SBX_Right + MIDPOSX ? SBX_Right :
4302 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4303 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4307 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4309 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4312 int fx = FX, fy = FY;
4314 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4315 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4317 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4323 fx += dx * TILEX / 2;
4324 fy += dy * TILEY / 2;
4326 ScrollLevel(dx, dy);
4329 /* scroll in two steps of half tile size to make things smoother */
4330 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4332 Delay(wait_delay_value);
4334 /* scroll second step to align at full tile size */
4336 Delay(wait_delay_value);
4341 Delay(wait_delay_value);
4345 void RelocatePlayer(int jx, int jy, int el_player_raw)
4347 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4348 int player_nr = GET_PLAYER_NR(el_player);
4349 struct PlayerInfo *player = &stored_player[player_nr];
4350 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4351 boolean no_delay = (tape.warp_forward);
4352 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4353 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4354 int old_jx = player->jx;
4355 int old_jy = player->jy;
4356 int old_element = Feld[old_jx][old_jy];
4357 int element = Feld[jx][jy];
4358 boolean player_relocated = (old_jx != jx || old_jy != jy);
4360 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4361 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4362 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4363 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4364 int leave_side_horiz = move_dir_horiz;
4365 int leave_side_vert = move_dir_vert;
4366 int enter_side = enter_side_horiz | enter_side_vert;
4367 int leave_side = leave_side_horiz | leave_side_vert;
4369 if (player->GameOver) /* do not reanimate dead player */
4372 if (!player_relocated) /* no need to relocate the player */
4375 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4377 RemoveField(jx, jy); /* temporarily remove newly placed player */
4378 DrawLevelField(jx, jy);
4381 if (player->present)
4383 while (player->MovPos)
4385 ScrollPlayer(player, SCROLL_GO_ON);
4386 ScrollScreen(NULL, SCROLL_GO_ON);
4388 AdvanceFrameAndPlayerCounters(player->index_nr);
4393 Delay(wait_delay_value);
4396 DrawPlayer(player); /* needed here only to cleanup last field */
4397 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4399 player->is_moving = FALSE;
4402 if (IS_CUSTOM_ELEMENT(old_element))
4403 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4405 player->index_bit, leave_side);
4407 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4409 player->index_bit, leave_side);
4411 Feld[jx][jy] = el_player;
4412 InitPlayerField(jx, jy, el_player, TRUE);
4414 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4416 Feld[jx][jy] = element;
4417 InitField(jx, jy, FALSE);
4420 /* only visually relocate centered player */
4421 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4422 FALSE, level.instant_relocation);
4424 TestIfPlayerTouchesBadThing(jx, jy);
4425 TestIfPlayerTouchesCustomElement(jx, jy);
4427 if (IS_CUSTOM_ELEMENT(element))
4428 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4429 player->index_bit, enter_side);
4431 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4432 player->index_bit, enter_side);
4435 void Explode(int ex, int ey, int phase, int mode)
4441 /* !!! eliminate this variable !!! */
4442 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4444 if (game.explosions_delayed)
4446 ExplodeField[ex][ey] = mode;
4450 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4452 int center_element = Feld[ex][ey];
4453 int artwork_element, explosion_element; /* set these values later */
4456 /* --- This is only really needed (and now handled) in "Impact()". --- */
4457 /* do not explode moving elements that left the explode field in time */
4458 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4459 center_element == EL_EMPTY &&
4460 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4465 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4466 if (mode == EX_TYPE_NORMAL ||
4467 mode == EX_TYPE_CENTER ||
4468 mode == EX_TYPE_CROSS)
4469 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4472 /* remove things displayed in background while burning dynamite */
4473 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4476 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4478 /* put moving element to center field (and let it explode there) */
4479 center_element = MovingOrBlocked2Element(ex, ey);
4480 RemoveMovingField(ex, ey);
4481 Feld[ex][ey] = center_element;
4484 /* now "center_element" is finally determined -- set related values now */
4485 artwork_element = center_element; /* for custom player artwork */
4486 explosion_element = center_element; /* for custom player artwork */
4488 if (IS_PLAYER(ex, ey))
4490 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4492 artwork_element = stored_player[player_nr].artwork_element;
4494 if (level.use_explosion_element[player_nr])
4496 explosion_element = level.explosion_element[player_nr];
4497 artwork_element = explosion_element;
4502 if (mode == EX_TYPE_NORMAL ||
4503 mode == EX_TYPE_CENTER ||
4504 mode == EX_TYPE_CROSS)
4505 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4508 last_phase = element_info[explosion_element].explosion_delay + 1;
4510 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4512 int xx = x - ex + 1;
4513 int yy = y - ey + 1;
4516 if (!IN_LEV_FIELD(x, y) ||
4517 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4518 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4521 element = Feld[x][y];
4523 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4525 element = MovingOrBlocked2Element(x, y);
4527 if (!IS_EXPLOSION_PROOF(element))
4528 RemoveMovingField(x, y);
4531 /* indestructible elements can only explode in center (but not flames) */
4532 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4533 mode == EX_TYPE_BORDER)) ||
4534 element == EL_FLAMES)
4537 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4538 behaviour, for example when touching a yamyam that explodes to rocks
4539 with active deadly shield, a rock is created under the player !!! */
4540 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4542 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4543 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4544 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4546 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4549 if (IS_ACTIVE_BOMB(element))
4551 /* re-activate things under the bomb like gate or penguin */
4552 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4559 /* save walkable background elements while explosion on same tile */
4560 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4561 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4562 Back[x][y] = element;
4564 /* ignite explodable elements reached by other explosion */
4565 if (element == EL_EXPLOSION)
4566 element = Store2[x][y];
4568 if (AmoebaNr[x][y] &&
4569 (element == EL_AMOEBA_FULL ||
4570 element == EL_BD_AMOEBA ||
4571 element == EL_AMOEBA_GROWING))
4573 AmoebaCnt[AmoebaNr[x][y]]--;
4574 AmoebaCnt2[AmoebaNr[x][y]]--;
4579 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4581 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4583 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4585 if (PLAYERINFO(ex, ey)->use_murphy)
4586 Store[x][y] = EL_EMPTY;
4589 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4590 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4591 else if (ELEM_IS_PLAYER(center_element))
4592 Store[x][y] = EL_EMPTY;
4593 else if (center_element == EL_YAMYAM)
4594 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4595 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4596 Store[x][y] = element_info[center_element].content.e[xx][yy];
4598 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4599 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4600 otherwise) -- FIX THIS !!! */
4601 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4602 Store[x][y] = element_info[element].content.e[1][1];
4604 else if (!CAN_EXPLODE(element))
4605 Store[x][y] = element_info[element].content.e[1][1];
4608 Store[x][y] = EL_EMPTY;
4610 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4611 center_element == EL_AMOEBA_TO_DIAMOND)
4612 Store2[x][y] = element;
4614 Feld[x][y] = EL_EXPLOSION;
4615 GfxElement[x][y] = artwork_element;
4617 ExplodePhase[x][y] = 1;
4618 ExplodeDelay[x][y] = last_phase;
4623 if (center_element == EL_YAMYAM)
4624 game.yamyam_content_nr =
4625 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4637 GfxFrame[x][y] = 0; /* restart explosion animation */
4639 last_phase = ExplodeDelay[x][y];
4641 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4645 /* activate this even in non-DEBUG version until cause for crash in
4646 getGraphicAnimationFrame() (see below) is found and eliminated */
4652 /* this can happen if the player leaves an explosion just in time */
4653 if (GfxElement[x][y] == EL_UNDEFINED)
4654 GfxElement[x][y] = EL_EMPTY;
4656 if (GfxElement[x][y] == EL_UNDEFINED)
4659 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4660 printf("Explode(): This should never happen!\n");
4663 GfxElement[x][y] = EL_EMPTY;
4669 border_element = Store2[x][y];
4670 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4671 border_element = StorePlayer[x][y];
4673 if (phase == element_info[border_element].ignition_delay ||
4674 phase == last_phase)
4676 boolean border_explosion = FALSE;
4678 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4679 !PLAYER_EXPLOSION_PROTECTED(x, y))
4681 KillPlayerUnlessExplosionProtected(x, y);
4682 border_explosion = TRUE;
4684 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4686 Feld[x][y] = Store2[x][y];
4689 border_explosion = TRUE;
4691 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4693 AmoebeUmwandeln(x, y);
4695 border_explosion = TRUE;
4698 /* if an element just explodes due to another explosion (chain-reaction),
4699 do not immediately end the new explosion when it was the last frame of
4700 the explosion (as it would be done in the following "if"-statement!) */
4701 if (border_explosion && phase == last_phase)
4705 if (phase == last_phase)
4709 element = Feld[x][y] = Store[x][y];
4710 Store[x][y] = Store2[x][y] = 0;
4711 GfxElement[x][y] = EL_UNDEFINED;
4713 /* player can escape from explosions and might therefore be still alive */
4714 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4715 element <= EL_PLAYER_IS_EXPLODING_4)
4717 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4718 int explosion_element = EL_PLAYER_1 + player_nr;
4719 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4720 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4722 if (level.use_explosion_element[player_nr])
4723 explosion_element = level.explosion_element[player_nr];
4725 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4726 element_info[explosion_element].content.e[xx][yy]);
4729 /* restore probably existing indestructible background element */
4730 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4731 element = Feld[x][y] = Back[x][y];
4734 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4735 GfxDir[x][y] = MV_NONE;
4736 ChangeDelay[x][y] = 0;
4737 ChangePage[x][y] = -1;
4739 #if USE_NEW_CUSTOM_VALUE
4740 CustomValue[x][y] = 0;
4743 InitField_WithBug2(x, y, FALSE);
4745 DrawLevelField(x, y);
4747 TestIfElementTouchesCustomElement(x, y);
4749 if (GFX_CRUMBLED(element))
4750 DrawLevelFieldCrumbledSandNeighbours(x, y);
4752 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4753 StorePlayer[x][y] = 0;
4755 if (ELEM_IS_PLAYER(element))
4756 RelocatePlayer(x, y, element);
4758 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4760 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4761 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4764 DrawLevelFieldCrumbledSand(x, y);
4766 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4768 DrawLevelElement(x, y, Back[x][y]);
4769 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4771 else if (IS_WALKABLE_UNDER(Back[x][y]))
4773 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4774 DrawLevelElementThruMask(x, y, Back[x][y]);
4776 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4777 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4781 void DynaExplode(int ex, int ey)
4784 int dynabomb_element = Feld[ex][ey];
4785 int dynabomb_size = 1;
4786 boolean dynabomb_xl = FALSE;
4787 struct PlayerInfo *player;
4788 static int xy[4][2] =
4796 if (IS_ACTIVE_BOMB(dynabomb_element))
4798 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4799 dynabomb_size = player->dynabomb_size;
4800 dynabomb_xl = player->dynabomb_xl;
4801 player->dynabombs_left++;
4804 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4806 for (i = 0; i < NUM_DIRECTIONS; i++)
4808 for (j = 1; j <= dynabomb_size; j++)
4810 int x = ex + j * xy[i][0];
4811 int y = ey + j * xy[i][1];
4814 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4817 element = Feld[x][y];
4819 /* do not restart explosions of fields with active bombs */
4820 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4823 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4825 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4826 !IS_DIGGABLE(element) && !dynabomb_xl)
4832 void Bang(int x, int y)
4834 int element = MovingOrBlocked2Element(x, y);
4835 int explosion_type = EX_TYPE_NORMAL;
4837 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4839 struct PlayerInfo *player = PLAYERINFO(x, y);
4841 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4842 player->element_nr);
4844 if (level.use_explosion_element[player->index_nr])
4846 int explosion_element = level.explosion_element[player->index_nr];
4848 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4849 explosion_type = EX_TYPE_CROSS;
4850 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4851 explosion_type = EX_TYPE_CENTER;
4859 case EL_BD_BUTTERFLY:
4862 case EL_DARK_YAMYAM:
4866 RaiseScoreElement(element);
4869 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4870 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4871 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4872 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4873 case EL_DYNABOMB_INCREASE_NUMBER:
4874 case EL_DYNABOMB_INCREASE_SIZE:
4875 case EL_DYNABOMB_INCREASE_POWER:
4876 explosion_type = EX_TYPE_DYNA;
4879 case EL_DC_LANDMINE:
4881 case EL_EM_EXIT_OPEN:
4882 case EL_EM_STEEL_EXIT_OPEN:
4884 explosion_type = EX_TYPE_CENTER;
4889 case EL_LAMP_ACTIVE:
4890 case EL_AMOEBA_TO_DIAMOND:
4891 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4892 explosion_type = EX_TYPE_CENTER;
4896 if (element_info[element].explosion_type == EXPLODES_CROSS)
4897 explosion_type = EX_TYPE_CROSS;
4898 else if (element_info[element].explosion_type == EXPLODES_1X1)
4899 explosion_type = EX_TYPE_CENTER;
4903 if (explosion_type == EX_TYPE_DYNA)
4906 Explode(x, y, EX_PHASE_START, explosion_type);
4908 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4911 void SplashAcid(int x, int y)
4913 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4914 (!IN_LEV_FIELD(x - 1, y - 2) ||
4915 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4916 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4918 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4919 (!IN_LEV_FIELD(x + 1, y - 2) ||
4920 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4921 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4923 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4926 static void InitBeltMovement()
4928 static int belt_base_element[4] =
4930 EL_CONVEYOR_BELT_1_LEFT,
4931 EL_CONVEYOR_BELT_2_LEFT,
4932 EL_CONVEYOR_BELT_3_LEFT,
4933 EL_CONVEYOR_BELT_4_LEFT
4935 static int belt_base_active_element[4] =
4937 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4938 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4939 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4940 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4945 /* set frame order for belt animation graphic according to belt direction */
4946 for (i = 0; i < NUM_BELTS; i++)
4950 for (j = 0; j < NUM_BELT_PARTS; j++)
4952 int element = belt_base_active_element[belt_nr] + j;
4953 int graphic = el2img(element);
4955 if (game.belt_dir[i] == MV_LEFT)
4956 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4958 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4962 SCAN_PLAYFIELD(x, y)
4964 int element = Feld[x][y];
4966 for (i = 0; i < NUM_BELTS; i++)
4968 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4970 int e_belt_nr = getBeltNrFromBeltElement(element);
4973 if (e_belt_nr == belt_nr)
4975 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4977 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4984 static void ToggleBeltSwitch(int x, int y)
4986 static int belt_base_element[4] =
4988 EL_CONVEYOR_BELT_1_LEFT,
4989 EL_CONVEYOR_BELT_2_LEFT,
4990 EL_CONVEYOR_BELT_3_LEFT,
4991 EL_CONVEYOR_BELT_4_LEFT
4993 static int belt_base_active_element[4] =
4995 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4996 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4997 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4998 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5000 static int belt_base_switch_element[4] =
5002 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5003 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5004 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5005 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5007 static int belt_move_dir[4] =
5015 int element = Feld[x][y];
5016 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5017 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5018 int belt_dir = belt_move_dir[belt_dir_nr];
5021 if (!IS_BELT_SWITCH(element))
5024 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5025 game.belt_dir[belt_nr] = belt_dir;
5027 if (belt_dir_nr == 3)
5030 /* set frame order for belt animation graphic according to belt direction */
5031 for (i = 0; i < NUM_BELT_PARTS; i++)
5033 int element = belt_base_active_element[belt_nr] + i;
5034 int graphic = el2img(element);
5036 if (belt_dir == MV_LEFT)
5037 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5039 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5042 SCAN_PLAYFIELD(xx, yy)
5044 int element = Feld[xx][yy];
5046 if (IS_BELT_SWITCH(element))
5048 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5050 if (e_belt_nr == belt_nr)
5052 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5053 DrawLevelField(xx, yy);
5056 else if (IS_BELT(element) && belt_dir != MV_NONE)
5058 int e_belt_nr = getBeltNrFromBeltElement(element);
5060 if (e_belt_nr == belt_nr)
5062 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5064 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5065 DrawLevelField(xx, yy);
5068 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5070 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5072 if (e_belt_nr == belt_nr)
5074 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5076 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5077 DrawLevelField(xx, yy);
5083 static void ToggleSwitchgateSwitch(int x, int y)
5087 game.switchgate_pos = !game.switchgate_pos;
5089 SCAN_PLAYFIELD(xx, yy)
5091 int element = Feld[xx][yy];
5093 #if !USE_BOTH_SWITCHGATE_SWITCHES
5094 if (element == EL_SWITCHGATE_SWITCH_UP ||
5095 element == EL_SWITCHGATE_SWITCH_DOWN)
5097 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5098 DrawLevelField(xx, yy);
5100 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5101 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5103 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5104 DrawLevelField(xx, yy);
5107 if (element == EL_SWITCHGATE_SWITCH_UP)
5109 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5110 DrawLevelField(xx, yy);
5112 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5114 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5115 DrawLevelField(xx, yy);
5117 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5119 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5120 DrawLevelField(xx, yy);
5122 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5124 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5125 DrawLevelField(xx, yy);
5128 else if (element == EL_SWITCHGATE_OPEN ||
5129 element == EL_SWITCHGATE_OPENING)
5131 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5133 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5135 else if (element == EL_SWITCHGATE_CLOSED ||
5136 element == EL_SWITCHGATE_CLOSING)
5138 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5140 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5145 static int getInvisibleActiveFromInvisibleElement(int element)
5147 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5148 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5149 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5153 static int getInvisibleFromInvisibleActiveElement(int element)
5155 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5156 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5157 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5161 static void RedrawAllLightSwitchesAndInvisibleElements()
5165 SCAN_PLAYFIELD(x, y)
5167 int element = Feld[x][y];
5169 if (element == EL_LIGHT_SWITCH &&
5170 game.light_time_left > 0)
5172 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5173 DrawLevelField(x, y);
5175 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5176 game.light_time_left == 0)
5178 Feld[x][y] = EL_LIGHT_SWITCH;
5179 DrawLevelField(x, y);
5181 else if (element == EL_EMC_DRIPPER &&
5182 game.light_time_left > 0)
5184 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5185 DrawLevelField(x, y);
5187 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5188 game.light_time_left == 0)
5190 Feld[x][y] = EL_EMC_DRIPPER;
5191 DrawLevelField(x, y);
5193 else if (element == EL_INVISIBLE_STEELWALL ||
5194 element == EL_INVISIBLE_WALL ||
5195 element == EL_INVISIBLE_SAND)
5197 if (game.light_time_left > 0)
5198 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5200 DrawLevelField(x, y);
5202 /* uncrumble neighbour fields, if needed */
5203 if (element == EL_INVISIBLE_SAND)
5204 DrawLevelFieldCrumbledSandNeighbours(x, y);
5206 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5207 element == EL_INVISIBLE_WALL_ACTIVE ||
5208 element == EL_INVISIBLE_SAND_ACTIVE)
5210 if (game.light_time_left == 0)
5211 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5213 DrawLevelField(x, y);
5215 /* re-crumble neighbour fields, if needed */
5216 if (element == EL_INVISIBLE_SAND)
5217 DrawLevelFieldCrumbledSandNeighbours(x, y);
5222 static void RedrawAllInvisibleElementsForLenses()
5226 SCAN_PLAYFIELD(x, y)
5228 int element = Feld[x][y];
5230 if (element == EL_EMC_DRIPPER &&
5231 game.lenses_time_left > 0)
5233 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5234 DrawLevelField(x, y);
5236 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5237 game.lenses_time_left == 0)
5239 Feld[x][y] = EL_EMC_DRIPPER;
5240 DrawLevelField(x, y);
5242 else if (element == EL_INVISIBLE_STEELWALL ||
5243 element == EL_INVISIBLE_WALL ||
5244 element == EL_INVISIBLE_SAND)
5246 if (game.lenses_time_left > 0)
5247 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5249 DrawLevelField(x, y);
5251 /* uncrumble neighbour fields, if needed */
5252 if (element == EL_INVISIBLE_SAND)
5253 DrawLevelFieldCrumbledSandNeighbours(x, y);
5255 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5256 element == EL_INVISIBLE_WALL_ACTIVE ||
5257 element == EL_INVISIBLE_SAND_ACTIVE)
5259 if (game.lenses_time_left == 0)
5260 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5262 DrawLevelField(x, y);
5264 /* re-crumble neighbour fields, if needed */
5265 if (element == EL_INVISIBLE_SAND)
5266 DrawLevelFieldCrumbledSandNeighbours(x, y);
5271 static void RedrawAllInvisibleElementsForMagnifier()
5275 SCAN_PLAYFIELD(x, y)
5277 int element = Feld[x][y];
5279 if (element == EL_EMC_FAKE_GRASS &&
5280 game.magnify_time_left > 0)
5282 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5283 DrawLevelField(x, y);
5285 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5286 game.magnify_time_left == 0)
5288 Feld[x][y] = EL_EMC_FAKE_GRASS;
5289 DrawLevelField(x, y);
5291 else if (IS_GATE_GRAY(element) &&
5292 game.magnify_time_left > 0)
5294 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5295 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5296 IS_EM_GATE_GRAY(element) ?
5297 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5298 IS_EMC_GATE_GRAY(element) ?
5299 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5301 DrawLevelField(x, y);
5303 else if (IS_GATE_GRAY_ACTIVE(element) &&
5304 game.magnify_time_left == 0)
5306 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5307 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5308 IS_EM_GATE_GRAY_ACTIVE(element) ?
5309 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5310 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5311 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5313 DrawLevelField(x, y);
5318 static void ToggleLightSwitch(int x, int y)
5320 int element = Feld[x][y];
5322 game.light_time_left =
5323 (element == EL_LIGHT_SWITCH ?
5324 level.time_light * FRAMES_PER_SECOND : 0);
5326 RedrawAllLightSwitchesAndInvisibleElements();
5329 static void ActivateTimegateSwitch(int x, int y)
5333 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5335 SCAN_PLAYFIELD(xx, yy)
5337 int element = Feld[xx][yy];
5339 if (element == EL_TIMEGATE_CLOSED ||
5340 element == EL_TIMEGATE_CLOSING)
5342 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5343 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5347 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5349 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5350 DrawLevelField(xx, yy);
5357 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5358 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5360 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5364 void Impact(int x, int y)
5366 boolean last_line = (y == lev_fieldy - 1);
5367 boolean object_hit = FALSE;
5368 boolean impact = (last_line || object_hit);
5369 int element = Feld[x][y];
5370 int smashed = EL_STEELWALL;
5372 if (!last_line) /* check if element below was hit */
5374 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5377 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5378 MovDir[x][y + 1] != MV_DOWN ||
5379 MovPos[x][y + 1] <= TILEY / 2));
5381 /* do not smash moving elements that left the smashed field in time */
5382 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5383 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5386 #if USE_QUICKSAND_IMPACT_BUGFIX
5387 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5389 RemoveMovingField(x, y + 1);
5390 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5391 Feld[x][y + 2] = EL_ROCK;
5392 DrawLevelField(x, y + 2);
5397 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5399 RemoveMovingField(x, y + 1);
5400 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5401 Feld[x][y + 2] = EL_ROCK;
5402 DrawLevelField(x, y + 2);
5409 smashed = MovingOrBlocked2Element(x, y + 1);
5411 impact = (last_line || object_hit);
5414 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5416 SplashAcid(x, y + 1);
5420 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5421 /* only reset graphic animation if graphic really changes after impact */
5423 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5425 ResetGfxAnimation(x, y);
5426 DrawLevelField(x, y);
5429 if (impact && CAN_EXPLODE_IMPACT(element))
5434 else if (impact && element == EL_PEARL &&
5435 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5437 ResetGfxAnimation(x, y);
5439 Feld[x][y] = EL_PEARL_BREAKING;
5440 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5443 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5445 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5450 if (impact && element == EL_AMOEBA_DROP)
5452 if (object_hit && IS_PLAYER(x, y + 1))
5453 KillPlayerUnlessEnemyProtected(x, y + 1);
5454 else if (object_hit && smashed == EL_PENGUIN)
5458 Feld[x][y] = EL_AMOEBA_GROWING;
5459 Store[x][y] = EL_AMOEBA_WET;
5461 ResetRandomAnimationValue(x, y);
5466 if (object_hit) /* check which object was hit */
5468 if ((CAN_PASS_MAGIC_WALL(element) &&
5469 (smashed == EL_MAGIC_WALL ||
5470 smashed == EL_BD_MAGIC_WALL)) ||
5471 (CAN_PASS_DC_MAGIC_WALL(element) &&
5472 smashed == EL_DC_MAGIC_WALL))
5475 int activated_magic_wall =
5476 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5477 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5478 EL_DC_MAGIC_WALL_ACTIVE);
5480 /* activate magic wall / mill */
5481 SCAN_PLAYFIELD(xx, yy)
5483 if (Feld[xx][yy] == smashed)
5484 Feld[xx][yy] = activated_magic_wall;
5487 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5488 game.magic_wall_active = TRUE;
5490 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5491 SND_MAGIC_WALL_ACTIVATING :
5492 smashed == EL_BD_MAGIC_WALL ?
5493 SND_BD_MAGIC_WALL_ACTIVATING :
5494 SND_DC_MAGIC_WALL_ACTIVATING));
5497 if (IS_PLAYER(x, y + 1))
5499 if (CAN_SMASH_PLAYER(element))
5501 KillPlayerUnlessEnemyProtected(x, y + 1);
5505 else if (smashed == EL_PENGUIN)
5507 if (CAN_SMASH_PLAYER(element))
5513 else if (element == EL_BD_DIAMOND)
5515 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5521 else if (((element == EL_SP_INFOTRON ||
5522 element == EL_SP_ZONK) &&
5523 (smashed == EL_SP_SNIKSNAK ||
5524 smashed == EL_SP_ELECTRON ||
5525 smashed == EL_SP_DISK_ORANGE)) ||
5526 (element == EL_SP_INFOTRON &&
5527 smashed == EL_SP_DISK_YELLOW))
5532 else if (CAN_SMASH_EVERYTHING(element))
5534 if (IS_CLASSIC_ENEMY(smashed) ||
5535 CAN_EXPLODE_SMASHED(smashed))
5540 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5542 if (smashed == EL_LAMP ||
5543 smashed == EL_LAMP_ACTIVE)
5548 else if (smashed == EL_NUT)
5550 Feld[x][y + 1] = EL_NUT_BREAKING;
5551 PlayLevelSound(x, y, SND_NUT_BREAKING);
5552 RaiseScoreElement(EL_NUT);
5555 else if (smashed == EL_PEARL)
5557 ResetGfxAnimation(x, y);
5559 Feld[x][y + 1] = EL_PEARL_BREAKING;
5560 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5563 else if (smashed == EL_DIAMOND)
5565 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5566 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5569 else if (IS_BELT_SWITCH(smashed))
5571 ToggleBeltSwitch(x, y + 1);
5573 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5574 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5575 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5576 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5578 ToggleSwitchgateSwitch(x, y + 1);
5580 else if (smashed == EL_LIGHT_SWITCH ||
5581 smashed == EL_LIGHT_SWITCH_ACTIVE)
5583 ToggleLightSwitch(x, y + 1);
5588 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5591 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5593 CheckElementChangeBySide(x, y + 1, smashed, element,
5594 CE_SWITCHED, CH_SIDE_TOP);
5595 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5601 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5606 /* play sound of magic wall / mill */
5608 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5609 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5610 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5612 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5613 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5614 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5615 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5616 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5617 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5622 /* play sound of object that hits the ground */
5623 if (last_line || object_hit)
5624 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5627 inline static void TurnRoundExt(int x, int y)
5639 { 0, 0 }, { 0, 0 }, { 0, 0 },
5644 int left, right, back;
5648 { MV_DOWN, MV_UP, MV_RIGHT },
5649 { MV_UP, MV_DOWN, MV_LEFT },
5651 { MV_LEFT, MV_RIGHT, MV_DOWN },
5655 { MV_RIGHT, MV_LEFT, MV_UP }
5658 int element = Feld[x][y];
5659 int move_pattern = element_info[element].move_pattern;
5661 int old_move_dir = MovDir[x][y];
5662 int left_dir = turn[old_move_dir].left;
5663 int right_dir = turn[old_move_dir].right;
5664 int back_dir = turn[old_move_dir].back;
5666 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5667 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5668 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5669 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5671 int left_x = x + left_dx, left_y = y + left_dy;
5672 int right_x = x + right_dx, right_y = y + right_dy;
5673 int move_x = x + move_dx, move_y = y + move_dy;
5677 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5679 TestIfBadThingTouchesOtherBadThing(x, y);
5681 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5682 MovDir[x][y] = right_dir;
5683 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5684 MovDir[x][y] = left_dir;
5686 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5688 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5691 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5693 TestIfBadThingTouchesOtherBadThing(x, y);
5695 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5696 MovDir[x][y] = left_dir;
5697 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5698 MovDir[x][y] = right_dir;
5700 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5702 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5705 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5707 TestIfBadThingTouchesOtherBadThing(x, y);
5709 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5710 MovDir[x][y] = left_dir;
5711 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5712 MovDir[x][y] = right_dir;
5714 if (MovDir[x][y] != old_move_dir)
5717 else if (element == EL_YAMYAM)
5719 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5720 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5722 if (can_turn_left && can_turn_right)
5723 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5724 else if (can_turn_left)
5725 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5726 else if (can_turn_right)
5727 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5729 MovDir[x][y] = back_dir;
5731 MovDelay[x][y] = 16 + 16 * RND(3);
5733 else if (element == EL_DARK_YAMYAM)
5735 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5737 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5740 if (can_turn_left && can_turn_right)
5741 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5742 else if (can_turn_left)
5743 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5744 else if (can_turn_right)
5745 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5747 MovDir[x][y] = back_dir;
5749 MovDelay[x][y] = 16 + 16 * RND(3);
5751 else if (element == EL_PACMAN)
5753 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5754 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5756 if (can_turn_left && can_turn_right)
5757 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5758 else if (can_turn_left)
5759 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5760 else if (can_turn_right)
5761 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5763 MovDir[x][y] = back_dir;
5765 MovDelay[x][y] = 6 + RND(40);
5767 else if (element == EL_PIG)
5769 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5770 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5771 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5772 boolean should_turn_left, should_turn_right, should_move_on;
5774 int rnd = RND(rnd_value);
5776 should_turn_left = (can_turn_left &&
5778 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5779 y + back_dy + left_dy)));
5780 should_turn_right = (can_turn_right &&
5782 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5783 y + back_dy + right_dy)));
5784 should_move_on = (can_move_on &&
5787 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5788 y + move_dy + left_dy) ||
5789 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5790 y + move_dy + right_dy)));
5792 if (should_turn_left || should_turn_right || should_move_on)
5794 if (should_turn_left && should_turn_right && should_move_on)
5795 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5796 rnd < 2 * rnd_value / 3 ? right_dir :
5798 else if (should_turn_left && should_turn_right)
5799 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5800 else if (should_turn_left && should_move_on)
5801 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5802 else if (should_turn_right && should_move_on)
5803 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5804 else if (should_turn_left)
5805 MovDir[x][y] = left_dir;
5806 else if (should_turn_right)
5807 MovDir[x][y] = right_dir;
5808 else if (should_move_on)
5809 MovDir[x][y] = old_move_dir;
5811 else if (can_move_on && rnd > rnd_value / 8)
5812 MovDir[x][y] = old_move_dir;
5813 else if (can_turn_left && can_turn_right)
5814 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5815 else if (can_turn_left && rnd > rnd_value / 8)
5816 MovDir[x][y] = left_dir;
5817 else if (can_turn_right && rnd > rnd_value/8)
5818 MovDir[x][y] = right_dir;
5820 MovDir[x][y] = back_dir;
5822 xx = x + move_xy[MovDir[x][y]].dx;
5823 yy = y + move_xy[MovDir[x][y]].dy;
5825 if (!IN_LEV_FIELD(xx, yy) ||
5826 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5827 MovDir[x][y] = old_move_dir;
5831 else if (element == EL_DRAGON)
5833 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5834 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5835 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5837 int rnd = RND(rnd_value);
5839 if (can_move_on && rnd > rnd_value / 8)
5840 MovDir[x][y] = old_move_dir;
5841 else if (can_turn_left && can_turn_right)
5842 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5843 else if (can_turn_left && rnd > rnd_value / 8)
5844 MovDir[x][y] = left_dir;
5845 else if (can_turn_right && rnd > rnd_value / 8)
5846 MovDir[x][y] = right_dir;
5848 MovDir[x][y] = back_dir;
5850 xx = x + move_xy[MovDir[x][y]].dx;
5851 yy = y + move_xy[MovDir[x][y]].dy;
5853 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5854 MovDir[x][y] = old_move_dir;
5858 else if (element == EL_MOLE)
5860 boolean can_move_on =
5861 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5862 IS_AMOEBOID(Feld[move_x][move_y]) ||
5863 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5866 boolean can_turn_left =
5867 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5868 IS_AMOEBOID(Feld[left_x][left_y])));
5870 boolean can_turn_right =
5871 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5872 IS_AMOEBOID(Feld[right_x][right_y])));
5874 if (can_turn_left && can_turn_right)
5875 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5876 else if (can_turn_left)
5877 MovDir[x][y] = left_dir;
5879 MovDir[x][y] = right_dir;
5882 if (MovDir[x][y] != old_move_dir)
5885 else if (element == EL_BALLOON)
5887 MovDir[x][y] = game.wind_direction;
5890 else if (element == EL_SPRING)
5892 #if USE_NEW_SPRING_BUMPER
5893 if (MovDir[x][y] & MV_HORIZONTAL)
5895 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5896 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5898 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5899 ResetGfxAnimation(move_x, move_y);
5900 DrawLevelField(move_x, move_y);
5902 MovDir[x][y] = back_dir;
5904 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5905 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5906 MovDir[x][y] = MV_NONE;
5909 if (MovDir[x][y] & MV_HORIZONTAL &&
5910 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5911 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5912 MovDir[x][y] = MV_NONE;
5917 else if (element == EL_ROBOT ||
5918 element == EL_SATELLITE ||
5919 element == EL_PENGUIN ||
5920 element == EL_EMC_ANDROID)
5922 int attr_x = -1, attr_y = -1;
5933 for (i = 0; i < MAX_PLAYERS; i++)
5935 struct PlayerInfo *player = &stored_player[i];
5936 int jx = player->jx, jy = player->jy;
5938 if (!player->active)
5942 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5950 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5951 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5952 game.engine_version < VERSION_IDENT(3,1,0,0)))
5958 if (element == EL_PENGUIN)
5961 static int xy[4][2] =
5969 for (i = 0; i < NUM_DIRECTIONS; i++)
5971 int ex = x + xy[i][0];
5972 int ey = y + xy[i][1];
5974 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5975 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5976 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5977 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5986 MovDir[x][y] = MV_NONE;
5988 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5989 else if (attr_x > x)
5990 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5992 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5993 else if (attr_y > y)
5994 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5996 if (element == EL_ROBOT)
6000 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6001 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6002 Moving2Blocked(x, y, &newx, &newy);
6004 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6005 MovDelay[x][y] = 8 + 8 * !RND(3);
6007 MovDelay[x][y] = 16;
6009 else if (element == EL_PENGUIN)
6015 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6017 boolean first_horiz = RND(2);
6018 int new_move_dir = MovDir[x][y];
6021 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6022 Moving2Blocked(x, y, &newx, &newy);
6024 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6028 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6029 Moving2Blocked(x, y, &newx, &newy);
6031 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6034 MovDir[x][y] = old_move_dir;
6038 else if (element == EL_SATELLITE)
6044 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6046 boolean first_horiz = RND(2);
6047 int new_move_dir = MovDir[x][y];
6050 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6051 Moving2Blocked(x, y, &newx, &newy);
6053 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6057 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6058 Moving2Blocked(x, y, &newx, &newy);
6060 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6063 MovDir[x][y] = old_move_dir;
6067 else if (element == EL_EMC_ANDROID)
6069 static int check_pos[16] =
6071 -1, /* 0 => (invalid) */
6072 7, /* 1 => MV_LEFT */
6073 3, /* 2 => MV_RIGHT */
6074 -1, /* 3 => (invalid) */
6076 0, /* 5 => MV_LEFT | MV_UP */
6077 2, /* 6 => MV_RIGHT | MV_UP */
6078 -1, /* 7 => (invalid) */
6079 5, /* 8 => MV_DOWN */
6080 6, /* 9 => MV_LEFT | MV_DOWN */
6081 4, /* 10 => MV_RIGHT | MV_DOWN */
6082 -1, /* 11 => (invalid) */
6083 -1, /* 12 => (invalid) */
6084 -1, /* 13 => (invalid) */
6085 -1, /* 14 => (invalid) */
6086 -1, /* 15 => (invalid) */
6094 { -1, -1, MV_LEFT | MV_UP },
6096 { +1, -1, MV_RIGHT | MV_UP },
6097 { +1, 0, MV_RIGHT },
6098 { +1, +1, MV_RIGHT | MV_DOWN },
6100 { -1, +1, MV_LEFT | MV_DOWN },
6103 int start_pos, check_order;
6104 boolean can_clone = FALSE;
6107 /* check if there is any free field around current position */
6108 for (i = 0; i < 8; i++)
6110 int newx = x + check_xy[i].dx;
6111 int newy = y + check_xy[i].dy;
6113 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6121 if (can_clone) /* randomly find an element to clone */
6125 start_pos = check_pos[RND(8)];
6126 check_order = (RND(2) ? -1 : +1);
6128 for (i = 0; i < 8; i++)
6130 int pos_raw = start_pos + i * check_order;
6131 int pos = (pos_raw + 8) % 8;
6132 int newx = x + check_xy[pos].dx;
6133 int newy = y + check_xy[pos].dy;
6135 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6137 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6138 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6140 Store[x][y] = Feld[newx][newy];
6149 if (can_clone) /* randomly find a direction to move */
6153 start_pos = check_pos[RND(8)];
6154 check_order = (RND(2) ? -1 : +1);
6156 for (i = 0; i < 8; i++)
6158 int pos_raw = start_pos + i * check_order;
6159 int pos = (pos_raw + 8) % 8;
6160 int newx = x + check_xy[pos].dx;
6161 int newy = y + check_xy[pos].dy;
6162 int new_move_dir = check_xy[pos].dir;
6164 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6166 MovDir[x][y] = new_move_dir;
6167 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6176 if (can_clone) /* cloning and moving successful */
6179 /* cannot clone -- try to move towards player */
6181 start_pos = check_pos[MovDir[x][y] & 0x0f];
6182 check_order = (RND(2) ? -1 : +1);
6184 for (i = 0; i < 3; i++)
6186 /* first check start_pos, then previous/next or (next/previous) pos */
6187 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6188 int pos = (pos_raw + 8) % 8;
6189 int newx = x + check_xy[pos].dx;
6190 int newy = y + check_xy[pos].dy;
6191 int new_move_dir = check_xy[pos].dir;
6193 if (IS_PLAYER(newx, newy))
6196 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6198 MovDir[x][y] = new_move_dir;
6199 MovDelay[x][y] = level.android_move_time * 8 + 1;
6206 else if (move_pattern == MV_TURNING_LEFT ||
6207 move_pattern == MV_TURNING_RIGHT ||
6208 move_pattern == MV_TURNING_LEFT_RIGHT ||
6209 move_pattern == MV_TURNING_RIGHT_LEFT ||
6210 move_pattern == MV_TURNING_RANDOM ||
6211 move_pattern == MV_ALL_DIRECTIONS)
6213 boolean can_turn_left =
6214 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6215 boolean can_turn_right =
6216 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6218 if (element_info[element].move_stepsize == 0) /* "not moving" */
6221 if (move_pattern == MV_TURNING_LEFT)
6222 MovDir[x][y] = left_dir;
6223 else if (move_pattern == MV_TURNING_RIGHT)
6224 MovDir[x][y] = right_dir;
6225 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6226 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6227 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6228 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6229 else if (move_pattern == MV_TURNING_RANDOM)
6230 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6231 can_turn_right && !can_turn_left ? right_dir :
6232 RND(2) ? left_dir : right_dir);
6233 else if (can_turn_left && can_turn_right)
6234 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6235 else if (can_turn_left)
6236 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6237 else if (can_turn_right)
6238 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6240 MovDir[x][y] = back_dir;
6242 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6244 else if (move_pattern == MV_HORIZONTAL ||
6245 move_pattern == MV_VERTICAL)
6247 if (move_pattern & old_move_dir)
6248 MovDir[x][y] = back_dir;
6249 else if (move_pattern == MV_HORIZONTAL)
6250 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6251 else if (move_pattern == MV_VERTICAL)
6252 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6254 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6256 else if (move_pattern & MV_ANY_DIRECTION)
6258 MovDir[x][y] = move_pattern;
6259 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6261 else if (move_pattern & MV_WIND_DIRECTION)
6263 MovDir[x][y] = game.wind_direction;
6264 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6266 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6268 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6269 MovDir[x][y] = left_dir;
6270 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6271 MovDir[x][y] = right_dir;
6273 if (MovDir[x][y] != old_move_dir)
6274 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6276 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6278 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6279 MovDir[x][y] = right_dir;
6280 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6281 MovDir[x][y] = left_dir;
6283 if (MovDir[x][y] != old_move_dir)
6284 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6286 else if (move_pattern == MV_TOWARDS_PLAYER ||
6287 move_pattern == MV_AWAY_FROM_PLAYER)
6289 int attr_x = -1, attr_y = -1;
6291 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6302 for (i = 0; i < MAX_PLAYERS; i++)
6304 struct PlayerInfo *player = &stored_player[i];
6305 int jx = player->jx, jy = player->jy;
6307 if (!player->active)
6311 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6319 MovDir[x][y] = MV_NONE;
6321 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6322 else if (attr_x > x)
6323 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6325 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6326 else if (attr_y > y)
6327 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6329 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6331 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6333 boolean first_horiz = RND(2);
6334 int new_move_dir = MovDir[x][y];
6336 if (element_info[element].move_stepsize == 0) /* "not moving" */
6338 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6339 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6345 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6346 Moving2Blocked(x, y, &newx, &newy);
6348 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6352 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6353 Moving2Blocked(x, y, &newx, &newy);
6355 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6358 MovDir[x][y] = old_move_dir;
6361 else if (move_pattern == MV_WHEN_PUSHED ||
6362 move_pattern == MV_WHEN_DROPPED)
6364 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6365 MovDir[x][y] = MV_NONE;
6369 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6371 static int test_xy[7][2] =
6381 static int test_dir[7] =
6391 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6392 int move_preference = -1000000; /* start with very low preference */
6393 int new_move_dir = MV_NONE;
6394 int start_test = RND(4);
6397 for (i = 0; i < NUM_DIRECTIONS; i++)
6399 int move_dir = test_dir[start_test + i];
6400 int move_dir_preference;
6402 xx = x + test_xy[start_test + i][0];
6403 yy = y + test_xy[start_test + i][1];
6405 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6406 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6408 new_move_dir = move_dir;
6413 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6416 move_dir_preference = -1 * RunnerVisit[xx][yy];
6417 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6418 move_dir_preference = PlayerVisit[xx][yy];
6420 if (move_dir_preference > move_preference)
6422 /* prefer field that has not been visited for the longest time */
6423 move_preference = move_dir_preference;
6424 new_move_dir = move_dir;
6426 else if (move_dir_preference == move_preference &&
6427 move_dir == old_move_dir)
6429 /* prefer last direction when all directions are preferred equally */
6430 move_preference = move_dir_preference;
6431 new_move_dir = move_dir;
6435 MovDir[x][y] = new_move_dir;
6436 if (old_move_dir != new_move_dir)
6437 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6441 static void TurnRound(int x, int y)
6443 int direction = MovDir[x][y];
6447 GfxDir[x][y] = MovDir[x][y];
6449 if (direction != MovDir[x][y])
6453 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6455 ResetGfxFrame(x, y, FALSE);
6458 static boolean JustBeingPushed(int x, int y)
6462 for (i = 0; i < MAX_PLAYERS; i++)
6464 struct PlayerInfo *player = &stored_player[i];
6466 if (player->active && player->is_pushing && player->MovPos)
6468 int next_jx = player->jx + (player->jx - player->last_jx);
6469 int next_jy = player->jy + (player->jy - player->last_jy);
6471 if (x == next_jx && y == next_jy)
6479 void StartMoving(int x, int y)
6481 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6482 int element = Feld[x][y];
6487 if (MovDelay[x][y] == 0)
6488 GfxAction[x][y] = ACTION_DEFAULT;
6490 if (CAN_FALL(element) && y < lev_fieldy - 1)
6492 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6493 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6494 if (JustBeingPushed(x, y))
6497 if (element == EL_QUICKSAND_FULL)
6499 if (IS_FREE(x, y + 1))
6501 InitMovingField(x, y, MV_DOWN);
6502 started_moving = TRUE;
6504 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6505 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6506 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6507 Store[x][y] = EL_ROCK;
6509 Store[x][y] = EL_ROCK;
6512 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6514 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6516 if (!MovDelay[x][y])
6517 MovDelay[x][y] = TILEY + 1;
6526 Feld[x][y] = EL_QUICKSAND_EMPTY;
6527 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6528 Store[x][y + 1] = Store[x][y];
6531 PlayLevelSoundAction(x, y, ACTION_FILLING);
6534 else if (element == EL_QUICKSAND_FAST_FULL)
6536 if (IS_FREE(x, y + 1))
6538 InitMovingField(x, y, MV_DOWN);
6539 started_moving = TRUE;
6541 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6542 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6543 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6544 Store[x][y] = EL_ROCK;
6546 Store[x][y] = EL_ROCK;
6549 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6551 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6553 if (!MovDelay[x][y])
6554 MovDelay[x][y] = TILEY + 1;
6563 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6564 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6565 Store[x][y + 1] = Store[x][y];
6568 PlayLevelSoundAction(x, y, ACTION_FILLING);
6571 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6572 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6574 InitMovingField(x, y, MV_DOWN);
6575 started_moving = TRUE;
6577 Feld[x][y] = EL_QUICKSAND_FILLING;
6578 Store[x][y] = element;
6580 PlayLevelSoundAction(x, y, ACTION_FILLING);
6582 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6583 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6585 InitMovingField(x, y, MV_DOWN);
6586 started_moving = TRUE;
6588 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6589 Store[x][y] = element;
6591 PlayLevelSoundAction(x, y, ACTION_FILLING);
6593 else if (element == EL_MAGIC_WALL_FULL)
6595 if (IS_FREE(x, y + 1))
6597 InitMovingField(x, y, MV_DOWN);
6598 started_moving = TRUE;
6600 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6601 Store[x][y] = EL_CHANGED(Store[x][y]);
6603 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6605 if (!MovDelay[x][y])
6606 MovDelay[x][y] = TILEY/4 + 1;
6615 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6616 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6617 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6621 else if (element == EL_BD_MAGIC_WALL_FULL)
6623 if (IS_FREE(x, y + 1))
6625 InitMovingField(x, y, MV_DOWN);
6626 started_moving = TRUE;
6628 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6629 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6631 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6633 if (!MovDelay[x][y])
6634 MovDelay[x][y] = TILEY/4 + 1;
6643 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6644 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6645 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6649 else if (element == EL_DC_MAGIC_WALL_FULL)
6651 if (IS_FREE(x, y + 1))
6653 InitMovingField(x, y, MV_DOWN);
6654 started_moving = TRUE;
6656 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6657 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6659 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6661 if (!MovDelay[x][y])
6662 MovDelay[x][y] = TILEY/4 + 1;
6671 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6672 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6673 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6677 else if ((CAN_PASS_MAGIC_WALL(element) &&
6678 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6679 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6680 (CAN_PASS_DC_MAGIC_WALL(element) &&
6681 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6684 InitMovingField(x, y, MV_DOWN);
6685 started_moving = TRUE;
6688 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6689 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6690 EL_DC_MAGIC_WALL_FILLING);
6691 Store[x][y] = element;
6693 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6695 SplashAcid(x, y + 1);
6697 InitMovingField(x, y, MV_DOWN);
6698 started_moving = TRUE;
6700 Store[x][y] = EL_ACID;
6703 #if USE_FIX_IMPACT_COLLISION
6704 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6705 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6707 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6708 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6710 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6711 CAN_FALL(element) && WasJustFalling[x][y] &&
6712 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6714 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6715 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6716 (Feld[x][y + 1] == EL_BLOCKED)))
6718 /* this is needed for a special case not covered by calling "Impact()"
6719 from "ContinueMoving()": if an element moves to a tile directly below
6720 another element which was just falling on that tile (which was empty
6721 in the previous frame), the falling element above would just stop
6722 instead of smashing the element below (in previous version, the above
6723 element was just checked for "moving" instead of "falling", resulting
6724 in incorrect smashes caused by horizontal movement of the above
6725 element; also, the case of the player being the element to smash was
6726 simply not covered here... :-/ ) */
6728 CheckCollision[x][y] = 0;
6729 CheckImpact[x][y] = 0;
6733 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6735 if (MovDir[x][y] == MV_NONE)
6737 InitMovingField(x, y, MV_DOWN);
6738 started_moving = TRUE;
6741 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6743 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6744 MovDir[x][y] = MV_DOWN;
6746 InitMovingField(x, y, MV_DOWN);
6747 started_moving = TRUE;
6749 else if (element == EL_AMOEBA_DROP)
6751 Feld[x][y] = EL_AMOEBA_GROWING;
6752 Store[x][y] = EL_AMOEBA_WET;
6754 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6755 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6756 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6757 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6759 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6760 (IS_FREE(x - 1, y + 1) ||
6761 Feld[x - 1][y + 1] == EL_ACID));
6762 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6763 (IS_FREE(x + 1, y + 1) ||
6764 Feld[x + 1][y + 1] == EL_ACID));
6765 boolean can_fall_any = (can_fall_left || can_fall_right);
6766 boolean can_fall_both = (can_fall_left && can_fall_right);
6767 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6769 #if USE_NEW_ALL_SLIPPERY
6770 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6772 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6773 can_fall_right = FALSE;
6774 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6775 can_fall_left = FALSE;
6776 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6777 can_fall_right = FALSE;
6778 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6779 can_fall_left = FALSE;
6781 can_fall_any = (can_fall_left || can_fall_right);
6782 can_fall_both = FALSE;
6785 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6787 if (slippery_type == SLIPPERY_ONLY_LEFT)
6788 can_fall_right = FALSE;
6789 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6790 can_fall_left = FALSE;
6791 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6792 can_fall_right = FALSE;
6793 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6794 can_fall_left = FALSE;
6796 can_fall_any = (can_fall_left || can_fall_right);
6797 can_fall_both = (can_fall_left && can_fall_right);
6801 #if USE_NEW_ALL_SLIPPERY
6803 #if USE_NEW_SP_SLIPPERY
6804 /* !!! better use the same properties as for custom elements here !!! */
6805 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6806 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6808 can_fall_right = FALSE; /* slip down on left side */
6809 can_fall_both = FALSE;
6814 #if USE_NEW_ALL_SLIPPERY
6817 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6818 can_fall_right = FALSE; /* slip down on left side */
6820 can_fall_left = !(can_fall_right = RND(2));
6822 can_fall_both = FALSE;
6827 if (game.emulation == EMU_BOULDERDASH ||
6828 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6829 can_fall_right = FALSE; /* slip down on left side */
6831 can_fall_left = !(can_fall_right = RND(2));
6833 can_fall_both = FALSE;
6839 /* if not determined otherwise, prefer left side for slipping down */
6840 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6841 started_moving = TRUE;
6845 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6847 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6850 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6851 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6852 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6853 int belt_dir = game.belt_dir[belt_nr];
6855 if ((belt_dir == MV_LEFT && left_is_free) ||
6856 (belt_dir == MV_RIGHT && right_is_free))
6858 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6860 InitMovingField(x, y, belt_dir);
6861 started_moving = TRUE;
6863 Pushed[x][y] = TRUE;
6864 Pushed[nextx][y] = TRUE;
6866 GfxAction[x][y] = ACTION_DEFAULT;
6870 MovDir[x][y] = 0; /* if element was moving, stop it */
6875 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6877 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6879 if (CAN_MOVE(element) && !started_moving)
6882 int move_pattern = element_info[element].move_pattern;
6887 if (MovDir[x][y] == MV_NONE)
6889 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6890 x, y, element, element_info[element].token_name);
6891 printf("StartMoving(): This should never happen!\n");
6896 Moving2Blocked(x, y, &newx, &newy);
6898 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6901 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6902 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6904 WasJustMoving[x][y] = 0;
6905 CheckCollision[x][y] = 0;
6907 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6909 if (Feld[x][y] != element) /* element has changed */
6913 if (!MovDelay[x][y]) /* start new movement phase */
6915 /* all objects that can change their move direction after each step
6916 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6918 if (element != EL_YAMYAM &&
6919 element != EL_DARK_YAMYAM &&
6920 element != EL_PACMAN &&
6921 !(move_pattern & MV_ANY_DIRECTION) &&
6922 move_pattern != MV_TURNING_LEFT &&
6923 move_pattern != MV_TURNING_RIGHT &&
6924 move_pattern != MV_TURNING_LEFT_RIGHT &&
6925 move_pattern != MV_TURNING_RIGHT_LEFT &&
6926 move_pattern != MV_TURNING_RANDOM)
6930 if (MovDelay[x][y] && (element == EL_BUG ||
6931 element == EL_SPACESHIP ||
6932 element == EL_SP_SNIKSNAK ||
6933 element == EL_SP_ELECTRON ||
6934 element == EL_MOLE))
6935 DrawLevelField(x, y);
6939 if (MovDelay[x][y]) /* wait some time before next movement */
6943 if (element == EL_ROBOT ||
6944 element == EL_YAMYAM ||
6945 element == EL_DARK_YAMYAM)
6947 DrawLevelElementAnimationIfNeeded(x, y, element);
6948 PlayLevelSoundAction(x, y, ACTION_WAITING);
6950 else if (element == EL_SP_ELECTRON)
6951 DrawLevelElementAnimationIfNeeded(x, y, element);
6952 else if (element == EL_DRAGON)
6955 int dir = MovDir[x][y];
6956 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6957 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6958 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6959 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6960 dir == MV_UP ? IMG_FLAMES_1_UP :
6961 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6962 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6964 GfxAction[x][y] = ACTION_ATTACKING;
6966 if (IS_PLAYER(x, y))
6967 DrawPlayerField(x, y);
6969 DrawLevelField(x, y);
6971 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6973 for (i = 1; i <= 3; i++)
6975 int xx = x + i * dx;
6976 int yy = y + i * dy;
6977 int sx = SCREENX(xx);
6978 int sy = SCREENY(yy);
6979 int flame_graphic = graphic + (i - 1);
6981 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6986 int flamed = MovingOrBlocked2Element(xx, yy);
6990 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6992 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6993 RemoveMovingField(xx, yy);
6995 RemoveField(xx, yy);
6997 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7000 RemoveMovingField(xx, yy);
7003 ChangeDelay[xx][yy] = 0;
7005 Feld[xx][yy] = EL_FLAMES;
7007 if (IN_SCR_FIELD(sx, sy))
7009 DrawLevelFieldCrumbledSand(xx, yy);
7010 DrawGraphic(sx, sy, flame_graphic, frame);
7015 if (Feld[xx][yy] == EL_FLAMES)
7016 Feld[xx][yy] = EL_EMPTY;
7017 DrawLevelField(xx, yy);
7022 if (MovDelay[x][y]) /* element still has to wait some time */
7024 PlayLevelSoundAction(x, y, ACTION_WAITING);
7030 /* now make next step */
7032 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7034 if (DONT_COLLIDE_WITH(element) &&
7035 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7036 !PLAYER_ENEMY_PROTECTED(newx, newy))
7038 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7043 else if (CAN_MOVE_INTO_ACID(element) &&
7044 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7045 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7046 (MovDir[x][y] == MV_DOWN ||
7047 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7049 SplashAcid(newx, newy);
7050 Store[x][y] = EL_ACID;
7052 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7054 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7055 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7056 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7057 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7060 DrawLevelField(x, y);
7062 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7063 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7064 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7066 local_player->friends_still_needed--;
7067 if (!local_player->friends_still_needed &&
7068 !local_player->GameOver && AllPlayersGone)
7069 PlayerWins(local_player);
7073 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7075 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7076 DrawLevelField(newx, newy);
7078 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7080 else if (!IS_FREE(newx, newy))
7082 GfxAction[x][y] = ACTION_WAITING;
7084 if (IS_PLAYER(x, y))
7085 DrawPlayerField(x, y);
7087 DrawLevelField(x, y);
7092 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7094 if (IS_FOOD_PIG(Feld[newx][newy]))
7096 if (IS_MOVING(newx, newy))
7097 RemoveMovingField(newx, newy);
7100 Feld[newx][newy] = EL_EMPTY;
7101 DrawLevelField(newx, newy);
7104 PlayLevelSound(x, y, SND_PIG_DIGGING);
7106 else if (!IS_FREE(newx, newy))
7108 if (IS_PLAYER(x, y))
7109 DrawPlayerField(x, y);
7111 DrawLevelField(x, y);
7116 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7118 if (Store[x][y] != EL_EMPTY)
7120 boolean can_clone = FALSE;
7123 /* check if element to clone is still there */
7124 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7126 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7134 /* cannot clone or target field not free anymore -- do not clone */
7135 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7136 Store[x][y] = EL_EMPTY;
7139 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7141 if (IS_MV_DIAGONAL(MovDir[x][y]))
7143 int diagonal_move_dir = MovDir[x][y];
7144 int stored = Store[x][y];
7145 int change_delay = 8;
7148 /* android is moving diagonally */
7150 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7152 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7153 GfxElement[x][y] = EL_EMC_ANDROID;
7154 GfxAction[x][y] = ACTION_SHRINKING;
7155 GfxDir[x][y] = diagonal_move_dir;
7156 ChangeDelay[x][y] = change_delay;
7158 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7161 DrawLevelGraphicAnimation(x, y, graphic);
7162 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7164 if (Feld[newx][newy] == EL_ACID)
7166 SplashAcid(newx, newy);
7171 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7173 Store[newx][newy] = EL_EMC_ANDROID;
7174 GfxElement[newx][newy] = EL_EMC_ANDROID;
7175 GfxAction[newx][newy] = ACTION_GROWING;
7176 GfxDir[newx][newy] = diagonal_move_dir;
7177 ChangeDelay[newx][newy] = change_delay;
7179 graphic = el_act_dir2img(GfxElement[newx][newy],
7180 GfxAction[newx][newy], GfxDir[newx][newy]);
7182 DrawLevelGraphicAnimation(newx, newy, graphic);
7183 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7189 Feld[newx][newy] = EL_EMPTY;
7190 DrawLevelField(newx, newy);
7192 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7195 else if (!IS_FREE(newx, newy))
7198 if (IS_PLAYER(x, y))
7199 DrawPlayerField(x, y);
7201 DrawLevelField(x, y);
7207 else if (IS_CUSTOM_ELEMENT(element) &&
7208 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7210 int new_element = Feld[newx][newy];
7212 if (!IS_FREE(newx, newy))
7214 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7215 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7218 /* no element can dig solid indestructible elements */
7219 if (IS_INDESTRUCTIBLE(new_element) &&
7220 !IS_DIGGABLE(new_element) &&
7221 !IS_COLLECTIBLE(new_element))
7224 if (AmoebaNr[newx][newy] &&
7225 (new_element == EL_AMOEBA_FULL ||
7226 new_element == EL_BD_AMOEBA ||
7227 new_element == EL_AMOEBA_GROWING))
7229 AmoebaCnt[AmoebaNr[newx][newy]]--;
7230 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7233 if (IS_MOVING(newx, newy))
7234 RemoveMovingField(newx, newy);
7237 RemoveField(newx, newy);
7238 DrawLevelField(newx, newy);
7241 /* if digged element was about to explode, prevent the explosion */
7242 ExplodeField[newx][newy] = EX_TYPE_NONE;
7244 PlayLevelSoundAction(x, y, action);
7247 Store[newx][newy] = EL_EMPTY;
7249 /* this makes it possible to leave the removed element again */
7250 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7251 Store[newx][newy] = new_element;
7253 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7255 int move_leave_element = element_info[element].move_leave_element;
7257 /* this makes it possible to leave the removed element again */
7258 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7259 new_element : move_leave_element);
7263 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7265 RunnerVisit[x][y] = FrameCounter;
7266 PlayerVisit[x][y] /= 8; /* expire player visit path */
7269 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7271 if (!IS_FREE(newx, newy))
7273 if (IS_PLAYER(x, y))
7274 DrawPlayerField(x, y);
7276 DrawLevelField(x, y);
7282 boolean wanna_flame = !RND(10);
7283 int dx = newx - x, dy = newy - y;
7284 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7285 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7286 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7287 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7288 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7289 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7292 IS_CLASSIC_ENEMY(element1) ||
7293 IS_CLASSIC_ENEMY(element2)) &&
7294 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7295 element1 != EL_FLAMES && element2 != EL_FLAMES)
7297 ResetGfxAnimation(x, y);
7298 GfxAction[x][y] = ACTION_ATTACKING;
7300 if (IS_PLAYER(x, y))
7301 DrawPlayerField(x, y);
7303 DrawLevelField(x, y);
7305 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7307 MovDelay[x][y] = 50;
7311 RemoveField(newx, newy);
7313 Feld[newx][newy] = EL_FLAMES;
7314 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7317 RemoveField(newx1, newy1);
7319 Feld[newx1][newy1] = EL_FLAMES;
7321 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7324 RemoveField(newx2, newy2);
7326 Feld[newx2][newy2] = EL_FLAMES;
7333 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7334 Feld[newx][newy] == EL_DIAMOND)
7336 if (IS_MOVING(newx, newy))
7337 RemoveMovingField(newx, newy);
7340 Feld[newx][newy] = EL_EMPTY;
7341 DrawLevelField(newx, newy);
7344 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7346 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7347 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7349 if (AmoebaNr[newx][newy])
7351 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7352 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7353 Feld[newx][newy] == EL_BD_AMOEBA)
7354 AmoebaCnt[AmoebaNr[newx][newy]]--;
7359 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7361 RemoveMovingField(newx, newy);
7364 if (IS_MOVING(newx, newy))
7366 RemoveMovingField(newx, newy);
7371 Feld[newx][newy] = EL_EMPTY;
7372 DrawLevelField(newx, newy);
7375 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7377 else if ((element == EL_PACMAN || element == EL_MOLE)
7378 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7380 if (AmoebaNr[newx][newy])
7382 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7383 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7384 Feld[newx][newy] == EL_BD_AMOEBA)
7385 AmoebaCnt[AmoebaNr[newx][newy]]--;
7388 if (element == EL_MOLE)
7390 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7391 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7393 ResetGfxAnimation(x, y);
7394 GfxAction[x][y] = ACTION_DIGGING;
7395 DrawLevelField(x, y);
7397 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7399 return; /* wait for shrinking amoeba */
7401 else /* element == EL_PACMAN */
7403 Feld[newx][newy] = EL_EMPTY;
7404 DrawLevelField(newx, newy);
7405 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7408 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7409 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7410 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7412 /* wait for shrinking amoeba to completely disappear */
7415 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7417 /* object was running against a wall */
7422 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7423 if (move_pattern & MV_ANY_DIRECTION &&
7424 move_pattern == MovDir[x][y])
7426 int blocking_element =
7427 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7429 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7432 element = Feld[x][y]; /* element might have changed */
7436 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7437 DrawLevelElementAnimation(x, y, element);
7439 if (DONT_TOUCH(element))
7440 TestIfBadThingTouchesPlayer(x, y);
7445 InitMovingField(x, y, MovDir[x][y]);
7447 PlayLevelSoundAction(x, y, ACTION_MOVING);
7451 ContinueMoving(x, y);
7454 void ContinueMoving(int x, int y)
7456 int element = Feld[x][y];
7457 struct ElementInfo *ei = &element_info[element];
7458 int direction = MovDir[x][y];
7459 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7460 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7461 int newx = x + dx, newy = y + dy;
7462 int stored = Store[x][y];
7463 int stored_new = Store[newx][newy];
7464 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7465 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7466 boolean last_line = (newy == lev_fieldy - 1);
7468 MovPos[x][y] += getElementMoveStepsize(x, y);
7470 if (pushed_by_player) /* special case: moving object pushed by player */
7471 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7473 if (ABS(MovPos[x][y]) < TILEX)
7476 int ee = Feld[x][y];
7477 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7478 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7480 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7481 x, y, ABS(MovPos[x][y]),
7483 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7486 DrawLevelField(x, y);
7488 return; /* element is still moving */
7491 /* element reached destination field */
7493 Feld[x][y] = EL_EMPTY;
7494 Feld[newx][newy] = element;
7495 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7497 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7499 element = Feld[newx][newy] = EL_ACID;
7501 else if (element == EL_MOLE)
7503 Feld[x][y] = EL_SAND;
7505 DrawLevelFieldCrumbledSandNeighbours(x, y);
7507 else if (element == EL_QUICKSAND_FILLING)
7509 element = Feld[newx][newy] = get_next_element(element);
7510 Store[newx][newy] = Store[x][y];
7512 else if (element == EL_QUICKSAND_EMPTYING)
7514 Feld[x][y] = get_next_element(element);
7515 element = Feld[newx][newy] = Store[x][y];
7517 else if (element == EL_QUICKSAND_FAST_FILLING)
7519 element = Feld[newx][newy] = get_next_element(element);
7520 Store[newx][newy] = Store[x][y];
7522 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7524 Feld[x][y] = get_next_element(element);
7525 element = Feld[newx][newy] = Store[x][y];
7527 else if (element == EL_MAGIC_WALL_FILLING)
7529 element = Feld[newx][newy] = get_next_element(element);
7530 if (!game.magic_wall_active)
7531 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7532 Store[newx][newy] = Store[x][y];
7534 else if (element == EL_MAGIC_WALL_EMPTYING)
7536 Feld[x][y] = get_next_element(element);
7537 if (!game.magic_wall_active)
7538 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7539 element = Feld[newx][newy] = Store[x][y];
7541 #if USE_NEW_CUSTOM_VALUE
7542 InitField(newx, newy, FALSE);
7545 else if (element == EL_BD_MAGIC_WALL_FILLING)
7547 element = Feld[newx][newy] = get_next_element(element);
7548 if (!game.magic_wall_active)
7549 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7550 Store[newx][newy] = Store[x][y];
7552 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7554 Feld[x][y] = get_next_element(element);
7555 if (!game.magic_wall_active)
7556 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7557 element = Feld[newx][newy] = Store[x][y];
7559 #if USE_NEW_CUSTOM_VALUE
7560 InitField(newx, newy, FALSE);
7563 else if (element == EL_DC_MAGIC_WALL_FILLING)
7565 element = Feld[newx][newy] = get_next_element(element);
7566 if (!game.magic_wall_active)
7567 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7568 Store[newx][newy] = Store[x][y];
7570 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7572 Feld[x][y] = get_next_element(element);
7573 if (!game.magic_wall_active)
7574 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7575 element = Feld[newx][newy] = Store[x][y];
7577 #if USE_NEW_CUSTOM_VALUE
7578 InitField(newx, newy, FALSE);
7581 else if (element == EL_AMOEBA_DROPPING)
7583 Feld[x][y] = get_next_element(element);
7584 element = Feld[newx][newy] = Store[x][y];
7586 else if (element == EL_SOKOBAN_OBJECT)
7589 Feld[x][y] = Back[x][y];
7591 if (Back[newx][newy])
7592 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7594 Back[x][y] = Back[newx][newy] = 0;
7597 Store[x][y] = EL_EMPTY;
7602 MovDelay[newx][newy] = 0;
7604 if (CAN_CHANGE_OR_HAS_ACTION(element))
7606 /* copy element change control values to new field */
7607 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7608 ChangePage[newx][newy] = ChangePage[x][y];
7609 ChangeCount[newx][newy] = ChangeCount[x][y];
7610 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7613 #if USE_NEW_CUSTOM_VALUE
7614 CustomValue[newx][newy] = CustomValue[x][y];
7617 ChangeDelay[x][y] = 0;
7618 ChangePage[x][y] = -1;
7619 ChangeCount[x][y] = 0;
7620 ChangeEvent[x][y] = -1;
7622 #if USE_NEW_CUSTOM_VALUE
7623 CustomValue[x][y] = 0;
7626 /* copy animation control values to new field */
7627 GfxFrame[newx][newy] = GfxFrame[x][y];
7628 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7629 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7630 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7632 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7634 /* some elements can leave other elements behind after moving */
7636 if (ei->move_leave_element != EL_EMPTY &&
7637 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7638 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7640 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7641 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7642 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7645 int move_leave_element = ei->move_leave_element;
7649 /* this makes it possible to leave the removed element again */
7650 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7651 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7653 /* this makes it possible to leave the removed element again */
7654 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7655 move_leave_element = stored;
7658 /* this makes it possible to leave the removed element again */
7659 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7660 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7661 move_leave_element = stored;
7664 Feld[x][y] = move_leave_element;
7666 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7667 MovDir[x][y] = direction;
7669 InitField(x, y, FALSE);
7671 if (GFX_CRUMBLED(Feld[x][y]))
7672 DrawLevelFieldCrumbledSandNeighbours(x, y);
7674 if (ELEM_IS_PLAYER(move_leave_element))
7675 RelocatePlayer(x, y, move_leave_element);
7678 /* do this after checking for left-behind element */
7679 ResetGfxAnimation(x, y); /* reset animation values for old field */
7681 if (!CAN_MOVE(element) ||
7682 (CAN_FALL(element) && direction == MV_DOWN &&
7683 (element == EL_SPRING ||
7684 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7685 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7686 GfxDir[x][y] = MovDir[newx][newy] = 0;
7688 DrawLevelField(x, y);
7689 DrawLevelField(newx, newy);
7691 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7693 /* prevent pushed element from moving on in pushed direction */
7694 if (pushed_by_player && CAN_MOVE(element) &&
7695 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7696 !(element_info[element].move_pattern & direction))
7697 TurnRound(newx, newy);
7699 /* prevent elements on conveyor belt from moving on in last direction */
7700 if (pushed_by_conveyor && CAN_FALL(element) &&
7701 direction & MV_HORIZONTAL)
7702 MovDir[newx][newy] = 0;
7704 if (!pushed_by_player)
7706 int nextx = newx + dx, nexty = newy + dy;
7707 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7709 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7711 if (CAN_FALL(element) && direction == MV_DOWN)
7712 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7714 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7715 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7717 #if USE_FIX_IMPACT_COLLISION
7718 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7719 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7723 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7725 TestIfBadThingTouchesPlayer(newx, newy);
7726 TestIfBadThingTouchesFriend(newx, newy);
7728 if (!IS_CUSTOM_ELEMENT(element))
7729 TestIfBadThingTouchesOtherBadThing(newx, newy);
7731 else if (element == EL_PENGUIN)
7732 TestIfFriendTouchesBadThing(newx, newy);
7734 /* give the player one last chance (one more frame) to move away */
7735 if (CAN_FALL(element) && direction == MV_DOWN &&
7736 (last_line || (!IS_FREE(x, newy + 1) &&
7737 (!IS_PLAYER(x, newy + 1) ||
7738 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7741 if (pushed_by_player && !game.use_change_when_pushing_bug)
7743 int push_side = MV_DIR_OPPOSITE(direction);
7744 struct PlayerInfo *player = PLAYERINFO(x, y);
7746 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7747 player->index_bit, push_side);
7748 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7749 player->index_bit, push_side);
7752 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7753 MovDelay[newx][newy] = 1;
7755 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7757 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7760 if (ChangePage[newx][newy] != -1) /* delayed change */
7762 int page = ChangePage[newx][newy];
7763 struct ElementChangeInfo *change = &ei->change_page[page];
7765 ChangePage[newx][newy] = -1;
7767 if (change->can_change)
7769 if (ChangeElement(newx, newy, element, page))
7771 if (change->post_change_function)
7772 change->post_change_function(newx, newy);
7776 if (change->has_action)
7777 ExecuteCustomElementAction(newx, newy, element, page);
7781 TestIfElementHitsCustomElement(newx, newy, direction);
7782 TestIfPlayerTouchesCustomElement(newx, newy);
7783 TestIfElementTouchesCustomElement(newx, newy);
7785 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7786 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7787 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7788 MV_DIR_OPPOSITE(direction));
7791 int AmoebeNachbarNr(int ax, int ay)
7794 int element = Feld[ax][ay];
7796 static int xy[4][2] =
7804 for (i = 0; i < NUM_DIRECTIONS; i++)
7806 int x = ax + xy[i][0];
7807 int y = ay + xy[i][1];
7809 if (!IN_LEV_FIELD(x, y))
7812 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7813 group_nr = AmoebaNr[x][y];
7819 void AmoebenVereinigen(int ax, int ay)
7821 int i, x, y, xx, yy;
7822 int new_group_nr = AmoebaNr[ax][ay];
7823 static int xy[4][2] =
7831 if (new_group_nr == 0)
7834 for (i = 0; i < NUM_DIRECTIONS; i++)
7839 if (!IN_LEV_FIELD(x, y))
7842 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7843 Feld[x][y] == EL_BD_AMOEBA ||
7844 Feld[x][y] == EL_AMOEBA_DEAD) &&
7845 AmoebaNr[x][y] != new_group_nr)
7847 int old_group_nr = AmoebaNr[x][y];
7849 if (old_group_nr == 0)
7852 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7853 AmoebaCnt[old_group_nr] = 0;
7854 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7855 AmoebaCnt2[old_group_nr] = 0;
7857 SCAN_PLAYFIELD(xx, yy)
7859 if (AmoebaNr[xx][yy] == old_group_nr)
7860 AmoebaNr[xx][yy] = new_group_nr;
7866 void AmoebeUmwandeln(int ax, int ay)
7870 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7872 int group_nr = AmoebaNr[ax][ay];
7877 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7878 printf("AmoebeUmwandeln(): This should never happen!\n");
7883 SCAN_PLAYFIELD(x, y)
7885 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7888 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7892 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7893 SND_AMOEBA_TURNING_TO_GEM :
7894 SND_AMOEBA_TURNING_TO_ROCK));
7899 static int xy[4][2] =
7907 for (i = 0; i < NUM_DIRECTIONS; i++)
7912 if (!IN_LEV_FIELD(x, y))
7915 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7917 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7918 SND_AMOEBA_TURNING_TO_GEM :
7919 SND_AMOEBA_TURNING_TO_ROCK));
7926 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7929 int group_nr = AmoebaNr[ax][ay];
7930 boolean done = FALSE;
7935 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7936 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7941 SCAN_PLAYFIELD(x, y)
7943 if (AmoebaNr[x][y] == group_nr &&
7944 (Feld[x][y] == EL_AMOEBA_DEAD ||
7945 Feld[x][y] == EL_BD_AMOEBA ||
7946 Feld[x][y] == EL_AMOEBA_GROWING))
7949 Feld[x][y] = new_element;
7950 InitField(x, y, FALSE);
7951 DrawLevelField(x, y);
7957 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7958 SND_BD_AMOEBA_TURNING_TO_ROCK :
7959 SND_BD_AMOEBA_TURNING_TO_GEM));
7962 void AmoebeWaechst(int x, int y)
7964 static unsigned long sound_delay = 0;
7965 static unsigned long sound_delay_value = 0;
7967 if (!MovDelay[x][y]) /* start new growing cycle */
7971 if (DelayReached(&sound_delay, sound_delay_value))
7973 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7974 sound_delay_value = 30;
7978 if (MovDelay[x][y]) /* wait some time before growing bigger */
7981 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7983 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7984 6 - MovDelay[x][y]);
7986 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7989 if (!MovDelay[x][y])
7991 Feld[x][y] = Store[x][y];
7993 DrawLevelField(x, y);
7998 void AmoebaDisappearing(int x, int y)
8000 static unsigned long sound_delay = 0;
8001 static unsigned long sound_delay_value = 0;
8003 if (!MovDelay[x][y]) /* start new shrinking cycle */
8007 if (DelayReached(&sound_delay, sound_delay_value))
8008 sound_delay_value = 30;
8011 if (MovDelay[x][y]) /* wait some time before shrinking */
8014 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8016 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8017 6 - MovDelay[x][y]);
8019 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8022 if (!MovDelay[x][y])
8024 Feld[x][y] = EL_EMPTY;
8025 DrawLevelField(x, y);
8027 /* don't let mole enter this field in this cycle;
8028 (give priority to objects falling to this field from above) */
8034 void AmoebeAbleger(int ax, int ay)
8037 int element = Feld[ax][ay];
8038 int graphic = el2img(element);
8039 int newax = ax, neway = ay;
8040 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8041 static int xy[4][2] =
8049 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8051 Feld[ax][ay] = EL_AMOEBA_DEAD;
8052 DrawLevelField(ax, ay);
8056 if (IS_ANIMATED(graphic))
8057 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8059 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8060 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8062 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8065 if (MovDelay[ax][ay])
8069 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8072 int x = ax + xy[start][0];
8073 int y = ay + xy[start][1];
8075 if (!IN_LEV_FIELD(x, y))
8078 if (IS_FREE(x, y) ||
8079 CAN_GROW_INTO(Feld[x][y]) ||
8080 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8081 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8087 if (newax == ax && neway == ay)
8090 else /* normal or "filled" (BD style) amoeba */
8093 boolean waiting_for_player = FALSE;
8095 for (i = 0; i < NUM_DIRECTIONS; i++)
8097 int j = (start + i) % 4;
8098 int x = ax + xy[j][0];
8099 int y = ay + xy[j][1];
8101 if (!IN_LEV_FIELD(x, y))
8104 if (IS_FREE(x, y) ||
8105 CAN_GROW_INTO(Feld[x][y]) ||
8106 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8107 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8113 else if (IS_PLAYER(x, y))
8114 waiting_for_player = TRUE;
8117 if (newax == ax && neway == ay) /* amoeba cannot grow */
8119 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8121 Feld[ax][ay] = EL_AMOEBA_DEAD;
8122 DrawLevelField(ax, ay);
8123 AmoebaCnt[AmoebaNr[ax][ay]]--;
8125 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8127 if (element == EL_AMOEBA_FULL)
8128 AmoebeUmwandeln(ax, ay);
8129 else if (element == EL_BD_AMOEBA)
8130 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8135 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8137 /* amoeba gets larger by growing in some direction */
8139 int new_group_nr = AmoebaNr[ax][ay];
8142 if (new_group_nr == 0)
8144 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8145 printf("AmoebeAbleger(): This should never happen!\n");
8150 AmoebaNr[newax][neway] = new_group_nr;
8151 AmoebaCnt[new_group_nr]++;
8152 AmoebaCnt2[new_group_nr]++;
8154 /* if amoeba touches other amoeba(s) after growing, unify them */
8155 AmoebenVereinigen(newax, neway);
8157 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8159 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8165 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8166 (neway == lev_fieldy - 1 && newax != ax))
8168 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8169 Store[newax][neway] = element;
8171 else if (neway == ay || element == EL_EMC_DRIPPER)
8173 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8175 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8179 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8180 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8181 Store[ax][ay] = EL_AMOEBA_DROP;
8182 ContinueMoving(ax, ay);
8186 DrawLevelField(newax, neway);
8189 void Life(int ax, int ay)
8193 int element = Feld[ax][ay];
8194 int graphic = el2img(element);
8195 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8197 boolean changed = FALSE;
8199 if (IS_ANIMATED(graphic))
8200 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8205 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8206 MovDelay[ax][ay] = life_time;
8208 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8211 if (MovDelay[ax][ay])
8215 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8217 int xx = ax+x1, yy = ay+y1;
8220 if (!IN_LEV_FIELD(xx, yy))
8223 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8225 int x = xx+x2, y = yy+y2;
8227 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8230 if (((Feld[x][y] == element ||
8231 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8233 (IS_FREE(x, y) && Stop[x][y]))
8237 if (xx == ax && yy == ay) /* field in the middle */
8239 if (nachbarn < life_parameter[0] ||
8240 nachbarn > life_parameter[1])
8242 Feld[xx][yy] = EL_EMPTY;
8244 DrawLevelField(xx, yy);
8245 Stop[xx][yy] = TRUE;
8249 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8250 { /* free border field */
8251 if (nachbarn >= life_parameter[2] &&
8252 nachbarn <= life_parameter[3])
8254 Feld[xx][yy] = element;
8255 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8257 DrawLevelField(xx, yy);
8258 Stop[xx][yy] = TRUE;
8265 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8266 SND_GAME_OF_LIFE_GROWING);
8269 static void InitRobotWheel(int x, int y)
8271 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8274 static void RunRobotWheel(int x, int y)
8276 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8279 static void StopRobotWheel(int x, int y)
8281 if (ZX == x && ZY == y)
8285 static void InitTimegateWheel(int x, int y)
8287 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8290 static void RunTimegateWheel(int x, int y)
8292 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8295 static void InitMagicBallDelay(int x, int y)
8298 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8300 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8304 static void ActivateMagicBall(int bx, int by)
8308 if (level.ball_random)
8310 int pos_border = RND(8); /* select one of the eight border elements */
8311 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8312 int xx = pos_content % 3;
8313 int yy = pos_content / 3;
8318 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8319 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8323 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8325 int xx = x - bx + 1;
8326 int yy = y - by + 1;
8328 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8329 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8333 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8336 void CheckExit(int x, int y)
8338 if (local_player->gems_still_needed > 0 ||
8339 local_player->sokobanfields_still_needed > 0 ||
8340 local_player->lights_still_needed > 0)
8342 int element = Feld[x][y];
8343 int graphic = el2img(element);
8345 if (IS_ANIMATED(graphic))
8346 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8351 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8354 Feld[x][y] = EL_EXIT_OPENING;
8356 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8359 void CheckExitEM(int x, int y)
8361 if (local_player->gems_still_needed > 0 ||
8362 local_player->sokobanfields_still_needed > 0 ||
8363 local_player->lights_still_needed > 0)
8365 int element = Feld[x][y];
8366 int graphic = el2img(element);
8368 if (IS_ANIMATED(graphic))
8369 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8374 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8377 Feld[x][y] = EL_EM_EXIT_OPENING;
8379 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8382 void CheckExitSteel(int x, int y)
8384 if (local_player->gems_still_needed > 0 ||
8385 local_player->sokobanfields_still_needed > 0 ||
8386 local_player->lights_still_needed > 0)
8388 int element = Feld[x][y];
8389 int graphic = el2img(element);
8391 if (IS_ANIMATED(graphic))
8392 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8397 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8400 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8402 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8405 void CheckExitSteelEM(int x, int y)
8407 if (local_player->gems_still_needed > 0 ||
8408 local_player->sokobanfields_still_needed > 0 ||
8409 local_player->lights_still_needed > 0)
8411 int element = Feld[x][y];
8412 int graphic = el2img(element);
8414 if (IS_ANIMATED(graphic))
8415 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8420 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8423 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8425 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8428 void CheckExitSP(int x, int y)
8430 if (local_player->gems_still_needed > 0)
8432 int element = Feld[x][y];
8433 int graphic = el2img(element);
8435 if (IS_ANIMATED(graphic))
8436 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8441 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8444 Feld[x][y] = EL_SP_EXIT_OPENING;
8446 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8449 static void CloseAllOpenTimegates()
8453 SCAN_PLAYFIELD(x, y)
8455 int element = Feld[x][y];
8457 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8459 Feld[x][y] = EL_TIMEGATE_CLOSING;
8461 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8466 void DrawTwinkleOnField(int x, int y)
8468 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8471 if (Feld[x][y] == EL_BD_DIAMOND)
8474 if (MovDelay[x][y] == 0) /* next animation frame */
8475 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8477 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8481 if (setup.direct_draw && MovDelay[x][y])
8482 SetDrawtoField(DRAW_BUFFERED);
8484 DrawLevelElementAnimation(x, y, Feld[x][y]);
8486 if (MovDelay[x][y] != 0)
8488 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8489 10 - MovDelay[x][y]);
8491 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8493 if (setup.direct_draw)
8497 dest_x = FX + SCREENX(x) * TILEX;
8498 dest_y = FY + SCREENY(y) * TILEY;
8500 BlitBitmap(drawto_field, window,
8501 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8502 SetDrawtoField(DRAW_DIRECT);
8508 void MauerWaechst(int x, int y)
8512 if (!MovDelay[x][y]) /* next animation frame */
8513 MovDelay[x][y] = 3 * delay;
8515 if (MovDelay[x][y]) /* wait some time before next frame */
8519 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8521 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8522 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8524 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8527 if (!MovDelay[x][y])
8529 if (MovDir[x][y] == MV_LEFT)
8531 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8532 DrawLevelField(x - 1, y);
8534 else if (MovDir[x][y] == MV_RIGHT)
8536 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8537 DrawLevelField(x + 1, y);
8539 else if (MovDir[x][y] == MV_UP)
8541 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8542 DrawLevelField(x, y - 1);
8546 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8547 DrawLevelField(x, y + 1);
8550 Feld[x][y] = Store[x][y];
8552 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8553 DrawLevelField(x, y);
8558 void MauerAbleger(int ax, int ay)
8560 int element = Feld[ax][ay];
8561 int graphic = el2img(element);
8562 boolean oben_frei = FALSE, unten_frei = FALSE;
8563 boolean links_frei = FALSE, rechts_frei = FALSE;
8564 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8565 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8566 boolean new_wall = FALSE;
8568 if (IS_ANIMATED(graphic))
8569 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8571 if (!MovDelay[ax][ay]) /* start building new wall */
8572 MovDelay[ax][ay] = 6;
8574 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8577 if (MovDelay[ax][ay])
8581 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8583 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8585 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8587 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8590 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8591 element == EL_EXPANDABLE_WALL_ANY)
8595 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8596 Store[ax][ay-1] = element;
8597 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8598 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8599 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8600 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8605 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8606 Store[ax][ay+1] = element;
8607 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8608 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8609 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8610 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8615 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8616 element == EL_EXPANDABLE_WALL_ANY ||
8617 element == EL_EXPANDABLE_WALL ||
8618 element == EL_BD_EXPANDABLE_WALL)
8622 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8623 Store[ax-1][ay] = element;
8624 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8625 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8626 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8627 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8633 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8634 Store[ax+1][ay] = element;
8635 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8636 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8637 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8638 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8643 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8644 DrawLevelField(ax, ay);
8646 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8648 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8649 unten_massiv = TRUE;
8650 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8651 links_massiv = TRUE;
8652 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8653 rechts_massiv = TRUE;
8655 if (((oben_massiv && unten_massiv) ||
8656 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8657 element == EL_EXPANDABLE_WALL) &&
8658 ((links_massiv && rechts_massiv) ||
8659 element == EL_EXPANDABLE_WALL_VERTICAL))
8660 Feld[ax][ay] = EL_WALL;
8663 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8666 void MauerAblegerStahl(int ax, int ay)
8668 int element = Feld[ax][ay];
8669 int graphic = el2img(element);
8670 boolean oben_frei = FALSE, unten_frei = FALSE;
8671 boolean links_frei = FALSE, rechts_frei = FALSE;
8672 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8673 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8674 boolean new_wall = FALSE;
8676 if (IS_ANIMATED(graphic))
8677 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8679 if (!MovDelay[ax][ay]) /* start building new wall */
8680 MovDelay[ax][ay] = 6;
8682 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8685 if (MovDelay[ax][ay])
8689 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8691 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8693 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8695 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8698 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8699 element == EL_EXPANDABLE_STEELWALL_ANY)
8703 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8704 Store[ax][ay-1] = element;
8705 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8706 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8707 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8708 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8713 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8714 Store[ax][ay+1] = element;
8715 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8716 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8717 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8718 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8723 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8724 element == EL_EXPANDABLE_STEELWALL_ANY)
8728 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8729 Store[ax-1][ay] = element;
8730 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8731 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8732 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8733 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8739 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8740 Store[ax+1][ay] = element;
8741 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8742 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8743 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8744 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8749 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8751 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8752 unten_massiv = TRUE;
8753 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8754 links_massiv = TRUE;
8755 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8756 rechts_massiv = TRUE;
8758 if (((oben_massiv && unten_massiv) ||
8759 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8760 ((links_massiv && rechts_massiv) ||
8761 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8762 Feld[ax][ay] = EL_WALL;
8765 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8768 void CheckForDragon(int x, int y)
8771 boolean dragon_found = FALSE;
8772 static int xy[4][2] =
8780 for (i = 0; i < NUM_DIRECTIONS; i++)
8782 for (j = 0; j < 4; j++)
8784 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8786 if (IN_LEV_FIELD(xx, yy) &&
8787 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8789 if (Feld[xx][yy] == EL_DRAGON)
8790 dragon_found = TRUE;
8799 for (i = 0; i < NUM_DIRECTIONS; i++)
8801 for (j = 0; j < 3; j++)
8803 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8805 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8807 Feld[xx][yy] = EL_EMPTY;
8808 DrawLevelField(xx, yy);
8817 static void InitBuggyBase(int x, int y)
8819 int element = Feld[x][y];
8820 int activating_delay = FRAMES_PER_SECOND / 4;
8823 (element == EL_SP_BUGGY_BASE ?
8824 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8825 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8827 element == EL_SP_BUGGY_BASE_ACTIVE ?
8828 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8831 static void WarnBuggyBase(int x, int y)
8834 static int xy[4][2] =
8842 for (i = 0; i < NUM_DIRECTIONS; i++)
8844 int xx = x + xy[i][0];
8845 int yy = y + xy[i][1];
8847 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8849 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8856 static void InitTrap(int x, int y)
8858 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8861 static void ActivateTrap(int x, int y)
8863 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8866 static void ChangeActiveTrap(int x, int y)
8868 int graphic = IMG_TRAP_ACTIVE;
8870 /* if new animation frame was drawn, correct crumbled sand border */
8871 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8872 DrawLevelFieldCrumbledSand(x, y);
8875 static int getSpecialActionElement(int element, int number, int base_element)
8877 return (element != EL_EMPTY ? element :
8878 number != -1 ? base_element + number - 1 :
8882 static int getModifiedActionNumber(int value_old, int operator, int operand,
8883 int value_min, int value_max)
8885 int value_new = (operator == CA_MODE_SET ? operand :
8886 operator == CA_MODE_ADD ? value_old + operand :
8887 operator == CA_MODE_SUBTRACT ? value_old - operand :
8888 operator == CA_MODE_MULTIPLY ? value_old * operand :
8889 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8890 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8893 return (value_new < value_min ? value_min :
8894 value_new > value_max ? value_max :
8898 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8900 struct ElementInfo *ei = &element_info[element];
8901 struct ElementChangeInfo *change = &ei->change_page[page];
8902 int target_element = change->target_element;
8903 int action_type = change->action_type;
8904 int action_mode = change->action_mode;
8905 int action_arg = change->action_arg;
8908 if (!change->has_action)
8911 /* ---------- determine action paramater values -------------------------- */
8913 int level_time_value =
8914 (level.time > 0 ? TimeLeft :
8917 int action_arg_element =
8918 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8919 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8920 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8923 int action_arg_direction =
8924 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8925 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8926 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8927 change->actual_trigger_side :
8928 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8929 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8932 int action_arg_number_min =
8933 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8936 int action_arg_number_max =
8937 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8938 action_type == CA_SET_LEVEL_GEMS ? 999 :
8939 action_type == CA_SET_LEVEL_TIME ? 9999 :
8940 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8941 action_type == CA_SET_CE_VALUE ? 9999 :
8942 action_type == CA_SET_CE_SCORE ? 9999 :
8945 int action_arg_number_reset =
8946 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8947 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8948 action_type == CA_SET_LEVEL_TIME ? level.time :
8949 action_type == CA_SET_LEVEL_SCORE ? 0 :
8950 #if USE_NEW_CUSTOM_VALUE
8951 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8953 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8955 action_type == CA_SET_CE_SCORE ? 0 :
8958 int action_arg_number =
8959 (action_arg <= CA_ARG_MAX ? action_arg :
8960 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8961 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8962 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8963 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8964 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8965 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8966 #if USE_NEW_CUSTOM_VALUE
8967 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8969 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8971 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8972 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8973 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8974 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8975 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8976 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8977 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8978 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8979 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8980 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8981 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8984 int action_arg_number_old =
8985 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8986 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8987 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8988 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8989 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8992 int action_arg_number_new =
8993 getModifiedActionNumber(action_arg_number_old,
8994 action_mode, action_arg_number,
8995 action_arg_number_min, action_arg_number_max);
8997 int trigger_player_bits =
8998 (change->actual_trigger_player >= EL_PLAYER_1 &&
8999 change->actual_trigger_player <= EL_PLAYER_4 ?
9000 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9003 int action_arg_player_bits =
9004 (action_arg >= CA_ARG_PLAYER_1 &&
9005 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9006 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9009 /* ---------- execute action -------------------------------------------- */
9011 switch (action_type)
9018 /* ---------- level actions ------------------------------------------- */
9020 case CA_RESTART_LEVEL:
9022 game.restart_level = TRUE;
9027 case CA_SHOW_ENVELOPE:
9029 int element = getSpecialActionElement(action_arg_element,
9030 action_arg_number, EL_ENVELOPE_1);
9032 if (IS_ENVELOPE(element))
9033 local_player->show_envelope = element;
9038 case CA_SET_LEVEL_TIME:
9040 if (level.time > 0) /* only modify limited time value */
9042 TimeLeft = action_arg_number_new;
9044 DrawGameValue_Time(TimeLeft);
9046 if (!TimeLeft && setup.time_limit)
9047 for (i = 0; i < MAX_PLAYERS; i++)
9048 KillPlayer(&stored_player[i]);
9054 case CA_SET_LEVEL_SCORE:
9056 local_player->score = action_arg_number_new;
9058 DrawGameValue_Score(local_player->score);
9063 case CA_SET_LEVEL_GEMS:
9065 local_player->gems_still_needed = action_arg_number_new;
9067 DrawGameValue_Emeralds(local_player->gems_still_needed);
9072 #if !USE_PLAYER_GRAVITY
9073 case CA_SET_LEVEL_GRAVITY:
9075 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9076 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9077 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9083 case CA_SET_LEVEL_WIND:
9085 game.wind_direction = action_arg_direction;
9090 /* ---------- player actions ------------------------------------------ */
9092 case CA_MOVE_PLAYER:
9094 /* automatically move to the next field in specified direction */
9095 for (i = 0; i < MAX_PLAYERS; i++)
9096 if (trigger_player_bits & (1 << i))
9097 stored_player[i].programmed_action = action_arg_direction;
9102 case CA_EXIT_PLAYER:
9104 for (i = 0; i < MAX_PLAYERS; i++)
9105 if (action_arg_player_bits & (1 << i))
9106 PlayerWins(&stored_player[i]);
9111 case CA_KILL_PLAYER:
9113 for (i = 0; i < MAX_PLAYERS; i++)
9114 if (action_arg_player_bits & (1 << i))
9115 KillPlayer(&stored_player[i]);
9120 case CA_SET_PLAYER_KEYS:
9122 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9123 int element = getSpecialActionElement(action_arg_element,
9124 action_arg_number, EL_KEY_1);
9126 if (IS_KEY(element))
9128 for (i = 0; i < MAX_PLAYERS; i++)
9130 if (trigger_player_bits & (1 << i))
9132 stored_player[i].key[KEY_NR(element)] = key_state;
9134 DrawGameDoorValues();
9142 case CA_SET_PLAYER_SPEED:
9144 for (i = 0; i < MAX_PLAYERS; i++)
9146 if (trigger_player_bits & (1 << i))
9148 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9150 if (action_arg == CA_ARG_SPEED_FASTER &&
9151 stored_player[i].cannot_move)
9153 action_arg_number = STEPSIZE_VERY_SLOW;
9155 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9156 action_arg == CA_ARG_SPEED_FASTER)
9158 action_arg_number = 2;
9159 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9162 else if (action_arg == CA_ARG_NUMBER_RESET)
9164 action_arg_number = level.initial_player_stepsize[i];
9168 getModifiedActionNumber(move_stepsize,
9171 action_arg_number_min,
9172 action_arg_number_max);
9174 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9181 case CA_SET_PLAYER_SHIELD:
9183 for (i = 0; i < MAX_PLAYERS; i++)
9185 if (trigger_player_bits & (1 << i))
9187 if (action_arg == CA_ARG_SHIELD_OFF)
9189 stored_player[i].shield_normal_time_left = 0;
9190 stored_player[i].shield_deadly_time_left = 0;
9192 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9194 stored_player[i].shield_normal_time_left = 999999;
9196 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9198 stored_player[i].shield_normal_time_left = 999999;
9199 stored_player[i].shield_deadly_time_left = 999999;
9207 #if USE_PLAYER_GRAVITY
9208 case CA_SET_PLAYER_GRAVITY:
9210 for (i = 0; i < MAX_PLAYERS; i++)
9212 if (trigger_player_bits & (1 << i))
9214 stored_player[i].gravity =
9215 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9216 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9217 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9218 stored_player[i].gravity);
9226 case CA_SET_PLAYER_ARTWORK:
9228 for (i = 0; i < MAX_PLAYERS; i++)
9230 if (trigger_player_bits & (1 << i))
9232 int artwork_element = action_arg_element;
9234 if (action_arg == CA_ARG_ELEMENT_RESET)
9236 (level.use_artwork_element[i] ? level.artwork_element[i] :
9237 stored_player[i].element_nr);
9239 #if USE_GFX_RESET_PLAYER_ARTWORK
9240 if (stored_player[i].artwork_element != artwork_element)
9241 stored_player[i].Frame = 0;
9244 stored_player[i].artwork_element = artwork_element;
9246 SetPlayerWaiting(&stored_player[i], FALSE);
9248 /* set number of special actions for bored and sleeping animation */
9249 stored_player[i].num_special_action_bored =
9250 get_num_special_action(artwork_element,
9251 ACTION_BORING_1, ACTION_BORING_LAST);
9252 stored_player[i].num_special_action_sleeping =
9253 get_num_special_action(artwork_element,
9254 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9261 /* ---------- CE actions ---------------------------------------------- */
9263 case CA_SET_CE_VALUE:
9265 #if USE_NEW_CUSTOM_VALUE
9266 int last_ce_value = CustomValue[x][y];
9268 CustomValue[x][y] = action_arg_number_new;
9270 if (CustomValue[x][y] != last_ce_value)
9272 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9273 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9275 if (CustomValue[x][y] == 0)
9277 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9278 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9286 case CA_SET_CE_SCORE:
9288 #if USE_NEW_CUSTOM_VALUE
9289 int last_ce_score = ei->collect_score;
9291 ei->collect_score = action_arg_number_new;
9293 if (ei->collect_score != last_ce_score)
9295 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9296 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9298 if (ei->collect_score == 0)
9302 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9303 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9306 This is a very special case that seems to be a mixture between
9307 CheckElementChange() and CheckTriggeredElementChange(): while
9308 the first one only affects single elements that are triggered
9309 directly, the second one affects multiple elements in the playfield
9310 that are triggered indirectly by another element. This is a third
9311 case: Changing the CE score always affects multiple identical CEs,
9312 so every affected CE must be checked, not only the single CE for
9313 which the CE score was changed in the first place (as every instance
9314 of that CE shares the same CE score, and therefore also can change)!
9316 SCAN_PLAYFIELD(xx, yy)
9318 if (Feld[xx][yy] == element)
9319 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9320 CE_SCORE_GETS_ZERO);
9329 /* ---------- engine actions ------------------------------------------ */
9331 case CA_SET_ENGINE_SCAN_MODE:
9333 InitPlayfieldScanMode(action_arg);
9343 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9345 int old_element = Feld[x][y];
9346 int new_element = GetElementFromGroupElement(element);
9347 int previous_move_direction = MovDir[x][y];
9348 #if USE_NEW_CUSTOM_VALUE
9349 int last_ce_value = CustomValue[x][y];
9351 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9352 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9353 boolean add_player_onto_element = (new_element_is_player &&
9354 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9355 /* this breaks SnakeBite when a snake is
9356 halfway through a door that closes */
9357 /* NOW FIXED AT LEVEL INIT IN files.c */
9358 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9360 IS_WALKABLE(old_element));
9363 /* check if element under the player changes from accessible to unaccessible
9364 (needed for special case of dropping element which then changes) */
9365 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9366 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9374 if (!add_player_onto_element)
9376 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9377 RemoveMovingField(x, y);
9381 Feld[x][y] = new_element;
9383 #if !USE_GFX_RESET_GFX_ANIMATION
9384 ResetGfxAnimation(x, y);
9385 ResetRandomAnimationValue(x, y);
9388 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9389 MovDir[x][y] = previous_move_direction;
9391 #if USE_NEW_CUSTOM_VALUE
9392 if (element_info[new_element].use_last_ce_value)
9393 CustomValue[x][y] = last_ce_value;
9396 InitField_WithBug1(x, y, FALSE);
9398 new_element = Feld[x][y]; /* element may have changed */
9400 #if USE_GFX_RESET_GFX_ANIMATION
9401 ResetGfxAnimation(x, y);
9402 ResetRandomAnimationValue(x, y);
9405 DrawLevelField(x, y);
9407 if (GFX_CRUMBLED(new_element))
9408 DrawLevelFieldCrumbledSandNeighbours(x, y);
9412 /* check if element under the player changes from accessible to unaccessible
9413 (needed for special case of dropping element which then changes) */
9414 /* (must be checked after creating new element for walkable group elements) */
9415 #if USE_FIX_KILLED_BY_NON_WALKABLE
9416 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9417 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9424 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9425 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9434 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9435 if (new_element_is_player)
9436 RelocatePlayer(x, y, new_element);
9439 ChangeCount[x][y]++; /* count number of changes in the same frame */
9441 TestIfBadThingTouchesPlayer(x, y);
9442 TestIfPlayerTouchesCustomElement(x, y);
9443 TestIfElementTouchesCustomElement(x, y);
9446 static void CreateField(int x, int y, int element)
9448 CreateFieldExt(x, y, element, FALSE);
9451 static void CreateElementFromChange(int x, int y, int element)
9453 element = GET_VALID_RUNTIME_ELEMENT(element);
9455 #if USE_STOP_CHANGED_ELEMENTS
9456 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9458 int old_element = Feld[x][y];
9460 /* prevent changed element from moving in same engine frame
9461 unless both old and new element can either fall or move */
9462 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9463 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9468 CreateFieldExt(x, y, element, TRUE);
9471 static boolean ChangeElement(int x, int y, int element, int page)
9473 struct ElementInfo *ei = &element_info[element];
9474 struct ElementChangeInfo *change = &ei->change_page[page];
9475 int ce_value = CustomValue[x][y];
9476 int ce_score = ei->collect_score;
9478 int old_element = Feld[x][y];
9480 /* always use default change event to prevent running into a loop */
9481 if (ChangeEvent[x][y] == -1)
9482 ChangeEvent[x][y] = CE_DELAY;
9484 if (ChangeEvent[x][y] == CE_DELAY)
9486 /* reset actual trigger element, trigger player and action element */
9487 change->actual_trigger_element = EL_EMPTY;
9488 change->actual_trigger_player = EL_PLAYER_1;
9489 change->actual_trigger_side = CH_SIDE_NONE;
9490 change->actual_trigger_ce_value = 0;
9491 change->actual_trigger_ce_score = 0;
9494 /* do not change elements more than a specified maximum number of changes */
9495 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9498 ChangeCount[x][y]++; /* count number of changes in the same frame */
9500 if (change->explode)
9507 if (change->use_target_content)
9509 boolean complete_replace = TRUE;
9510 boolean can_replace[3][3];
9513 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9516 boolean is_walkable;
9517 boolean is_diggable;
9518 boolean is_collectible;
9519 boolean is_removable;
9520 boolean is_destructible;
9521 int ex = x + xx - 1;
9522 int ey = y + yy - 1;
9523 int content_element = change->target_content.e[xx][yy];
9526 can_replace[xx][yy] = TRUE;
9528 if (ex == x && ey == y) /* do not check changing element itself */
9531 if (content_element == EL_EMPTY_SPACE)
9533 can_replace[xx][yy] = FALSE; /* do not replace border with space */
9538 if (!IN_LEV_FIELD(ex, ey))
9540 can_replace[xx][yy] = FALSE;
9541 complete_replace = FALSE;
9548 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9549 e = MovingOrBlocked2Element(ex, ey);
9551 is_empty = (IS_FREE(ex, ey) ||
9552 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9554 is_walkable = (is_empty || IS_WALKABLE(e));
9555 is_diggable = (is_empty || IS_DIGGABLE(e));
9556 is_collectible = (is_empty || IS_COLLECTIBLE(e));
9557 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9558 is_removable = (is_diggable || is_collectible);
9560 can_replace[xx][yy] =
9561 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9562 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9563 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9564 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9565 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9566 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9567 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9569 if (!can_replace[xx][yy])
9570 complete_replace = FALSE;
9573 if (!change->only_if_complete || complete_replace)
9575 boolean something_has_changed = FALSE;
9577 if (change->only_if_complete && change->use_random_replace &&
9578 RND(100) < change->random_percentage)
9581 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9583 int ex = x + xx - 1;
9584 int ey = y + yy - 1;
9585 int content_element;
9587 if (can_replace[xx][yy] && (!change->use_random_replace ||
9588 RND(100) < change->random_percentage))
9590 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9591 RemoveMovingField(ex, ey);
9593 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9595 content_element = change->target_content.e[xx][yy];
9596 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9597 ce_value, ce_score);
9599 CreateElementFromChange(ex, ey, target_element);
9601 something_has_changed = TRUE;
9603 /* for symmetry reasons, freeze newly created border elements */
9604 if (ex != x || ey != y)
9605 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9609 if (something_has_changed)
9611 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9612 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9618 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9619 ce_value, ce_score);
9621 if (element == EL_DIAGONAL_GROWING ||
9622 element == EL_DIAGONAL_SHRINKING)
9624 target_element = Store[x][y];
9626 Store[x][y] = EL_EMPTY;
9629 CreateElementFromChange(x, y, target_element);
9631 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9632 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9635 /* this uses direct change before indirect change */
9636 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9641 #if USE_NEW_DELAYED_ACTION
9643 static void HandleElementChange(int x, int y, int page)
9645 int element = MovingOrBlocked2Element(x, y);
9646 struct ElementInfo *ei = &element_info[element];
9647 struct ElementChangeInfo *change = &ei->change_page[page];
9650 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9651 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9654 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9655 x, y, element, element_info[element].token_name);
9656 printf("HandleElementChange(): This should never happen!\n");
9661 /* this can happen with classic bombs on walkable, changing elements */
9662 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9665 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9666 ChangeDelay[x][y] = 0;
9672 if (ChangeDelay[x][y] == 0) /* initialize element change */
9674 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9676 if (change->can_change)
9679 /* !!! not clear why graphic animation should be reset at all here !!! */
9680 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9681 #if USE_GFX_RESET_WHEN_NOT_MOVING
9682 /* when a custom element is about to change (for example by change delay),
9683 do not reset graphic animation when the custom element is moving */
9684 if (!IS_MOVING(x, y))
9687 ResetGfxAnimation(x, y);
9688 ResetRandomAnimationValue(x, y);
9692 if (change->pre_change_function)
9693 change->pre_change_function(x, y);
9697 ChangeDelay[x][y]--;
9699 if (ChangeDelay[x][y] != 0) /* continue element change */
9701 if (change->can_change)
9703 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9705 if (IS_ANIMATED(graphic))
9706 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9708 if (change->change_function)
9709 change->change_function(x, y);
9712 else /* finish element change */
9714 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9716 page = ChangePage[x][y];
9717 ChangePage[x][y] = -1;
9719 change = &ei->change_page[page];
9722 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9724 ChangeDelay[x][y] = 1; /* try change after next move step */
9725 ChangePage[x][y] = page; /* remember page to use for change */
9730 if (change->can_change)
9732 if (ChangeElement(x, y, element, page))
9734 if (change->post_change_function)
9735 change->post_change_function(x, y);
9739 if (change->has_action)
9740 ExecuteCustomElementAction(x, y, element, page);
9746 static void HandleElementChange(int x, int y, int page)
9748 int element = MovingOrBlocked2Element(x, y);
9749 struct ElementInfo *ei = &element_info[element];
9750 struct ElementChangeInfo *change = &ei->change_page[page];
9753 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9756 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9757 x, y, element, element_info[element].token_name);
9758 printf("HandleElementChange(): This should never happen!\n");
9763 /* this can happen with classic bombs on walkable, changing elements */
9764 if (!CAN_CHANGE(element))
9767 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9768 ChangeDelay[x][y] = 0;
9774 if (ChangeDelay[x][y] == 0) /* initialize element change */
9776 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9778 ResetGfxAnimation(x, y);
9779 ResetRandomAnimationValue(x, y);
9781 if (change->pre_change_function)
9782 change->pre_change_function(x, y);
9785 ChangeDelay[x][y]--;
9787 if (ChangeDelay[x][y] != 0) /* continue element change */
9789 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9791 if (IS_ANIMATED(graphic))
9792 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9794 if (change->change_function)
9795 change->change_function(x, y);
9797 else /* finish element change */
9799 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9801 page = ChangePage[x][y];
9802 ChangePage[x][y] = -1;
9804 change = &ei->change_page[page];
9807 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9809 ChangeDelay[x][y] = 1; /* try change after next move step */
9810 ChangePage[x][y] = page; /* remember page to use for change */
9815 if (ChangeElement(x, y, element, page))
9817 if (change->post_change_function)
9818 change->post_change_function(x, y);
9825 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9826 int trigger_element,
9832 boolean change_done_any = FALSE;
9833 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9836 if (!(trigger_events[trigger_element][trigger_event]))
9840 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9841 trigger_event, recursion_loop_depth, recursion_loop_detected,
9842 recursion_loop_element, EL_NAME(recursion_loop_element));
9845 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9847 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9849 int element = EL_CUSTOM_START + i;
9850 boolean change_done = FALSE;
9853 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9854 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9857 for (p = 0; p < element_info[element].num_change_pages; p++)
9859 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9861 if (change->can_change_or_has_action &&
9862 change->has_event[trigger_event] &&
9863 change->trigger_side & trigger_side &&
9864 change->trigger_player & trigger_player &&
9865 change->trigger_page & trigger_page_bits &&
9866 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9868 change->actual_trigger_element = trigger_element;
9869 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9870 change->actual_trigger_side = trigger_side;
9871 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9872 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9874 if ((change->can_change && !change_done) || change->has_action)
9878 SCAN_PLAYFIELD(x, y)
9880 if (Feld[x][y] == element)
9882 if (change->can_change && !change_done)
9884 ChangeDelay[x][y] = 1;
9885 ChangeEvent[x][y] = trigger_event;
9887 HandleElementChange(x, y, p);
9889 #if USE_NEW_DELAYED_ACTION
9890 else if (change->has_action)
9892 ExecuteCustomElementAction(x, y, element, p);
9893 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9896 if (change->has_action)
9898 ExecuteCustomElementAction(x, y, element, p);
9899 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9905 if (change->can_change)
9908 change_done_any = TRUE;
9915 RECURSION_LOOP_DETECTION_END();
9917 return change_done_any;
9920 static boolean CheckElementChangeExt(int x, int y,
9922 int trigger_element,
9927 boolean change_done = FALSE;
9930 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9931 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9934 if (Feld[x][y] == EL_BLOCKED)
9936 Blocked2Moving(x, y, &x, &y);
9937 element = Feld[x][y];
9941 /* check if element has already changed */
9942 if (Feld[x][y] != element)
9945 /* check if element has already changed or is about to change after moving */
9946 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9947 Feld[x][y] != element) ||
9949 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9950 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9951 ChangePage[x][y] != -1)))
9956 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9957 trigger_event, recursion_loop_depth, recursion_loop_detected,
9958 recursion_loop_element, EL_NAME(recursion_loop_element));
9961 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9963 for (p = 0; p < element_info[element].num_change_pages; p++)
9965 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9967 /* check trigger element for all events where the element that is checked
9968 for changing interacts with a directly adjacent element -- this is
9969 different to element changes that affect other elements to change on the
9970 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9971 boolean check_trigger_element =
9972 (trigger_event == CE_TOUCHING_X ||
9973 trigger_event == CE_HITTING_X ||
9974 trigger_event == CE_HIT_BY_X ||
9976 /* this one was forgotten until 3.2.3 */
9977 trigger_event == CE_DIGGING_X);
9980 if (change->can_change_or_has_action &&
9981 change->has_event[trigger_event] &&
9982 change->trigger_side & trigger_side &&
9983 change->trigger_player & trigger_player &&
9984 (!check_trigger_element ||
9985 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9987 change->actual_trigger_element = trigger_element;
9988 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9989 change->actual_trigger_side = trigger_side;
9990 change->actual_trigger_ce_value = CustomValue[x][y];
9991 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9993 /* special case: trigger element not at (x,y) position for some events */
9994 if (check_trigger_element)
10006 { 0, 0 }, { 0, 0 }, { 0, 0 },
10010 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10011 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10013 change->actual_trigger_ce_value = CustomValue[xx][yy];
10014 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10017 if (change->can_change && !change_done)
10019 ChangeDelay[x][y] = 1;
10020 ChangeEvent[x][y] = trigger_event;
10022 HandleElementChange(x, y, p);
10024 change_done = TRUE;
10026 #if USE_NEW_DELAYED_ACTION
10027 else if (change->has_action)
10029 ExecuteCustomElementAction(x, y, element, p);
10030 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10033 if (change->has_action)
10035 ExecuteCustomElementAction(x, y, element, p);
10036 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10042 RECURSION_LOOP_DETECTION_END();
10044 return change_done;
10047 static void PlayPlayerSound(struct PlayerInfo *player)
10049 int jx = player->jx, jy = player->jy;
10050 int sound_element = player->artwork_element;
10051 int last_action = player->last_action_waiting;
10052 int action = player->action_waiting;
10054 if (player->is_waiting)
10056 if (action != last_action)
10057 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10059 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10063 if (action != last_action)
10064 StopSound(element_info[sound_element].sound[last_action]);
10066 if (last_action == ACTION_SLEEPING)
10067 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10071 static void PlayAllPlayersSound()
10075 for (i = 0; i < MAX_PLAYERS; i++)
10076 if (stored_player[i].active)
10077 PlayPlayerSound(&stored_player[i]);
10080 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10082 boolean last_waiting = player->is_waiting;
10083 int move_dir = player->MovDir;
10085 player->dir_waiting = move_dir;
10086 player->last_action_waiting = player->action_waiting;
10090 if (!last_waiting) /* not waiting -> waiting */
10092 player->is_waiting = TRUE;
10094 player->frame_counter_bored =
10096 game.player_boring_delay_fixed +
10097 GetSimpleRandom(game.player_boring_delay_random);
10098 player->frame_counter_sleeping =
10100 game.player_sleeping_delay_fixed +
10101 GetSimpleRandom(game.player_sleeping_delay_random);
10103 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10106 if (game.player_sleeping_delay_fixed +
10107 game.player_sleeping_delay_random > 0 &&
10108 player->anim_delay_counter == 0 &&
10109 player->post_delay_counter == 0 &&
10110 FrameCounter >= player->frame_counter_sleeping)
10111 player->is_sleeping = TRUE;
10112 else if (game.player_boring_delay_fixed +
10113 game.player_boring_delay_random > 0 &&
10114 FrameCounter >= player->frame_counter_bored)
10115 player->is_bored = TRUE;
10117 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10118 player->is_bored ? ACTION_BORING :
10121 if (player->is_sleeping && player->use_murphy)
10123 /* special case for sleeping Murphy when leaning against non-free tile */
10125 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10126 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10127 !IS_MOVING(player->jx - 1, player->jy)))
10128 move_dir = MV_LEFT;
10129 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10130 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10131 !IS_MOVING(player->jx + 1, player->jy)))
10132 move_dir = MV_RIGHT;
10134 player->is_sleeping = FALSE;
10136 player->dir_waiting = move_dir;
10139 if (player->is_sleeping)
10141 if (player->num_special_action_sleeping > 0)
10143 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10145 int last_special_action = player->special_action_sleeping;
10146 int num_special_action = player->num_special_action_sleeping;
10147 int special_action =
10148 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10149 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10150 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10151 last_special_action + 1 : ACTION_SLEEPING);
10152 int special_graphic =
10153 el_act_dir2img(player->artwork_element, special_action, move_dir);
10155 player->anim_delay_counter =
10156 graphic_info[special_graphic].anim_delay_fixed +
10157 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10158 player->post_delay_counter =
10159 graphic_info[special_graphic].post_delay_fixed +
10160 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10162 player->special_action_sleeping = special_action;
10165 if (player->anim_delay_counter > 0)
10167 player->action_waiting = player->special_action_sleeping;
10168 player->anim_delay_counter--;
10170 else if (player->post_delay_counter > 0)
10172 player->post_delay_counter--;
10176 else if (player->is_bored)
10178 if (player->num_special_action_bored > 0)
10180 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10182 int special_action =
10183 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10184 int special_graphic =
10185 el_act_dir2img(player->artwork_element, special_action, move_dir);
10187 player->anim_delay_counter =
10188 graphic_info[special_graphic].anim_delay_fixed +
10189 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10190 player->post_delay_counter =
10191 graphic_info[special_graphic].post_delay_fixed +
10192 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10194 player->special_action_bored = special_action;
10197 if (player->anim_delay_counter > 0)
10199 player->action_waiting = player->special_action_bored;
10200 player->anim_delay_counter--;
10202 else if (player->post_delay_counter > 0)
10204 player->post_delay_counter--;
10209 else if (last_waiting) /* waiting -> not waiting */
10211 player->is_waiting = FALSE;
10212 player->is_bored = FALSE;
10213 player->is_sleeping = FALSE;
10215 player->frame_counter_bored = -1;
10216 player->frame_counter_sleeping = -1;
10218 player->anim_delay_counter = 0;
10219 player->post_delay_counter = 0;
10221 player->dir_waiting = player->MovDir;
10222 player->action_waiting = ACTION_DEFAULT;
10224 player->special_action_bored = ACTION_DEFAULT;
10225 player->special_action_sleeping = ACTION_DEFAULT;
10229 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10231 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10232 int left = player_action & JOY_LEFT;
10233 int right = player_action & JOY_RIGHT;
10234 int up = player_action & JOY_UP;
10235 int down = player_action & JOY_DOWN;
10236 int button1 = player_action & JOY_BUTTON_1;
10237 int button2 = player_action & JOY_BUTTON_2;
10238 int dx = (left ? -1 : right ? 1 : 0);
10239 int dy = (up ? -1 : down ? 1 : 0);
10241 if (!player->active || tape.pausing)
10247 snapped = SnapField(player, dx, dy);
10251 dropped = DropElement(player);
10253 moved = MovePlayer(player, dx, dy);
10256 if (tape.single_step && tape.recording && !tape.pausing)
10258 if (button1 || (dropped && !moved))
10260 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10261 SnapField(player, 0, 0); /* stop snapping */
10265 SetPlayerWaiting(player, FALSE);
10267 return player_action;
10271 /* no actions for this player (no input at player's configured device) */
10273 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10274 SnapField(player, 0, 0);
10275 CheckGravityMovementWhenNotMoving(player);
10277 if (player->MovPos == 0)
10278 SetPlayerWaiting(player, TRUE);
10280 if (player->MovPos == 0) /* needed for tape.playing */
10281 player->is_moving = FALSE;
10283 player->is_dropping = FALSE;
10284 player->is_dropping_pressed = FALSE;
10285 player->drop_pressed_delay = 0;
10291 static void CheckLevelTime()
10295 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10297 if (level.native_em_level->lev->home == 0) /* all players at home */
10299 PlayerWins(local_player);
10301 AllPlayersGone = TRUE;
10303 level.native_em_level->lev->home = -1;
10306 if (level.native_em_level->ply[0]->alive == 0 &&
10307 level.native_em_level->ply[1]->alive == 0 &&
10308 level.native_em_level->ply[2]->alive == 0 &&
10309 level.native_em_level->ply[3]->alive == 0) /* all dead */
10310 AllPlayersGone = TRUE;
10313 if (TimeFrames >= FRAMES_PER_SECOND)
10318 for (i = 0; i < MAX_PLAYERS; i++)
10320 struct PlayerInfo *player = &stored_player[i];
10322 if (SHIELD_ON(player))
10324 player->shield_normal_time_left--;
10326 if (player->shield_deadly_time_left > 0)
10327 player->shield_deadly_time_left--;
10331 if (!local_player->LevelSolved && !level.use_step_counter)
10339 if (TimeLeft <= 10 && setup.time_limit)
10340 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10342 DrawGameValue_Time(TimeLeft);
10344 if (!TimeLeft && setup.time_limit)
10346 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10347 level.native_em_level->lev->killed_out_of_time = TRUE;
10349 for (i = 0; i < MAX_PLAYERS; i++)
10350 KillPlayer(&stored_player[i]);
10353 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10354 DrawGameValue_Time(TimePlayed);
10356 level.native_em_level->lev->time =
10357 (level.time == 0 ? TimePlayed : TimeLeft);
10360 if (tape.recording || tape.playing)
10361 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10365 void AdvanceFrameAndPlayerCounters(int player_nr)
10369 /* advance frame counters (global frame counter and time frame counter) */
10373 /* advance player counters (counters for move delay, move animation etc.) */
10374 for (i = 0; i < MAX_PLAYERS; i++)
10376 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10377 int move_delay_value = stored_player[i].move_delay_value;
10378 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10380 if (!advance_player_counters) /* not all players may be affected */
10383 #if USE_NEW_PLAYER_ANIM
10384 if (move_frames == 0) /* less than one move per game frame */
10386 int stepsize = TILEX / move_delay_value;
10387 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10388 int count = (stored_player[i].is_moving ?
10389 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10391 if (count % delay == 0)
10396 stored_player[i].Frame += move_frames;
10398 if (stored_player[i].MovPos != 0)
10399 stored_player[i].StepFrame += move_frames;
10401 if (stored_player[i].move_delay > 0)
10402 stored_player[i].move_delay--;
10404 /* due to bugs in previous versions, counter must count up, not down */
10405 if (stored_player[i].push_delay != -1)
10406 stored_player[i].push_delay++;
10408 if (stored_player[i].drop_delay > 0)
10409 stored_player[i].drop_delay--;
10411 if (stored_player[i].is_dropping_pressed)
10412 stored_player[i].drop_pressed_delay++;
10416 void StartGameActions(boolean init_network_game, boolean record_tape,
10419 unsigned long new_random_seed = InitRND(random_seed);
10422 TapeStartRecording(new_random_seed);
10424 #if defined(NETWORK_AVALIABLE)
10425 if (init_network_game)
10427 SendToServer_StartPlaying();
10438 static unsigned long game_frame_delay = 0;
10439 unsigned long game_frame_delay_value;
10440 byte *recorded_player_action;
10441 byte summarized_player_action = 0;
10442 byte tape_action[MAX_PLAYERS];
10445 /* detect endless loops, caused by custom element programming */
10446 if (recursion_loop_detected && recursion_loop_depth == 0)
10448 char *message = getStringCat3("Internal Error ! Element ",
10449 EL_NAME(recursion_loop_element),
10450 " caused endless loop ! Quit the game ?");
10452 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10453 EL_NAME(recursion_loop_element));
10455 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10457 recursion_loop_detected = FALSE; /* if game should be continued */
10464 if (game.restart_level)
10465 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10467 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10469 if (level.native_em_level->lev->home == 0) /* all players at home */
10471 PlayerWins(local_player);
10473 AllPlayersGone = TRUE;
10475 level.native_em_level->lev->home = -1;
10478 if (level.native_em_level->ply[0]->alive == 0 &&
10479 level.native_em_level->ply[1]->alive == 0 &&
10480 level.native_em_level->ply[2]->alive == 0 &&
10481 level.native_em_level->ply[3]->alive == 0) /* all dead */
10482 AllPlayersGone = TRUE;
10485 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10488 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10491 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10494 game_frame_delay_value =
10495 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10497 if (tape.playing && tape.warp_forward && !tape.pausing)
10498 game_frame_delay_value = 0;
10500 /* ---------- main game synchronization point ---------- */
10502 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10504 if (network_playing && !network_player_action_received)
10506 /* try to get network player actions in time */
10508 #if defined(NETWORK_AVALIABLE)
10509 /* last chance to get network player actions without main loop delay */
10510 HandleNetworking();
10513 /* game was quit by network peer */
10514 if (game_status != GAME_MODE_PLAYING)
10517 if (!network_player_action_received)
10518 return; /* failed to get network player actions in time */
10520 /* do not yet reset "network_player_action_received" (for tape.pausing) */
10526 /* at this point we know that we really continue executing the game */
10528 network_player_action_received = FALSE;
10530 /* when playing tape, read previously recorded player input from tape data */
10531 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10534 /* TapePlayAction() may return NULL when toggling to "pause before death" */
10539 if (tape.set_centered_player)
10541 game.centered_player_nr_next = tape.centered_player_nr_next;
10542 game.set_centered_player = TRUE;
10545 for (i = 0; i < MAX_PLAYERS; i++)
10547 summarized_player_action |= stored_player[i].action;
10549 if (!network_playing)
10550 stored_player[i].effective_action = stored_player[i].action;
10553 #if defined(NETWORK_AVALIABLE)
10554 if (network_playing)
10555 SendToServer_MovePlayer(summarized_player_action);
10558 if (!options.network && !setup.team_mode)
10559 local_player->effective_action = summarized_player_action;
10561 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10563 for (i = 0; i < MAX_PLAYERS; i++)
10564 stored_player[i].effective_action =
10565 (i == game.centered_player_nr ? summarized_player_action : 0);
10568 if (recorded_player_action != NULL)
10569 for (i = 0; i < MAX_PLAYERS; i++)
10570 stored_player[i].effective_action = recorded_player_action[i];
10572 for (i = 0; i < MAX_PLAYERS; i++)
10574 tape_action[i] = stored_player[i].effective_action;
10576 /* (this can only happen in the R'n'D game engine) */
10577 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10578 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10581 /* only record actions from input devices, but not programmed actions */
10582 if (tape.recording)
10583 TapeRecordAction(tape_action);
10585 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10587 GameActions_EM_Main();
10595 void GameActions_EM_Main()
10597 byte effective_action[MAX_PLAYERS];
10598 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10601 for (i = 0; i < MAX_PLAYERS; i++)
10602 effective_action[i] = stored_player[i].effective_action;
10604 GameActions_EM(effective_action, warp_mode);
10608 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10611 void GameActions_RND()
10613 int magic_wall_x = 0, magic_wall_y = 0;
10614 int i, x, y, element, graphic;
10616 InitPlayfieldScanModeVars();
10618 #if USE_ONE_MORE_CHANGE_PER_FRAME
10619 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10621 SCAN_PLAYFIELD(x, y)
10623 ChangeCount[x][y] = 0;
10624 ChangeEvent[x][y] = -1;
10629 if (game.set_centered_player)
10631 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10633 /* switching to "all players" only possible if all players fit to screen */
10634 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10636 game.centered_player_nr_next = game.centered_player_nr;
10637 game.set_centered_player = FALSE;
10640 /* do not switch focus to non-existing (or non-active) player */
10641 if (game.centered_player_nr_next >= 0 &&
10642 !stored_player[game.centered_player_nr_next].active)
10644 game.centered_player_nr_next = game.centered_player_nr;
10645 game.set_centered_player = FALSE;
10649 if (game.set_centered_player &&
10650 ScreenMovPos == 0) /* screen currently aligned at tile position */
10654 if (game.centered_player_nr_next == -1)
10656 setScreenCenteredToAllPlayers(&sx, &sy);
10660 sx = stored_player[game.centered_player_nr_next].jx;
10661 sy = stored_player[game.centered_player_nr_next].jy;
10664 game.centered_player_nr = game.centered_player_nr_next;
10665 game.set_centered_player = FALSE;
10667 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10668 DrawGameDoorValues();
10671 for (i = 0; i < MAX_PLAYERS; i++)
10673 int actual_player_action = stored_player[i].effective_action;
10676 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10677 - rnd_equinox_tetrachloride 048
10678 - rnd_equinox_tetrachloride_ii 096
10679 - rnd_emanuel_schmieg 002
10680 - doctor_sloan_ww 001, 020
10682 if (stored_player[i].MovPos == 0)
10683 CheckGravityMovement(&stored_player[i]);
10686 /* overwrite programmed action with tape action */
10687 if (stored_player[i].programmed_action)
10688 actual_player_action = stored_player[i].programmed_action;
10690 PlayerActions(&stored_player[i], actual_player_action);
10692 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10695 ScrollScreen(NULL, SCROLL_GO_ON);
10697 /* for backwards compatibility, the following code emulates a fixed bug that
10698 occured when pushing elements (causing elements that just made their last
10699 pushing step to already (if possible) make their first falling step in the
10700 same game frame, which is bad); this code is also needed to use the famous
10701 "spring push bug" which is used in older levels and might be wanted to be
10702 used also in newer levels, but in this case the buggy pushing code is only
10703 affecting the "spring" element and no other elements */
10705 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10707 for (i = 0; i < MAX_PLAYERS; i++)
10709 struct PlayerInfo *player = &stored_player[i];
10710 int x = player->jx;
10711 int y = player->jy;
10713 if (player->active && player->is_pushing && player->is_moving &&
10715 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10716 Feld[x][y] == EL_SPRING))
10718 ContinueMoving(x, y);
10720 /* continue moving after pushing (this is actually a bug) */
10721 if (!IS_MOVING(x, y))
10722 Stop[x][y] = FALSE;
10728 debug_print_timestamp(0, "start main loop profiling");
10731 SCAN_PLAYFIELD(x, y)
10733 ChangeCount[x][y] = 0;
10734 ChangeEvent[x][y] = -1;
10736 /* this must be handled before main playfield loop */
10737 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10740 if (MovDelay[x][y] <= 0)
10744 #if USE_NEW_SNAP_DELAY
10745 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10748 if (MovDelay[x][y] <= 0)
10751 DrawLevelField(x, y);
10753 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10759 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10761 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10762 printf("GameActions(): This should never happen!\n");
10764 ChangePage[x][y] = -1;
10768 Stop[x][y] = FALSE;
10769 if (WasJustMoving[x][y] > 0)
10770 WasJustMoving[x][y]--;
10771 if (WasJustFalling[x][y] > 0)
10772 WasJustFalling[x][y]--;
10773 if (CheckCollision[x][y] > 0)
10774 CheckCollision[x][y]--;
10775 if (CheckImpact[x][y] > 0)
10776 CheckImpact[x][y]--;
10780 /* reset finished pushing action (not done in ContinueMoving() to allow
10781 continuous pushing animation for elements with zero push delay) */
10782 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10784 ResetGfxAnimation(x, y);
10785 DrawLevelField(x, y);
10789 if (IS_BLOCKED(x, y))
10793 Blocked2Moving(x, y, &oldx, &oldy);
10794 if (!IS_MOVING(oldx, oldy))
10796 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10797 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10798 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10799 printf("GameActions(): This should never happen!\n");
10806 debug_print_timestamp(0, "- time for pre-main loop:");
10809 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
10810 SCAN_PLAYFIELD(x, y)
10812 element = Feld[x][y];
10813 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10818 int element2 = element;
10819 int graphic2 = graphic;
10821 int element2 = Feld[x][y];
10822 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10824 int last_gfx_frame = GfxFrame[x][y];
10826 if (graphic_info[graphic2].anim_global_sync)
10827 GfxFrame[x][y] = FrameCounter;
10828 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10829 GfxFrame[x][y] = CustomValue[x][y];
10830 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10831 GfxFrame[x][y] = element_info[element2].collect_score;
10832 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10833 GfxFrame[x][y] = ChangeDelay[x][y];
10835 if (redraw && GfxFrame[x][y] != last_gfx_frame)
10836 DrawLevelGraphicAnimation(x, y, graphic2);
10839 ResetGfxFrame(x, y, TRUE);
10843 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10844 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10845 ResetRandomAnimationValue(x, y);
10849 SetRandomAnimationValue(x, y);
10853 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10856 #endif // -------------------- !!! TEST ONLY !!! --------------------
10859 debug_print_timestamp(0, "- time for TEST loop: -->");
10862 SCAN_PLAYFIELD(x, y)
10864 element = Feld[x][y];
10865 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10867 ResetGfxFrame(x, y, TRUE);
10869 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10870 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10871 ResetRandomAnimationValue(x, y);
10873 SetRandomAnimationValue(x, y);
10875 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10877 if (IS_INACTIVE(element))
10879 if (IS_ANIMATED(graphic))
10880 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10885 /* this may take place after moving, so 'element' may have changed */
10886 if (IS_CHANGING(x, y) &&
10887 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10889 int page = element_info[element].event_page_nr[CE_DELAY];
10892 HandleElementChange(x, y, page);
10894 if (CAN_CHANGE(element))
10895 HandleElementChange(x, y, page);
10897 if (HAS_ACTION(element))
10898 ExecuteCustomElementAction(x, y, element, page);
10901 element = Feld[x][y];
10902 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10905 #if 0 // ---------------------------------------------------------------------
10907 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10911 element = Feld[x][y];
10912 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10914 if (IS_ANIMATED(graphic) &&
10915 !IS_MOVING(x, y) &&
10917 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10919 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10920 DrawTwinkleOnField(x, y);
10922 else if (IS_MOVING(x, y))
10923 ContinueMoving(x, y);
10930 case EL_EM_EXIT_OPEN:
10931 case EL_SP_EXIT_OPEN:
10932 case EL_STEEL_EXIT_OPEN:
10933 case EL_EM_STEEL_EXIT_OPEN:
10934 case EL_SP_TERMINAL:
10935 case EL_SP_TERMINAL_ACTIVE:
10936 case EL_EXTRA_TIME:
10937 case EL_SHIELD_NORMAL:
10938 case EL_SHIELD_DEADLY:
10939 if (IS_ANIMATED(graphic))
10940 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10943 case EL_DYNAMITE_ACTIVE:
10944 case EL_EM_DYNAMITE_ACTIVE:
10945 case EL_DYNABOMB_PLAYER_1_ACTIVE:
10946 case EL_DYNABOMB_PLAYER_2_ACTIVE:
10947 case EL_DYNABOMB_PLAYER_3_ACTIVE:
10948 case EL_DYNABOMB_PLAYER_4_ACTIVE:
10949 case EL_SP_DISK_RED_ACTIVE:
10950 CheckDynamite(x, y);
10953 case EL_AMOEBA_GROWING:
10954 AmoebeWaechst(x, y);
10957 case EL_AMOEBA_SHRINKING:
10958 AmoebaDisappearing(x, y);
10961 #if !USE_NEW_AMOEBA_CODE
10962 case EL_AMOEBA_WET:
10963 case EL_AMOEBA_DRY:
10964 case EL_AMOEBA_FULL:
10966 case EL_EMC_DRIPPER:
10967 AmoebeAbleger(x, y);
10971 case EL_GAME_OF_LIFE:
10976 case EL_EXIT_CLOSED:
10980 case EL_EM_EXIT_CLOSED:
10984 case EL_STEEL_EXIT_CLOSED:
10985 CheckExitSteel(x, y);
10988 case EL_EM_STEEL_EXIT_CLOSED:
10989 CheckExitSteelEM(x, y);
10992 case EL_SP_EXIT_CLOSED:
10996 case EL_EXPANDABLE_WALL_GROWING:
10997 case EL_EXPANDABLE_STEELWALL_GROWING:
10998 MauerWaechst(x, y);
11001 case EL_EXPANDABLE_WALL:
11002 case EL_EXPANDABLE_WALL_HORIZONTAL:
11003 case EL_EXPANDABLE_WALL_VERTICAL:
11004 case EL_EXPANDABLE_WALL_ANY:
11005 case EL_BD_EXPANDABLE_WALL:
11006 MauerAbleger(x, y);
11009 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11010 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11011 case EL_EXPANDABLE_STEELWALL_ANY:
11012 MauerAblegerStahl(x, y);
11016 CheckForDragon(x, y);
11022 case EL_ELEMENT_SNAPPING:
11023 case EL_DIAGONAL_SHRINKING:
11024 case EL_DIAGONAL_GROWING:
11027 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11029 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11034 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11035 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11040 #else // ---------------------------------------------------------------------
11042 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11046 element = Feld[x][y];
11047 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11049 if (IS_ANIMATED(graphic) &&
11050 !IS_MOVING(x, y) &&
11052 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11054 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11055 DrawTwinkleOnField(x, y);
11057 else if ((element == EL_ACID ||
11058 element == EL_EXIT_OPEN ||
11059 element == EL_EM_EXIT_OPEN ||
11060 element == EL_SP_EXIT_OPEN ||
11061 element == EL_STEEL_EXIT_OPEN ||
11062 element == EL_EM_STEEL_EXIT_OPEN ||
11063 element == EL_SP_TERMINAL ||
11064 element == EL_SP_TERMINAL_ACTIVE ||
11065 element == EL_EXTRA_TIME ||
11066 element == EL_SHIELD_NORMAL ||
11067 element == EL_SHIELD_DEADLY) &&
11068 IS_ANIMATED(graphic))
11069 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11070 else if (IS_MOVING(x, y))
11071 ContinueMoving(x, y);
11072 else if (IS_ACTIVE_BOMB(element))
11073 CheckDynamite(x, y);
11074 else if (element == EL_AMOEBA_GROWING)
11075 AmoebeWaechst(x, y);
11076 else if (element == EL_AMOEBA_SHRINKING)
11077 AmoebaDisappearing(x, y);
11079 #if !USE_NEW_AMOEBA_CODE
11080 else if (IS_AMOEBALIVE(element))
11081 AmoebeAbleger(x, y);
11084 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11086 else if (element == EL_EXIT_CLOSED)
11088 else if (element == EL_EM_EXIT_CLOSED)
11090 else if (element == EL_STEEL_EXIT_CLOSED)
11091 CheckExitSteel(x, y);
11092 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11093 CheckExitSteelEM(x, y);
11094 else if (element == EL_SP_EXIT_CLOSED)
11096 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11097 element == EL_EXPANDABLE_STEELWALL_GROWING)
11098 MauerWaechst(x, y);
11099 else if (element == EL_EXPANDABLE_WALL ||
11100 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11101 element == EL_EXPANDABLE_WALL_VERTICAL ||
11102 element == EL_EXPANDABLE_WALL_ANY ||
11103 element == EL_BD_EXPANDABLE_WALL)
11104 MauerAbleger(x, y);
11105 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11106 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11107 element == EL_EXPANDABLE_STEELWALL_ANY)
11108 MauerAblegerStahl(x, y);
11109 else if (element == EL_FLAMES)
11110 CheckForDragon(x, y);
11111 else if (element == EL_EXPLOSION)
11112 ; /* drawing of correct explosion animation is handled separately */
11113 else if (element == EL_ELEMENT_SNAPPING ||
11114 element == EL_DIAGONAL_SHRINKING ||
11115 element == EL_DIAGONAL_GROWING)
11117 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11119 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11121 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11122 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11124 #endif // ---------------------------------------------------------------------
11126 if (IS_BELT_ACTIVE(element))
11127 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11129 if (game.magic_wall_active)
11131 int jx = local_player->jx, jy = local_player->jy;
11133 /* play the element sound at the position nearest to the player */
11134 if ((element == EL_MAGIC_WALL_FULL ||
11135 element == EL_MAGIC_WALL_ACTIVE ||
11136 element == EL_MAGIC_WALL_EMPTYING ||
11137 element == EL_BD_MAGIC_WALL_FULL ||
11138 element == EL_BD_MAGIC_WALL_ACTIVE ||
11139 element == EL_BD_MAGIC_WALL_EMPTYING ||
11140 element == EL_DC_MAGIC_WALL_FULL ||
11141 element == EL_DC_MAGIC_WALL_ACTIVE ||
11142 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11143 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11152 debug_print_timestamp(0, "- time for MAIN loop: -->");
11155 #if USE_NEW_AMOEBA_CODE
11156 /* new experimental amoeba growth stuff */
11157 if (!(FrameCounter % 8))
11159 static unsigned long random = 1684108901;
11161 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11163 x = RND(lev_fieldx);
11164 y = RND(lev_fieldy);
11165 element = Feld[x][y];
11167 if (!IS_PLAYER(x,y) &&
11168 (element == EL_EMPTY ||
11169 CAN_GROW_INTO(element) ||
11170 element == EL_QUICKSAND_EMPTY ||
11171 element == EL_QUICKSAND_FAST_EMPTY ||
11172 element == EL_ACID_SPLASH_LEFT ||
11173 element == EL_ACID_SPLASH_RIGHT))
11175 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11176 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11177 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11178 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11179 Feld[x][y] = EL_AMOEBA_DROP;
11182 random = random * 129 + 1;
11188 if (game.explosions_delayed)
11191 game.explosions_delayed = FALSE;
11193 SCAN_PLAYFIELD(x, y)
11195 element = Feld[x][y];
11197 if (ExplodeField[x][y])
11198 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11199 else if (element == EL_EXPLOSION)
11200 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11202 ExplodeField[x][y] = EX_TYPE_NONE;
11205 game.explosions_delayed = TRUE;
11208 if (game.magic_wall_active)
11210 if (!(game.magic_wall_time_left % 4))
11212 int element = Feld[magic_wall_x][magic_wall_y];
11214 if (element == EL_BD_MAGIC_WALL_FULL ||
11215 element == EL_BD_MAGIC_WALL_ACTIVE ||
11216 element == EL_BD_MAGIC_WALL_EMPTYING)
11217 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11218 else if (element == EL_DC_MAGIC_WALL_FULL ||
11219 element == EL_DC_MAGIC_WALL_ACTIVE ||
11220 element == EL_DC_MAGIC_WALL_EMPTYING)
11221 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11223 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11226 if (game.magic_wall_time_left > 0)
11228 game.magic_wall_time_left--;
11229 if (!game.magic_wall_time_left)
11231 SCAN_PLAYFIELD(x, y)
11233 element = Feld[x][y];
11235 if (element == EL_MAGIC_WALL_ACTIVE ||
11236 element == EL_MAGIC_WALL_FULL)
11238 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11239 DrawLevelField(x, y);
11241 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11242 element == EL_BD_MAGIC_WALL_FULL)
11244 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11245 DrawLevelField(x, y);
11247 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11248 element == EL_DC_MAGIC_WALL_FULL)
11250 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11251 DrawLevelField(x, y);
11255 game.magic_wall_active = FALSE;
11260 if (game.light_time_left > 0)
11262 game.light_time_left--;
11264 if (game.light_time_left == 0)
11265 RedrawAllLightSwitchesAndInvisibleElements();
11268 if (game.timegate_time_left > 0)
11270 game.timegate_time_left--;
11272 if (game.timegate_time_left == 0)
11273 CloseAllOpenTimegates();
11276 if (game.lenses_time_left > 0)
11278 game.lenses_time_left--;
11280 if (game.lenses_time_left == 0)
11281 RedrawAllInvisibleElementsForLenses();
11284 if (game.magnify_time_left > 0)
11286 game.magnify_time_left--;
11288 if (game.magnify_time_left == 0)
11289 RedrawAllInvisibleElementsForMagnifier();
11292 for (i = 0; i < MAX_PLAYERS; i++)
11294 struct PlayerInfo *player = &stored_player[i];
11296 if (SHIELD_ON(player))
11298 if (player->shield_deadly_time_left)
11299 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11300 else if (player->shield_normal_time_left)
11301 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11308 PlayAllPlayersSound();
11310 if (options.debug) /* calculate frames per second */
11312 static unsigned long fps_counter = 0;
11313 static int fps_frames = 0;
11314 unsigned long fps_delay_ms = Counter() - fps_counter;
11318 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11320 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11323 fps_counter = Counter();
11326 redraw_mask |= REDRAW_FPS;
11329 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11331 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11333 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11335 local_player->show_envelope = 0;
11339 debug_print_timestamp(0, "stop main loop profiling ");
11340 printf("----------------------------------------------------------\n");
11343 /* use random number generator in every frame to make it less predictable */
11344 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11348 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11350 int min_x = x, min_y = y, max_x = x, max_y = y;
11353 for (i = 0; i < MAX_PLAYERS; i++)
11355 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11357 if (!stored_player[i].active || &stored_player[i] == player)
11360 min_x = MIN(min_x, jx);
11361 min_y = MIN(min_y, jy);
11362 max_x = MAX(max_x, jx);
11363 max_y = MAX(max_y, jy);
11366 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11369 static boolean AllPlayersInVisibleScreen()
11373 for (i = 0; i < MAX_PLAYERS; i++)
11375 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11377 if (!stored_player[i].active)
11380 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11387 void ScrollLevel(int dx, int dy)
11390 static Bitmap *bitmap_db_field2 = NULL;
11391 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11398 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11399 /* only horizontal XOR vertical scroll direction allowed */
11400 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11405 if (bitmap_db_field2 == NULL)
11406 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11408 /* needed when blitting directly to same bitmap -- should not be needed with
11409 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11410 BlitBitmap(drawto_field, bitmap_db_field2,
11411 FX + TILEX * (dx == -1) - softscroll_offset,
11412 FY + TILEY * (dy == -1) - softscroll_offset,
11413 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11414 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11415 FX + TILEX * (dx == 1) - softscroll_offset,
11416 FY + TILEY * (dy == 1) - softscroll_offset);
11417 BlitBitmap(bitmap_db_field2, drawto_field,
11418 FX + TILEX * (dx == 1) - softscroll_offset,
11419 FY + TILEY * (dy == 1) - softscroll_offset,
11420 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11421 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11422 FX + TILEX * (dx == 1) - softscroll_offset,
11423 FY + TILEY * (dy == 1) - softscroll_offset);
11428 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11429 int xsize = (BX2 - BX1 + 1);
11430 int ysize = (BY2 - BY1 + 1);
11431 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11432 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11433 int step = (start < end ? +1 : -1);
11435 for (i = start; i != end; i += step)
11437 BlitBitmap(drawto_field, drawto_field,
11438 FX + TILEX * (dx != 0 ? i + step : 0),
11439 FY + TILEY * (dy != 0 ? i + step : 0),
11440 TILEX * (dx != 0 ? 1 : xsize),
11441 TILEY * (dy != 0 ? 1 : ysize),
11442 FX + TILEX * (dx != 0 ? i : 0),
11443 FY + TILEY * (dy != 0 ? i : 0));
11448 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11450 BlitBitmap(drawto_field, drawto_field,
11451 FX + TILEX * (dx == -1) - softscroll_offset,
11452 FY + TILEY * (dy == -1) - softscroll_offset,
11453 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11454 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11455 FX + TILEX * (dx == 1) - softscroll_offset,
11456 FY + TILEY * (dy == 1) - softscroll_offset);
11462 x = (dx == 1 ? BX1 : BX2);
11463 for (y = BY1; y <= BY2; y++)
11464 DrawScreenField(x, y);
11469 y = (dy == 1 ? BY1 : BY2);
11470 for (x = BX1; x <= BX2; x++)
11471 DrawScreenField(x, y);
11474 redraw_mask |= REDRAW_FIELD;
11477 static boolean canFallDown(struct PlayerInfo *player)
11479 int jx = player->jx, jy = player->jy;
11481 return (IN_LEV_FIELD(jx, jy + 1) &&
11482 (IS_FREE(jx, jy + 1) ||
11483 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11484 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11485 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11488 static boolean canPassField(int x, int y, int move_dir)
11490 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11491 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11492 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11493 int nextx = x + dx;
11494 int nexty = y + dy;
11495 int element = Feld[x][y];
11497 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11498 !CAN_MOVE(element) &&
11499 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11500 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11501 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11504 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11506 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11507 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11508 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11512 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11513 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11514 (IS_DIGGABLE(Feld[newx][newy]) ||
11515 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11516 canPassField(newx, newy, move_dir)));
11519 static void CheckGravityMovement(struct PlayerInfo *player)
11521 #if USE_PLAYER_GRAVITY
11522 if (player->gravity && !player->programmed_action)
11524 if (game.gravity && !player->programmed_action)
11527 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11528 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11529 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11530 int jx = player->jx, jy = player->jy;
11531 boolean player_is_moving_to_valid_field =
11532 (!player_is_snapping &&
11533 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11534 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11535 boolean player_can_fall_down = canFallDown(player);
11537 if (player_can_fall_down &&
11538 !player_is_moving_to_valid_field)
11539 player->programmed_action = MV_DOWN;
11543 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11545 return CheckGravityMovement(player);
11547 #if USE_PLAYER_GRAVITY
11548 if (player->gravity && !player->programmed_action)
11550 if (game.gravity && !player->programmed_action)
11553 int jx = player->jx, jy = player->jy;
11554 boolean field_under_player_is_free =
11555 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11556 boolean player_is_standing_on_valid_field =
11557 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11558 (IS_WALKABLE(Feld[jx][jy]) &&
11559 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11561 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11562 player->programmed_action = MV_DOWN;
11567 MovePlayerOneStep()
11568 -----------------------------------------------------------------------------
11569 dx, dy: direction (non-diagonal) to try to move the player to
11570 real_dx, real_dy: direction as read from input device (can be diagonal)
11573 boolean MovePlayerOneStep(struct PlayerInfo *player,
11574 int dx, int dy, int real_dx, int real_dy)
11576 int jx = player->jx, jy = player->jy;
11577 int new_jx = jx + dx, new_jy = jy + dy;
11578 #if !USE_FIXED_DONT_RUN_INTO
11582 boolean player_can_move = !player->cannot_move;
11584 if (!player->active || (!dx && !dy))
11585 return MP_NO_ACTION;
11587 player->MovDir = (dx < 0 ? MV_LEFT :
11588 dx > 0 ? MV_RIGHT :
11590 dy > 0 ? MV_DOWN : MV_NONE);
11592 if (!IN_LEV_FIELD(new_jx, new_jy))
11593 return MP_NO_ACTION;
11595 if (!player_can_move)
11597 if (player->MovPos == 0)
11599 player->is_moving = FALSE;
11600 player->is_digging = FALSE;
11601 player->is_collecting = FALSE;
11602 player->is_snapping = FALSE;
11603 player->is_pushing = FALSE;
11608 if (!options.network && game.centered_player_nr == -1 &&
11609 !AllPlayersInSight(player, new_jx, new_jy))
11610 return MP_NO_ACTION;
11612 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11613 return MP_NO_ACTION;
11616 #if !USE_FIXED_DONT_RUN_INTO
11617 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11619 /* (moved to DigField()) */
11620 if (player_can_move && DONT_RUN_INTO(element))
11622 if (element == EL_ACID && dx == 0 && dy == 1)
11624 SplashAcid(new_jx, new_jy);
11625 Feld[jx][jy] = EL_PLAYER_1;
11626 InitMovingField(jx, jy, MV_DOWN);
11627 Store[jx][jy] = EL_ACID;
11628 ContinueMoving(jx, jy);
11629 BuryPlayer(player);
11632 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11638 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11639 if (can_move != MP_MOVING)
11642 /* check if DigField() has caused relocation of the player */
11643 if (player->jx != jx || player->jy != jy)
11644 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11646 StorePlayer[jx][jy] = 0;
11647 player->last_jx = jx;
11648 player->last_jy = jy;
11649 player->jx = new_jx;
11650 player->jy = new_jy;
11651 StorePlayer[new_jx][new_jy] = player->element_nr;
11653 if (player->move_delay_value_next != -1)
11655 player->move_delay_value = player->move_delay_value_next;
11656 player->move_delay_value_next = -1;
11660 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11662 player->step_counter++;
11664 PlayerVisit[jx][jy] = FrameCounter;
11666 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11667 player->is_moving = TRUE;
11671 /* should better be called in MovePlayer(), but this breaks some tapes */
11672 ScrollPlayer(player, SCROLL_INIT);
11678 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11680 int jx = player->jx, jy = player->jy;
11681 int old_jx = jx, old_jy = jy;
11682 int moved = MP_NO_ACTION;
11684 if (!player->active)
11689 if (player->MovPos == 0)
11691 player->is_moving = FALSE;
11692 player->is_digging = FALSE;
11693 player->is_collecting = FALSE;
11694 player->is_snapping = FALSE;
11695 player->is_pushing = FALSE;
11701 if (player->move_delay > 0)
11704 player->move_delay = -1; /* set to "uninitialized" value */
11706 /* store if player is automatically moved to next field */
11707 player->is_auto_moving = (player->programmed_action != MV_NONE);
11709 /* remove the last programmed player action */
11710 player->programmed_action = 0;
11712 if (player->MovPos)
11714 /* should only happen if pre-1.2 tape recordings are played */
11715 /* this is only for backward compatibility */
11717 int original_move_delay_value = player->move_delay_value;
11720 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11724 /* scroll remaining steps with finest movement resolution */
11725 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11727 while (player->MovPos)
11729 ScrollPlayer(player, SCROLL_GO_ON);
11730 ScrollScreen(NULL, SCROLL_GO_ON);
11732 AdvanceFrameAndPlayerCounters(player->index_nr);
11738 player->move_delay_value = original_move_delay_value;
11741 player->is_active = FALSE;
11743 if (player->last_move_dir & MV_HORIZONTAL)
11745 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11746 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11750 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11751 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11754 #if USE_FIXED_BORDER_RUNNING_GFX
11755 if (!moved && !player->is_active)
11757 player->is_moving = FALSE;
11758 player->is_digging = FALSE;
11759 player->is_collecting = FALSE;
11760 player->is_snapping = FALSE;
11761 player->is_pushing = FALSE;
11769 if (moved & MP_MOVING && !ScreenMovPos &&
11770 (player->index_nr == game.centered_player_nr ||
11771 game.centered_player_nr == -1))
11773 if (moved & MP_MOVING && !ScreenMovPos &&
11774 (player == local_player || !options.network))
11777 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11778 int offset = (setup.scroll_delay ? 3 : 0);
11780 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11782 /* actual player has left the screen -- scroll in that direction */
11783 if (jx != old_jx) /* player has moved horizontally */
11784 scroll_x += (jx - old_jx);
11785 else /* player has moved vertically */
11786 scroll_y += (jy - old_jy);
11790 if (jx != old_jx) /* player has moved horizontally */
11792 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
11793 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11794 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11796 /* don't scroll over playfield boundaries */
11797 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11798 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11800 /* don't scroll more than one field at a time */
11801 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11803 /* don't scroll against the player's moving direction */
11804 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
11805 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11806 scroll_x = old_scroll_x;
11808 else /* player has moved vertically */
11810 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
11811 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11812 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11814 /* don't scroll over playfield boundaries */
11815 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11816 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11818 /* don't scroll more than one field at a time */
11819 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11821 /* don't scroll against the player's moving direction */
11822 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
11823 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11824 scroll_y = old_scroll_y;
11828 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11831 if (!options.network && game.centered_player_nr == -1 &&
11832 !AllPlayersInVisibleScreen())
11834 scroll_x = old_scroll_x;
11835 scroll_y = old_scroll_y;
11839 if (!options.network && !AllPlayersInVisibleScreen())
11841 scroll_x = old_scroll_x;
11842 scroll_y = old_scroll_y;
11847 ScrollScreen(player, SCROLL_INIT);
11848 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11853 player->StepFrame = 0;
11855 if (moved & MP_MOVING)
11857 if (old_jx != jx && old_jy == jy)
11858 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11859 else if (old_jx == jx && old_jy != jy)
11860 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11862 DrawLevelField(jx, jy); /* for "crumbled sand" */
11864 player->last_move_dir = player->MovDir;
11865 player->is_moving = TRUE;
11866 player->is_snapping = FALSE;
11867 player->is_switching = FALSE;
11868 player->is_dropping = FALSE;
11869 player->is_dropping_pressed = FALSE;
11870 player->drop_pressed_delay = 0;
11873 /* should better be called here than above, but this breaks some tapes */
11874 ScrollPlayer(player, SCROLL_INIT);
11879 CheckGravityMovementWhenNotMoving(player);
11881 player->is_moving = FALSE;
11883 /* at this point, the player is allowed to move, but cannot move right now
11884 (e.g. because of something blocking the way) -- ensure that the player
11885 is also allowed to move in the next frame (in old versions before 3.1.1,
11886 the player was forced to wait again for eight frames before next try) */
11888 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11889 player->move_delay = 0; /* allow direct movement in the next frame */
11892 if (player->move_delay == -1) /* not yet initialized by DigField() */
11893 player->move_delay = player->move_delay_value;
11895 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11897 TestIfPlayerTouchesBadThing(jx, jy);
11898 TestIfPlayerTouchesCustomElement(jx, jy);
11901 if (!player->active)
11902 RemovePlayer(player);
11907 void ScrollPlayer(struct PlayerInfo *player, int mode)
11909 int jx = player->jx, jy = player->jy;
11910 int last_jx = player->last_jx, last_jy = player->last_jy;
11911 int move_stepsize = TILEX / player->move_delay_value;
11913 #if USE_NEW_PLAYER_SPEED
11914 if (!player->active)
11917 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11920 if (!player->active || player->MovPos == 0)
11924 if (mode == SCROLL_INIT)
11926 player->actual_frame_counter = FrameCounter;
11927 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11929 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11930 Feld[last_jx][last_jy] == EL_EMPTY)
11932 int last_field_block_delay = 0; /* start with no blocking at all */
11933 int block_delay_adjustment = player->block_delay_adjustment;
11935 /* if player blocks last field, add delay for exactly one move */
11936 if (player->block_last_field)
11938 last_field_block_delay += player->move_delay_value;
11940 /* when blocking enabled, prevent moving up despite gravity */
11941 #if USE_PLAYER_GRAVITY
11942 if (player->gravity && player->MovDir == MV_UP)
11943 block_delay_adjustment = -1;
11945 if (game.gravity && player->MovDir == MV_UP)
11946 block_delay_adjustment = -1;
11950 /* add block delay adjustment (also possible when not blocking) */
11951 last_field_block_delay += block_delay_adjustment;
11953 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11954 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11957 #if USE_NEW_PLAYER_SPEED
11958 if (player->MovPos != 0) /* player has not yet reached destination */
11964 else if (!FrameReached(&player->actual_frame_counter, 1))
11967 #if USE_NEW_PLAYER_SPEED
11968 if (player->MovPos != 0)
11970 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11971 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11973 /* before DrawPlayer() to draw correct player graphic for this case */
11974 if (player->MovPos == 0)
11975 CheckGravityMovement(player);
11978 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11979 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11981 /* before DrawPlayer() to draw correct player graphic for this case */
11982 if (player->MovPos == 0)
11983 CheckGravityMovement(player);
11986 if (player->MovPos == 0) /* player reached destination field */
11988 if (player->move_delay_reset_counter > 0)
11990 player->move_delay_reset_counter--;
11992 if (player->move_delay_reset_counter == 0)
11994 /* continue with normal speed after quickly moving through gate */
11995 HALVE_PLAYER_SPEED(player);
11997 /* be able to make the next move without delay */
11998 player->move_delay = 0;
12002 player->last_jx = jx;
12003 player->last_jy = jy;
12005 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12006 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12007 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12008 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12009 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12010 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12012 DrawPlayer(player); /* needed here only to cleanup last field */
12013 RemovePlayer(player);
12015 if (local_player->friends_still_needed == 0 ||
12016 IS_SP_ELEMENT(Feld[jx][jy]))
12017 PlayerWins(player);
12020 /* this breaks one level: "machine", level 000 */
12022 int move_direction = player->MovDir;
12023 int enter_side = MV_DIR_OPPOSITE(move_direction);
12024 int leave_side = move_direction;
12025 int old_jx = last_jx;
12026 int old_jy = last_jy;
12027 int old_element = Feld[old_jx][old_jy];
12028 int new_element = Feld[jx][jy];
12030 if (IS_CUSTOM_ELEMENT(old_element))
12031 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12033 player->index_bit, leave_side);
12035 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12036 CE_PLAYER_LEAVES_X,
12037 player->index_bit, leave_side);
12039 if (IS_CUSTOM_ELEMENT(new_element))
12040 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12041 player->index_bit, enter_side);
12043 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12044 CE_PLAYER_ENTERS_X,
12045 player->index_bit, enter_side);
12047 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12048 CE_MOVE_OF_X, move_direction);
12051 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12053 TestIfPlayerTouchesBadThing(jx, jy);
12054 TestIfPlayerTouchesCustomElement(jx, jy);
12056 /* needed because pushed element has not yet reached its destination,
12057 so it would trigger a change event at its previous field location */
12058 if (!player->is_pushing)
12059 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12061 if (!player->active)
12062 RemovePlayer(player);
12065 if (!local_player->LevelSolved && level.use_step_counter)
12075 if (TimeLeft <= 10 && setup.time_limit)
12076 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12078 DrawGameValue_Time(TimeLeft);
12080 if (!TimeLeft && setup.time_limit)
12081 for (i = 0; i < MAX_PLAYERS; i++)
12082 KillPlayer(&stored_player[i]);
12084 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12085 DrawGameValue_Time(TimePlayed);
12088 if (tape.single_step && tape.recording && !tape.pausing &&
12089 !player->programmed_action)
12090 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12094 void ScrollScreen(struct PlayerInfo *player, int mode)
12096 static unsigned long screen_frame_counter = 0;
12098 if (mode == SCROLL_INIT)
12100 /* set scrolling step size according to actual player's moving speed */
12101 ScrollStepSize = TILEX / player->move_delay_value;
12103 screen_frame_counter = FrameCounter;
12104 ScreenMovDir = player->MovDir;
12105 ScreenMovPos = player->MovPos;
12106 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12109 else if (!FrameReached(&screen_frame_counter, 1))
12114 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12115 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12116 redraw_mask |= REDRAW_FIELD;
12119 ScreenMovDir = MV_NONE;
12122 void TestIfPlayerTouchesCustomElement(int x, int y)
12124 static int xy[4][2] =
12131 static int trigger_sides[4][2] =
12133 /* center side border side */
12134 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12135 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12136 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12137 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12139 static int touch_dir[4] =
12141 MV_LEFT | MV_RIGHT,
12146 int center_element = Feld[x][y]; /* should always be non-moving! */
12149 for (i = 0; i < NUM_DIRECTIONS; i++)
12151 int xx = x + xy[i][0];
12152 int yy = y + xy[i][1];
12153 int center_side = trigger_sides[i][0];
12154 int border_side = trigger_sides[i][1];
12155 int border_element;
12157 if (!IN_LEV_FIELD(xx, yy))
12160 if (IS_PLAYER(x, y))
12162 struct PlayerInfo *player = PLAYERINFO(x, y);
12164 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12165 border_element = Feld[xx][yy]; /* may be moving! */
12166 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12167 border_element = Feld[xx][yy];
12168 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12169 border_element = MovingOrBlocked2Element(xx, yy);
12171 continue; /* center and border element do not touch */
12173 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12174 player->index_bit, border_side);
12175 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12176 CE_PLAYER_TOUCHES_X,
12177 player->index_bit, border_side);
12179 else if (IS_PLAYER(xx, yy))
12181 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12183 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12185 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12186 continue; /* center and border element do not touch */
12189 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12190 player->index_bit, center_side);
12191 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12192 CE_PLAYER_TOUCHES_X,
12193 player->index_bit, center_side);
12199 #if USE_ELEMENT_TOUCHING_BUGFIX
12201 void TestIfElementTouchesCustomElement(int x, int y)
12203 static int xy[4][2] =
12210 static int trigger_sides[4][2] =
12212 /* center side border side */
12213 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12214 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12215 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12216 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12218 static int touch_dir[4] =
12220 MV_LEFT | MV_RIGHT,
12225 boolean change_center_element = FALSE;
12226 int center_element = Feld[x][y]; /* should always be non-moving! */
12227 int border_element_old[NUM_DIRECTIONS];
12230 for (i = 0; i < NUM_DIRECTIONS; i++)
12232 int xx = x + xy[i][0];
12233 int yy = y + xy[i][1];
12234 int border_element;
12236 border_element_old[i] = -1;
12238 if (!IN_LEV_FIELD(xx, yy))
12241 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12242 border_element = Feld[xx][yy]; /* may be moving! */
12243 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12244 border_element = Feld[xx][yy];
12245 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12246 border_element = MovingOrBlocked2Element(xx, yy);
12248 continue; /* center and border element do not touch */
12250 border_element_old[i] = border_element;
12253 for (i = 0; i < NUM_DIRECTIONS; i++)
12255 int xx = x + xy[i][0];
12256 int yy = y + xy[i][1];
12257 int center_side = trigger_sides[i][0];
12258 int border_element = border_element_old[i];
12260 if (border_element == -1)
12263 /* check for change of border element */
12264 CheckElementChangeBySide(xx, yy, border_element, center_element,
12265 CE_TOUCHING_X, center_side);
12268 for (i = 0; i < NUM_DIRECTIONS; i++)
12270 int border_side = trigger_sides[i][1];
12271 int border_element = border_element_old[i];
12273 if (border_element == -1)
12276 /* check for change of center element (but change it only once) */
12277 if (!change_center_element)
12278 change_center_element =
12279 CheckElementChangeBySide(x, y, center_element, border_element,
12280 CE_TOUCHING_X, border_side);
12286 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12288 static int xy[4][2] =
12295 static int trigger_sides[4][2] =
12297 /* center side border side */
12298 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12299 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12300 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12301 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12303 static int touch_dir[4] =
12305 MV_LEFT | MV_RIGHT,
12310 boolean change_center_element = FALSE;
12311 int center_element = Feld[x][y]; /* should always be non-moving! */
12314 for (i = 0; i < NUM_DIRECTIONS; i++)
12316 int xx = x + xy[i][0];
12317 int yy = y + xy[i][1];
12318 int center_side = trigger_sides[i][0];
12319 int border_side = trigger_sides[i][1];
12320 int border_element;
12322 if (!IN_LEV_FIELD(xx, yy))
12325 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12326 border_element = Feld[xx][yy]; /* may be moving! */
12327 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12328 border_element = Feld[xx][yy];
12329 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12330 border_element = MovingOrBlocked2Element(xx, yy);
12332 continue; /* center and border element do not touch */
12334 /* check for change of center element (but change it only once) */
12335 if (!change_center_element)
12336 change_center_element =
12337 CheckElementChangeBySide(x, y, center_element, border_element,
12338 CE_TOUCHING_X, border_side);
12340 /* check for change of border element */
12341 CheckElementChangeBySide(xx, yy, border_element, center_element,
12342 CE_TOUCHING_X, center_side);
12348 void TestIfElementHitsCustomElement(int x, int y, int direction)
12350 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12351 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12352 int hitx = x + dx, hity = y + dy;
12353 int hitting_element = Feld[x][y];
12354 int touched_element;
12356 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12359 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12360 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12362 if (IN_LEV_FIELD(hitx, hity))
12364 int opposite_direction = MV_DIR_OPPOSITE(direction);
12365 int hitting_side = direction;
12366 int touched_side = opposite_direction;
12367 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12368 MovDir[hitx][hity] != direction ||
12369 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12375 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12376 CE_HITTING_X, touched_side);
12378 CheckElementChangeBySide(hitx, hity, touched_element,
12379 hitting_element, CE_HIT_BY_X, hitting_side);
12381 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12382 CE_HIT_BY_SOMETHING, opposite_direction);
12386 /* "hitting something" is also true when hitting the playfield border */
12387 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12388 CE_HITTING_SOMETHING, direction);
12392 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12394 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12395 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12396 int hitx = x + dx, hity = y + dy;
12397 int hitting_element = Feld[x][y];
12398 int touched_element;
12400 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12401 !IS_FREE(hitx, hity) &&
12402 (!IS_MOVING(hitx, hity) ||
12403 MovDir[hitx][hity] != direction ||
12404 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12407 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12411 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12415 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12416 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12418 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12419 EP_CAN_SMASH_EVERYTHING, direction);
12421 if (IN_LEV_FIELD(hitx, hity))
12423 int opposite_direction = MV_DIR_OPPOSITE(direction);
12424 int hitting_side = direction;
12425 int touched_side = opposite_direction;
12427 int touched_element = MovingOrBlocked2Element(hitx, hity);
12430 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12431 MovDir[hitx][hity] != direction ||
12432 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12441 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12442 CE_SMASHED_BY_SOMETHING, opposite_direction);
12444 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12445 CE_OTHER_IS_SMASHING, touched_side);
12447 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12448 CE_OTHER_GETS_SMASHED, hitting_side);
12454 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12456 int i, kill_x = -1, kill_y = -1;
12458 int bad_element = -1;
12459 static int test_xy[4][2] =
12466 static int test_dir[4] =
12474 for (i = 0; i < NUM_DIRECTIONS; i++)
12476 int test_x, test_y, test_move_dir, test_element;
12478 test_x = good_x + test_xy[i][0];
12479 test_y = good_y + test_xy[i][1];
12481 if (!IN_LEV_FIELD(test_x, test_y))
12485 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12487 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12489 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12490 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12492 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12493 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12497 bad_element = test_element;
12503 if (kill_x != -1 || kill_y != -1)
12505 if (IS_PLAYER(good_x, good_y))
12507 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12509 if (player->shield_deadly_time_left > 0 &&
12510 !IS_INDESTRUCTIBLE(bad_element))
12511 Bang(kill_x, kill_y);
12512 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12513 KillPlayer(player);
12516 Bang(good_x, good_y);
12520 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12522 int i, kill_x = -1, kill_y = -1;
12523 int bad_element = Feld[bad_x][bad_y];
12524 static int test_xy[4][2] =
12531 static int touch_dir[4] =
12533 MV_LEFT | MV_RIGHT,
12538 static int test_dir[4] =
12546 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12549 for (i = 0; i < NUM_DIRECTIONS; i++)
12551 int test_x, test_y, test_move_dir, test_element;
12553 test_x = bad_x + test_xy[i][0];
12554 test_y = bad_y + test_xy[i][1];
12555 if (!IN_LEV_FIELD(test_x, test_y))
12559 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12561 test_element = Feld[test_x][test_y];
12563 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12564 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12566 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12567 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12569 /* good thing is player or penguin that does not move away */
12570 if (IS_PLAYER(test_x, test_y))
12572 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12574 if (bad_element == EL_ROBOT && player->is_moving)
12575 continue; /* robot does not kill player if he is moving */
12577 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12579 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12580 continue; /* center and border element do not touch */
12587 else if (test_element == EL_PENGUIN)
12596 if (kill_x != -1 || kill_y != -1)
12598 if (IS_PLAYER(kill_x, kill_y))
12600 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12602 if (player->shield_deadly_time_left > 0 &&
12603 !IS_INDESTRUCTIBLE(bad_element))
12604 Bang(bad_x, bad_y);
12605 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12606 KillPlayer(player);
12609 Bang(kill_x, kill_y);
12613 void TestIfPlayerTouchesBadThing(int x, int y)
12615 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12618 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12620 TestIfGoodThingHitsBadThing(x, y, move_dir);
12623 void TestIfBadThingTouchesPlayer(int x, int y)
12625 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12628 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12630 TestIfBadThingHitsGoodThing(x, y, move_dir);
12633 void TestIfFriendTouchesBadThing(int x, int y)
12635 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12638 void TestIfBadThingTouchesFriend(int x, int y)
12640 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12643 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12645 int i, kill_x = bad_x, kill_y = bad_y;
12646 static int xy[4][2] =
12654 for (i = 0; i < NUM_DIRECTIONS; i++)
12658 x = bad_x + xy[i][0];
12659 y = bad_y + xy[i][1];
12660 if (!IN_LEV_FIELD(x, y))
12663 element = Feld[x][y];
12664 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12665 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12673 if (kill_x != bad_x || kill_y != bad_y)
12674 Bang(bad_x, bad_y);
12677 void KillPlayer(struct PlayerInfo *player)
12679 int jx = player->jx, jy = player->jy;
12681 if (!player->active)
12684 /* the following code was introduced to prevent an infinite loop when calling
12686 -> CheckTriggeredElementChangeExt()
12687 -> ExecuteCustomElementAction()
12689 -> (infinitely repeating the above sequence of function calls)
12690 which occurs when killing the player while having a CE with the setting
12691 "kill player X when explosion of <player X>"; the solution using a new
12692 field "player->killed" was chosen for backwards compatibility, although
12693 clever use of the fields "player->active" etc. would probably also work */
12695 if (player->killed)
12699 player->killed = TRUE;
12701 /* remove accessible field at the player's position */
12702 Feld[jx][jy] = EL_EMPTY;
12704 /* deactivate shield (else Bang()/Explode() would not work right) */
12705 player->shield_normal_time_left = 0;
12706 player->shield_deadly_time_left = 0;
12709 BuryPlayer(player);
12712 static void KillPlayerUnlessEnemyProtected(int x, int y)
12714 if (!PLAYER_ENEMY_PROTECTED(x, y))
12715 KillPlayer(PLAYERINFO(x, y));
12718 static void KillPlayerUnlessExplosionProtected(int x, int y)
12720 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12721 KillPlayer(PLAYERINFO(x, y));
12724 void BuryPlayer(struct PlayerInfo *player)
12726 int jx = player->jx, jy = player->jy;
12728 if (!player->active)
12731 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12732 PlayLevelSound(jx, jy, SND_GAME_LOSING);
12734 player->GameOver = TRUE;
12735 RemovePlayer(player);
12738 void RemovePlayer(struct PlayerInfo *player)
12740 int jx = player->jx, jy = player->jy;
12741 int i, found = FALSE;
12743 player->present = FALSE;
12744 player->active = FALSE;
12746 if (!ExplodeField[jx][jy])
12747 StorePlayer[jx][jy] = 0;
12749 if (player->is_moving)
12750 DrawLevelField(player->last_jx, player->last_jy);
12752 for (i = 0; i < MAX_PLAYERS; i++)
12753 if (stored_player[i].active)
12757 AllPlayersGone = TRUE;
12763 #if USE_NEW_SNAP_DELAY
12764 static void setFieldForSnapping(int x, int y, int element, int direction)
12766 struct ElementInfo *ei = &element_info[element];
12767 int direction_bit = MV_DIR_TO_BIT(direction);
12768 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12769 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12770 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12772 Feld[x][y] = EL_ELEMENT_SNAPPING;
12773 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12775 ResetGfxAnimation(x, y);
12777 GfxElement[x][y] = element;
12778 GfxAction[x][y] = action;
12779 GfxDir[x][y] = direction;
12780 GfxFrame[x][y] = -1;
12785 =============================================================================
12786 checkDiagonalPushing()
12787 -----------------------------------------------------------------------------
12788 check if diagonal input device direction results in pushing of object
12789 (by checking if the alternative direction is walkable, diggable, ...)
12790 =============================================================================
12793 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12794 int x, int y, int real_dx, int real_dy)
12796 int jx, jy, dx, dy, xx, yy;
12798 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
12801 /* diagonal direction: check alternative direction */
12806 xx = jx + (dx == 0 ? real_dx : 0);
12807 yy = jy + (dy == 0 ? real_dy : 0);
12809 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12813 =============================================================================
12815 -----------------------------------------------------------------------------
12816 x, y: field next to player (non-diagonal) to try to dig to
12817 real_dx, real_dy: direction as read from input device (can be diagonal)
12818 =============================================================================
12821 int DigField(struct PlayerInfo *player,
12822 int oldx, int oldy, int x, int y,
12823 int real_dx, int real_dy, int mode)
12825 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12826 boolean player_was_pushing = player->is_pushing;
12827 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12828 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12829 int jx = oldx, jy = oldy;
12830 int dx = x - jx, dy = y - jy;
12831 int nextx = x + dx, nexty = y + dy;
12832 int move_direction = (dx == -1 ? MV_LEFT :
12833 dx == +1 ? MV_RIGHT :
12835 dy == +1 ? MV_DOWN : MV_NONE);
12836 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12837 int dig_side = MV_DIR_OPPOSITE(move_direction);
12838 int old_element = Feld[jx][jy];
12839 #if USE_FIXED_DONT_RUN_INTO
12840 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12846 if (is_player) /* function can also be called by EL_PENGUIN */
12848 if (player->MovPos == 0)
12850 player->is_digging = FALSE;
12851 player->is_collecting = FALSE;
12854 if (player->MovPos == 0) /* last pushing move finished */
12855 player->is_pushing = FALSE;
12857 if (mode == DF_NO_PUSH) /* player just stopped pushing */
12859 player->is_switching = FALSE;
12860 player->push_delay = -1;
12862 return MP_NO_ACTION;
12866 #if !USE_FIXED_DONT_RUN_INTO
12867 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12868 return MP_NO_ACTION;
12871 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12872 old_element = Back[jx][jy];
12874 /* in case of element dropped at player position, check background */
12875 else if (Back[jx][jy] != EL_EMPTY &&
12876 game.engine_version >= VERSION_IDENT(2,2,0,0))
12877 old_element = Back[jx][jy];
12879 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12880 return MP_NO_ACTION; /* field has no opening in this direction */
12882 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12883 return MP_NO_ACTION; /* field has no opening in this direction */
12885 #if USE_FIXED_DONT_RUN_INTO
12886 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12890 Feld[jx][jy] = player->artwork_element;
12891 InitMovingField(jx, jy, MV_DOWN);
12892 Store[jx][jy] = EL_ACID;
12893 ContinueMoving(jx, jy);
12894 BuryPlayer(player);
12896 return MP_DONT_RUN_INTO;
12900 #if USE_FIXED_DONT_RUN_INTO
12901 if (player_can_move && DONT_RUN_INTO(element))
12903 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12905 return MP_DONT_RUN_INTO;
12909 #if USE_FIXED_DONT_RUN_INTO
12910 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12911 return MP_NO_ACTION;
12914 #if !USE_FIXED_DONT_RUN_INTO
12915 element = Feld[x][y];
12918 collect_count = element_info[element].collect_count_initial;
12920 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12921 return MP_NO_ACTION;
12923 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12924 player_can_move = player_can_move_or_snap;
12926 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12927 game.engine_version >= VERSION_IDENT(2,2,0,0))
12929 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12930 player->index_bit, dig_side);
12931 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12932 player->index_bit, dig_side);
12934 if (element == EL_DC_LANDMINE)
12937 if (Feld[x][y] != element) /* field changed by snapping */
12940 return MP_NO_ACTION;
12943 #if USE_PLAYER_GRAVITY
12944 if (player->gravity && is_player && !player->is_auto_moving &&
12945 canFallDown(player) && move_direction != MV_DOWN &&
12946 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12947 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12949 if (game.gravity && is_player && !player->is_auto_moving &&
12950 canFallDown(player) && move_direction != MV_DOWN &&
12951 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12952 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12955 if (player_can_move &&
12956 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12958 int sound_element = SND_ELEMENT(element);
12959 int sound_action = ACTION_WALKING;
12961 if (IS_RND_GATE(element))
12963 if (!player->key[RND_GATE_NR(element)])
12964 return MP_NO_ACTION;
12966 else if (IS_RND_GATE_GRAY(element))
12968 if (!player->key[RND_GATE_GRAY_NR(element)])
12969 return MP_NO_ACTION;
12971 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12973 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12974 return MP_NO_ACTION;
12976 else if (element == EL_EXIT_OPEN ||
12977 element == EL_EM_EXIT_OPEN ||
12978 element == EL_STEEL_EXIT_OPEN ||
12979 element == EL_EM_STEEL_EXIT_OPEN ||
12980 element == EL_SP_EXIT_OPEN ||
12981 element == EL_SP_EXIT_OPENING)
12983 sound_action = ACTION_PASSING; /* player is passing exit */
12985 else if (element == EL_EMPTY)
12987 sound_action = ACTION_MOVING; /* nothing to walk on */
12990 /* play sound from background or player, whatever is available */
12991 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12992 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12994 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12996 else if (player_can_move &&
12997 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12999 if (!ACCESS_FROM(element, opposite_direction))
13000 return MP_NO_ACTION; /* field not accessible from this direction */
13002 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13003 return MP_NO_ACTION;
13005 if (IS_EM_GATE(element))
13007 if (!player->key[EM_GATE_NR(element)])
13008 return MP_NO_ACTION;
13010 else if (IS_EM_GATE_GRAY(element))
13012 if (!player->key[EM_GATE_GRAY_NR(element)])
13013 return MP_NO_ACTION;
13015 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13017 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13018 return MP_NO_ACTION;
13020 else if (IS_EMC_GATE(element))
13022 if (!player->key[EMC_GATE_NR(element)])
13023 return MP_NO_ACTION;
13025 else if (IS_EMC_GATE_GRAY(element))
13027 if (!player->key[EMC_GATE_GRAY_NR(element)])
13028 return MP_NO_ACTION;
13030 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13032 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13033 return MP_NO_ACTION;
13035 else if (element == EL_DC_GATE_WHITE ||
13036 element == EL_DC_GATE_WHITE_GRAY ||
13037 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13039 if (player->num_white_keys == 0)
13040 return MP_NO_ACTION;
13042 player->num_white_keys--;
13044 else if (IS_SP_PORT(element))
13046 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13047 element == EL_SP_GRAVITY_PORT_RIGHT ||
13048 element == EL_SP_GRAVITY_PORT_UP ||
13049 element == EL_SP_GRAVITY_PORT_DOWN)
13050 #if USE_PLAYER_GRAVITY
13051 player->gravity = !player->gravity;
13053 game.gravity = !game.gravity;
13055 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13056 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13057 element == EL_SP_GRAVITY_ON_PORT_UP ||
13058 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13059 #if USE_PLAYER_GRAVITY
13060 player->gravity = TRUE;
13062 game.gravity = TRUE;
13064 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13065 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13066 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13067 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13068 #if USE_PLAYER_GRAVITY
13069 player->gravity = FALSE;
13071 game.gravity = FALSE;
13075 /* automatically move to the next field with double speed */
13076 player->programmed_action = move_direction;
13078 if (player->move_delay_reset_counter == 0)
13080 player->move_delay_reset_counter = 2; /* two double speed steps */
13082 DOUBLE_PLAYER_SPEED(player);
13085 PlayLevelSoundAction(x, y, ACTION_PASSING);
13087 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13091 if (mode != DF_SNAP)
13093 GfxElement[x][y] = GFX_ELEMENT(element);
13094 player->is_digging = TRUE;
13097 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13099 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13100 player->index_bit, dig_side);
13102 if (mode == DF_SNAP)
13104 #if USE_NEW_SNAP_DELAY
13105 if (level.block_snap_field)
13106 setFieldForSnapping(x, y, element, move_direction);
13108 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13110 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13113 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13114 player->index_bit, dig_side);
13117 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13121 if (is_player && mode != DF_SNAP)
13123 GfxElement[x][y] = element;
13124 player->is_collecting = TRUE;
13127 if (element == EL_SPEED_PILL)
13129 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13131 else if (element == EL_EXTRA_TIME && level.time > 0)
13133 TimeLeft += level.extra_time;
13134 DrawGameValue_Time(TimeLeft);
13136 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13138 player->shield_normal_time_left += level.shield_normal_time;
13139 if (element == EL_SHIELD_DEADLY)
13140 player->shield_deadly_time_left += level.shield_deadly_time;
13142 else if (element == EL_DYNAMITE ||
13143 element == EL_EM_DYNAMITE ||
13144 element == EL_SP_DISK_RED)
13146 if (player->inventory_size < MAX_INVENTORY_SIZE)
13147 player->inventory_element[player->inventory_size++] = element;
13149 DrawGameDoorValues();
13151 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13153 player->dynabomb_count++;
13154 player->dynabombs_left++;
13156 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13158 player->dynabomb_size++;
13160 else if (element == EL_DYNABOMB_INCREASE_POWER)
13162 player->dynabomb_xl = TRUE;
13164 else if (IS_KEY(element))
13166 player->key[KEY_NR(element)] = TRUE;
13168 DrawGameDoorValues();
13170 else if (element == EL_DC_KEY_WHITE)
13172 player->num_white_keys++;
13174 /* display white keys? */
13175 /* DrawGameDoorValues(); */
13177 else if (IS_ENVELOPE(element))
13179 player->show_envelope = element;
13181 else if (element == EL_EMC_LENSES)
13183 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13185 RedrawAllInvisibleElementsForLenses();
13187 else if (element == EL_EMC_MAGNIFIER)
13189 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13191 RedrawAllInvisibleElementsForMagnifier();
13193 else if (IS_DROPPABLE(element) ||
13194 IS_THROWABLE(element)) /* can be collected and dropped */
13198 if (collect_count == 0)
13199 player->inventory_infinite_element = element;
13201 for (i = 0; i < collect_count; i++)
13202 if (player->inventory_size < MAX_INVENTORY_SIZE)
13203 player->inventory_element[player->inventory_size++] = element;
13205 DrawGameDoorValues();
13207 else if (collect_count > 0)
13209 local_player->gems_still_needed -= collect_count;
13210 if (local_player->gems_still_needed < 0)
13211 local_player->gems_still_needed = 0;
13213 DrawGameValue_Emeralds(local_player->gems_still_needed);
13216 RaiseScoreElement(element);
13217 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13220 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13221 player->index_bit, dig_side);
13223 if (mode == DF_SNAP)
13225 #if USE_NEW_SNAP_DELAY
13226 if (level.block_snap_field)
13227 setFieldForSnapping(x, y, element, move_direction);
13229 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13231 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13234 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13235 player->index_bit, dig_side);
13238 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13240 if (mode == DF_SNAP && element != EL_BD_ROCK)
13241 return MP_NO_ACTION;
13243 if (CAN_FALL(element) && dy)
13244 return MP_NO_ACTION;
13246 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13247 !(element == EL_SPRING && level.use_spring_bug))
13248 return MP_NO_ACTION;
13250 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13251 ((move_direction & MV_VERTICAL &&
13252 ((element_info[element].move_pattern & MV_LEFT &&
13253 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13254 (element_info[element].move_pattern & MV_RIGHT &&
13255 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13256 (move_direction & MV_HORIZONTAL &&
13257 ((element_info[element].move_pattern & MV_UP &&
13258 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13259 (element_info[element].move_pattern & MV_DOWN &&
13260 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13261 return MP_NO_ACTION;
13263 /* do not push elements already moving away faster than player */
13264 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13265 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13266 return MP_NO_ACTION;
13268 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13270 if (player->push_delay_value == -1 || !player_was_pushing)
13271 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13273 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13275 if (player->push_delay_value == -1)
13276 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13278 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13280 if (!player->is_pushing)
13281 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13284 player->is_pushing = TRUE;
13285 player->is_active = TRUE;
13287 if (!(IN_LEV_FIELD(nextx, nexty) &&
13288 (IS_FREE(nextx, nexty) ||
13289 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13290 IS_SB_ELEMENT(element)))))
13291 return MP_NO_ACTION;
13293 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13294 return MP_NO_ACTION;
13296 if (player->push_delay == -1) /* new pushing; restart delay */
13297 player->push_delay = 0;
13299 if (player->push_delay < player->push_delay_value &&
13300 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13301 element != EL_SPRING && element != EL_BALLOON)
13303 /* make sure that there is no move delay before next try to push */
13304 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13305 player->move_delay = 0;
13307 return MP_NO_ACTION;
13310 if (IS_SB_ELEMENT(element))
13312 if (element == EL_SOKOBAN_FIELD_FULL)
13314 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13315 local_player->sokobanfields_still_needed++;
13318 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13320 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13321 local_player->sokobanfields_still_needed--;
13324 Feld[x][y] = EL_SOKOBAN_OBJECT;
13326 if (Back[x][y] == Back[nextx][nexty])
13327 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13328 else if (Back[x][y] != 0)
13329 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13332 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13335 if (local_player->sokobanfields_still_needed == 0 &&
13336 game.emulation == EMU_SOKOBAN)
13338 PlayerWins(player);
13340 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13344 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13346 InitMovingField(x, y, move_direction);
13347 GfxAction[x][y] = ACTION_PUSHING;
13349 if (mode == DF_SNAP)
13350 ContinueMoving(x, y);
13352 MovPos[x][y] = (dx != 0 ? dx : dy);
13354 Pushed[x][y] = TRUE;
13355 Pushed[nextx][nexty] = TRUE;
13357 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13358 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13360 player->push_delay_value = -1; /* get new value later */
13362 /* check for element change _after_ element has been pushed */
13363 if (game.use_change_when_pushing_bug)
13365 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13366 player->index_bit, dig_side);
13367 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13368 player->index_bit, dig_side);
13371 else if (IS_SWITCHABLE(element))
13373 if (PLAYER_SWITCHING(player, x, y))
13375 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13376 player->index_bit, dig_side);
13381 player->is_switching = TRUE;
13382 player->switch_x = x;
13383 player->switch_y = y;
13385 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13387 if (element == EL_ROBOT_WHEEL)
13389 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13393 DrawLevelField(x, y);
13395 else if (element == EL_SP_TERMINAL)
13399 SCAN_PLAYFIELD(xx, yy)
13401 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13403 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13404 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13407 else if (IS_BELT_SWITCH(element))
13409 ToggleBeltSwitch(x, y);
13411 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13412 element == EL_SWITCHGATE_SWITCH_DOWN ||
13413 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13414 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13416 ToggleSwitchgateSwitch(x, y);
13418 else if (element == EL_LIGHT_SWITCH ||
13419 element == EL_LIGHT_SWITCH_ACTIVE)
13421 ToggleLightSwitch(x, y);
13423 else if (element == EL_TIMEGATE_SWITCH ||
13424 element == EL_DC_TIMEGATE_SWITCH)
13426 ActivateTimegateSwitch(x, y);
13428 else if (element == EL_BALLOON_SWITCH_LEFT ||
13429 element == EL_BALLOON_SWITCH_RIGHT ||
13430 element == EL_BALLOON_SWITCH_UP ||
13431 element == EL_BALLOON_SWITCH_DOWN ||
13432 element == EL_BALLOON_SWITCH_NONE ||
13433 element == EL_BALLOON_SWITCH_ANY)
13435 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13436 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13437 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13438 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13439 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13442 else if (element == EL_LAMP)
13444 Feld[x][y] = EL_LAMP_ACTIVE;
13445 local_player->lights_still_needed--;
13447 ResetGfxAnimation(x, y);
13448 DrawLevelField(x, y);
13450 else if (element == EL_TIME_ORB_FULL)
13452 Feld[x][y] = EL_TIME_ORB_EMPTY;
13454 if (level.time > 0 || level.use_time_orb_bug)
13456 TimeLeft += level.time_orb_time;
13457 DrawGameValue_Time(TimeLeft);
13460 ResetGfxAnimation(x, y);
13461 DrawLevelField(x, y);
13463 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13464 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13468 game.ball_state = !game.ball_state;
13470 SCAN_PLAYFIELD(xx, yy)
13472 int e = Feld[xx][yy];
13474 if (game.ball_state)
13476 if (e == EL_EMC_MAGIC_BALL)
13477 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13478 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13479 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13483 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13484 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13485 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13486 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13491 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13492 player->index_bit, dig_side);
13494 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13495 player->index_bit, dig_side);
13497 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13498 player->index_bit, dig_side);
13504 if (!PLAYER_SWITCHING(player, x, y))
13506 player->is_switching = TRUE;
13507 player->switch_x = x;
13508 player->switch_y = y;
13510 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13511 player->index_bit, dig_side);
13512 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13513 player->index_bit, dig_side);
13515 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13516 player->index_bit, dig_side);
13517 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13518 player->index_bit, dig_side);
13521 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13522 player->index_bit, dig_side);
13523 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13524 player->index_bit, dig_side);
13526 return MP_NO_ACTION;
13529 player->push_delay = -1;
13531 if (is_player) /* function can also be called by EL_PENGUIN */
13533 if (Feld[x][y] != element) /* really digged/collected something */
13535 player->is_collecting = !player->is_digging;
13536 player->is_active = TRUE;
13543 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13545 int jx = player->jx, jy = player->jy;
13546 int x = jx + dx, y = jy + dy;
13547 int snap_direction = (dx == -1 ? MV_LEFT :
13548 dx == +1 ? MV_RIGHT :
13550 dy == +1 ? MV_DOWN : MV_NONE);
13551 boolean can_continue_snapping = (level.continuous_snapping &&
13552 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13554 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13557 if (!player->active || !IN_LEV_FIELD(x, y))
13565 if (player->MovPos == 0)
13566 player->is_pushing = FALSE;
13568 player->is_snapping = FALSE;
13570 if (player->MovPos == 0)
13572 player->is_moving = FALSE;
13573 player->is_digging = FALSE;
13574 player->is_collecting = FALSE;
13580 #if USE_NEW_CONTINUOUS_SNAPPING
13581 /* prevent snapping with already pressed snap key when not allowed */
13582 if (player->is_snapping && !can_continue_snapping)
13585 if (player->is_snapping)
13589 player->MovDir = snap_direction;
13591 if (player->MovPos == 0)
13593 player->is_moving = FALSE;
13594 player->is_digging = FALSE;
13595 player->is_collecting = FALSE;
13598 player->is_dropping = FALSE;
13599 player->is_dropping_pressed = FALSE;
13600 player->drop_pressed_delay = 0;
13602 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13605 player->is_snapping = TRUE;
13606 player->is_active = TRUE;
13608 if (player->MovPos == 0)
13610 player->is_moving = FALSE;
13611 player->is_digging = FALSE;
13612 player->is_collecting = FALSE;
13615 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13616 DrawLevelField(player->last_jx, player->last_jy);
13618 DrawLevelField(x, y);
13623 boolean DropElement(struct PlayerInfo *player)
13625 int old_element, new_element;
13626 int dropx = player->jx, dropy = player->jy;
13627 int drop_direction = player->MovDir;
13628 int drop_side = drop_direction;
13629 int drop_element = (player->inventory_size > 0 ?
13630 player->inventory_element[player->inventory_size - 1] :
13631 player->inventory_infinite_element != EL_UNDEFINED ?
13632 player->inventory_infinite_element :
13633 player->dynabombs_left > 0 ?
13634 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13637 player->is_dropping_pressed = TRUE;
13639 /* do not drop an element on top of another element; when holding drop key
13640 pressed without moving, dropped element must move away before the next
13641 element can be dropped (this is especially important if the next element
13642 is dynamite, which can be placed on background for historical reasons) */
13643 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13646 if (IS_THROWABLE(drop_element))
13648 dropx += GET_DX_FROM_DIR(drop_direction);
13649 dropy += GET_DY_FROM_DIR(drop_direction);
13651 if (!IN_LEV_FIELD(dropx, dropy))
13655 old_element = Feld[dropx][dropy]; /* old element at dropping position */
13656 new_element = drop_element; /* default: no change when dropping */
13658 /* check if player is active, not moving and ready to drop */
13659 if (!player->active || player->MovPos || player->drop_delay > 0)
13662 /* check if player has anything that can be dropped */
13663 if (new_element == EL_UNDEFINED)
13666 /* check if drop key was pressed long enough for EM style dynamite */
13667 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13670 /* check if anything can be dropped at the current position */
13671 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13674 /* collected custom elements can only be dropped on empty fields */
13675 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13678 if (old_element != EL_EMPTY)
13679 Back[dropx][dropy] = old_element; /* store old element on this field */
13681 ResetGfxAnimation(dropx, dropy);
13682 ResetRandomAnimationValue(dropx, dropy);
13684 if (player->inventory_size > 0 ||
13685 player->inventory_infinite_element != EL_UNDEFINED)
13687 if (player->inventory_size > 0)
13689 player->inventory_size--;
13691 DrawGameDoorValues();
13693 if (new_element == EL_DYNAMITE)
13694 new_element = EL_DYNAMITE_ACTIVE;
13695 else if (new_element == EL_EM_DYNAMITE)
13696 new_element = EL_EM_DYNAMITE_ACTIVE;
13697 else if (new_element == EL_SP_DISK_RED)
13698 new_element = EL_SP_DISK_RED_ACTIVE;
13701 Feld[dropx][dropy] = new_element;
13703 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13704 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13705 el2img(Feld[dropx][dropy]), 0);
13707 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13709 /* needed if previous element just changed to "empty" in the last frame */
13710 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13712 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13713 player->index_bit, drop_side);
13714 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13716 player->index_bit, drop_side);
13718 TestIfElementTouchesCustomElement(dropx, dropy);
13720 else /* player is dropping a dyna bomb */
13722 player->dynabombs_left--;
13724 Feld[dropx][dropy] = new_element;
13726 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13727 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13728 el2img(Feld[dropx][dropy]), 0);
13730 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13733 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13734 InitField_WithBug1(dropx, dropy, FALSE);
13736 new_element = Feld[dropx][dropy]; /* element might have changed */
13738 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13739 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13741 int move_direction, nextx, nexty;
13743 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13744 MovDir[dropx][dropy] = drop_direction;
13746 move_direction = MovDir[dropx][dropy];
13747 nextx = dropx + GET_DX_FROM_DIR(move_direction);
13748 nexty = dropy + GET_DY_FROM_DIR(move_direction);
13750 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13752 #if USE_FIX_IMPACT_COLLISION
13753 /* do not cause impact style collision by dropping elements that can fall */
13754 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13756 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13760 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13761 player->is_dropping = TRUE;
13763 player->drop_pressed_delay = 0;
13764 player->is_dropping_pressed = FALSE;
13766 player->drop_x = dropx;
13767 player->drop_y = dropy;
13772 /* ------------------------------------------------------------------------- */
13773 /* game sound playing functions */
13774 /* ------------------------------------------------------------------------- */
13776 static int *loop_sound_frame = NULL;
13777 static int *loop_sound_volume = NULL;
13779 void InitPlayLevelSound()
13781 int num_sounds = getSoundListSize();
13783 checked_free(loop_sound_frame);
13784 checked_free(loop_sound_volume);
13786 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
13787 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13790 static void PlayLevelSound(int x, int y, int nr)
13792 int sx = SCREENX(x), sy = SCREENY(y);
13793 int volume, stereo_position;
13794 int max_distance = 8;
13795 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13797 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13798 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13801 if (!IN_LEV_FIELD(x, y) ||
13802 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13803 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13806 volume = SOUND_MAX_VOLUME;
13808 if (!IN_SCR_FIELD(sx, sy))
13810 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13811 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13813 volume -= volume * (dx > dy ? dx : dy) / max_distance;
13816 stereo_position = (SOUND_MAX_LEFT +
13817 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13818 (SCR_FIELDX + 2 * max_distance));
13820 if (IS_LOOP_SOUND(nr))
13822 /* This assures that quieter loop sounds do not overwrite louder ones,
13823 while restarting sound volume comparison with each new game frame. */
13825 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13828 loop_sound_volume[nr] = volume;
13829 loop_sound_frame[nr] = FrameCounter;
13832 PlaySoundExt(nr, volume, stereo_position, type);
13835 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13837 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13838 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13839 y < LEVELY(BY1) ? LEVELY(BY1) :
13840 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13844 static void PlayLevelSoundAction(int x, int y, int action)
13846 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13849 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13851 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13853 if (sound_effect != SND_UNDEFINED)
13854 PlayLevelSound(x, y, sound_effect);
13857 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13860 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13862 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13863 PlayLevelSound(x, y, sound_effect);
13866 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13868 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13870 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13871 PlayLevelSound(x, y, sound_effect);
13874 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13876 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13878 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13879 StopSound(sound_effect);
13882 static void PlayLevelMusic()
13884 if (levelset.music[level_nr] != MUS_UNDEFINED)
13885 PlayMusic(levelset.music[level_nr]); /* from config file */
13887 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13890 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13892 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13893 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13894 int x = xx - 1 - offset;
13895 int y = yy - 1 - offset;
13900 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13904 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13908 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13912 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13916 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13920 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13924 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13927 case SAMPLE_android_clone:
13928 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13931 case SAMPLE_android_move:
13932 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13935 case SAMPLE_spring:
13936 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13940 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13944 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13947 case SAMPLE_eater_eat:
13948 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13952 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13955 case SAMPLE_collect:
13956 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13959 case SAMPLE_diamond:
13960 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13963 case SAMPLE_squash:
13964 /* !!! CHECK THIS !!! */
13966 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13968 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13972 case SAMPLE_wonderfall:
13973 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13977 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13981 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13985 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13989 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13993 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13997 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14000 case SAMPLE_wonder:
14001 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14005 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14008 case SAMPLE_exit_open:
14009 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14012 case SAMPLE_exit_leave:
14013 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14016 case SAMPLE_dynamite:
14017 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14021 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14025 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14029 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14033 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14037 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14041 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14045 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14051 void ChangeTime(int value)
14053 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14057 /* EMC game engine uses value from time counter of RND game engine */
14058 level.native_em_level->lev->time = *time;
14060 DrawGameValue_Time(*time);
14063 void RaiseScore(int value)
14065 /* EMC game engine and RND game engine have separate score counters */
14066 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14067 &level.native_em_level->lev->score : &local_player->score);
14071 DrawGameValue_Score(*score);
14075 void RaiseScore(int value)
14077 local_player->score += value;
14079 DrawGameValue_Score(local_player->score);
14082 void RaiseScoreElement(int element)
14087 case EL_BD_DIAMOND:
14088 case EL_EMERALD_YELLOW:
14089 case EL_EMERALD_RED:
14090 case EL_EMERALD_PURPLE:
14091 case EL_SP_INFOTRON:
14092 RaiseScore(level.score[SC_EMERALD]);
14095 RaiseScore(level.score[SC_DIAMOND]);
14098 RaiseScore(level.score[SC_CRYSTAL]);
14101 RaiseScore(level.score[SC_PEARL]);
14104 case EL_BD_BUTTERFLY:
14105 case EL_SP_ELECTRON:
14106 RaiseScore(level.score[SC_BUG]);
14109 case EL_BD_FIREFLY:
14110 case EL_SP_SNIKSNAK:
14111 RaiseScore(level.score[SC_SPACESHIP]);
14114 case EL_DARK_YAMYAM:
14115 RaiseScore(level.score[SC_YAMYAM]);
14118 RaiseScore(level.score[SC_ROBOT]);
14121 RaiseScore(level.score[SC_PACMAN]);
14124 RaiseScore(level.score[SC_NUT]);
14127 case EL_EM_DYNAMITE:
14128 case EL_SP_DISK_RED:
14129 case EL_DYNABOMB_INCREASE_NUMBER:
14130 case EL_DYNABOMB_INCREASE_SIZE:
14131 case EL_DYNABOMB_INCREASE_POWER:
14132 RaiseScore(level.score[SC_DYNAMITE]);
14134 case EL_SHIELD_NORMAL:
14135 case EL_SHIELD_DEADLY:
14136 RaiseScore(level.score[SC_SHIELD]);
14138 case EL_EXTRA_TIME:
14139 RaiseScore(level.extra_time_score);
14153 case EL_DC_KEY_WHITE:
14154 RaiseScore(level.score[SC_KEY]);
14157 RaiseScore(element_info[element].collect_score);
14162 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14164 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14166 #if defined(NETWORK_AVALIABLE)
14167 if (options.network)
14168 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14177 FadeSkipNextFadeIn();
14179 fading = fading_none;
14183 OpenDoor(DOOR_CLOSE_1);
14186 game_status = GAME_MODE_MAIN;
14189 DrawAndFadeInMainMenu(REDRAW_FIELD);
14197 FadeOut(REDRAW_FIELD);
14200 game_status = GAME_MODE_MAIN;
14202 DrawAndFadeInMainMenu(REDRAW_FIELD);
14206 else /* continue playing the game */
14208 if (tape.playing && tape.deactivate_display)
14209 TapeDeactivateDisplayOff(TRUE);
14211 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14213 if (tape.playing && tape.deactivate_display)
14214 TapeDeactivateDisplayOn();
14218 void RequestQuitGame(boolean ask_if_really_quit)
14220 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14221 boolean skip_request = AllPlayersGone || quick_quit;
14223 RequestQuitGameExt(skip_request, quick_quit,
14224 "Do you really want to quit the game ?");
14228 /* ------------------------------------------------------------------------- */
14229 /* random generator functions */
14230 /* ------------------------------------------------------------------------- */
14232 unsigned int InitEngineRandom_RND(long seed)
14234 game.num_random_calls = 0;
14237 unsigned int rnd_seed = InitEngineRandom(seed);
14239 printf("::: START RND: %d\n", rnd_seed);
14244 return InitEngineRandom(seed);
14250 unsigned int RND(int max)
14254 game.num_random_calls++;
14256 return GetEngineRandom(max);
14263 /* ------------------------------------------------------------------------- */
14264 /* game engine snapshot handling functions */
14265 /* ------------------------------------------------------------------------- */
14267 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14269 struct EngineSnapshotInfo
14271 /* runtime values for custom element collect score */
14272 int collect_score[NUM_CUSTOM_ELEMENTS];
14274 /* runtime values for group element choice position */
14275 int choice_pos[NUM_GROUP_ELEMENTS];
14277 /* runtime values for belt position animations */
14278 int belt_graphic[4 * NUM_BELT_PARTS];
14279 int belt_anim_mode[4 * NUM_BELT_PARTS];
14282 struct EngineSnapshotNodeInfo
14289 static struct EngineSnapshotInfo engine_snapshot_rnd;
14290 static ListNode *engine_snapshot_list = NULL;
14291 static char *snapshot_level_identifier = NULL;
14292 static int snapshot_level_nr = -1;
14294 void FreeEngineSnapshot()
14296 while (engine_snapshot_list != NULL)
14297 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14300 setString(&snapshot_level_identifier, NULL);
14301 snapshot_level_nr = -1;
14304 static void SaveEngineSnapshotValues_RND()
14306 static int belt_base_active_element[4] =
14308 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14309 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14310 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14311 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14315 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14317 int element = EL_CUSTOM_START + i;
14319 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14322 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14324 int element = EL_GROUP_START + i;
14326 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14329 for (i = 0; i < 4; i++)
14331 for (j = 0; j < NUM_BELT_PARTS; j++)
14333 int element = belt_base_active_element[i] + j;
14334 int graphic = el2img(element);
14335 int anim_mode = graphic_info[graphic].anim_mode;
14337 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14338 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14343 static void LoadEngineSnapshotValues_RND()
14345 unsigned long num_random_calls = game.num_random_calls;
14348 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14350 int element = EL_CUSTOM_START + i;
14352 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14355 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14357 int element = EL_GROUP_START + i;
14359 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14362 for (i = 0; i < 4; i++)
14364 for (j = 0; j < NUM_BELT_PARTS; j++)
14366 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14367 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14369 graphic_info[graphic].anim_mode = anim_mode;
14373 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14375 InitRND(tape.random_seed);
14376 for (i = 0; i < num_random_calls; i++)
14380 if (game.num_random_calls != num_random_calls)
14382 Error(ERR_INFO, "number of random calls out of sync");
14383 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14384 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14385 Error(ERR_EXIT, "this should not happen -- please debug");
14389 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14391 struct EngineSnapshotNodeInfo *bi =
14392 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14394 bi->buffer_orig = buffer;
14395 bi->buffer_copy = checked_malloc(size);
14398 memcpy(bi->buffer_copy, buffer, size);
14400 addNodeToList(&engine_snapshot_list, NULL, bi);
14403 void SaveEngineSnapshot()
14405 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14407 if (level_editor_test_game) /* do not save snapshots from editor */
14410 /* copy some special values to a structure better suited for the snapshot */
14412 SaveEngineSnapshotValues_RND();
14413 SaveEngineSnapshotValues_EM();
14415 /* save values stored in special snapshot structure */
14417 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14418 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14420 /* save further RND engine values */
14422 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14423 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14424 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14426 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14427 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14428 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14429 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14431 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14432 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14433 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14434 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14435 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14437 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14438 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14439 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14441 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14443 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14445 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14446 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14448 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14449 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14450 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14451 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14452 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14453 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14454 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14455 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14456 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14457 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14458 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14459 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14460 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14461 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14462 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14463 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14464 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14465 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14467 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14468 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14470 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14471 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14472 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14474 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14475 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14477 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14478 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14479 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14480 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14481 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14483 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14484 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14486 /* save level identification information */
14488 setString(&snapshot_level_identifier, leveldir_current->identifier);
14489 snapshot_level_nr = level_nr;
14492 ListNode *node = engine_snapshot_list;
14495 while (node != NULL)
14497 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14502 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14506 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14508 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14511 void LoadEngineSnapshot()
14513 ListNode *node = engine_snapshot_list;
14515 if (engine_snapshot_list == NULL)
14518 while (node != NULL)
14520 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14525 /* restore special values from snapshot structure */
14527 LoadEngineSnapshotValues_RND();
14528 LoadEngineSnapshotValues_EM();
14531 boolean CheckEngineSnapshot()
14533 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14534 snapshot_level_nr == level_nr);
14538 /* ---------- new game button stuff ---------------------------------------- */
14540 /* graphic position values for game buttons */
14541 #define GAME_BUTTON_XSIZE 30
14542 #define GAME_BUTTON_YSIZE 30
14543 #define GAME_BUTTON_XPOS 5
14544 #define GAME_BUTTON_YPOS 215
14545 #define SOUND_BUTTON_XPOS 5
14546 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14548 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14549 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14550 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14551 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14552 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14553 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14561 } gamebutton_info[NUM_GAME_BUTTONS] =
14565 &game.button.stop.x, &game.button.stop.y,
14566 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14571 &game.button.pause.x, &game.button.pause.y,
14572 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14573 GAME_CTRL_ID_PAUSE,
14577 &game.button.play.x, &game.button.play.y,
14578 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14583 &game.button.sound_music.x, &game.button.sound_music.y,
14584 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14585 SOUND_CTRL_ID_MUSIC,
14586 "background music on/off"
14589 &game.button.sound_loops.x, &game.button.sound_loops.y,
14590 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
14591 SOUND_CTRL_ID_LOOPS,
14592 "sound loops on/off"
14595 &game.button.sound_simple.x,&game.button.sound_simple.y,
14596 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
14597 SOUND_CTRL_ID_SIMPLE,
14598 "normal sounds on/off"
14602 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14607 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14608 GAME_CTRL_ID_PAUSE,
14612 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14617 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14618 SOUND_CTRL_ID_MUSIC,
14619 "background music on/off"
14622 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
14623 SOUND_CTRL_ID_LOOPS,
14624 "sound loops on/off"
14627 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
14628 SOUND_CTRL_ID_SIMPLE,
14629 "normal sounds on/off"
14634 void CreateGameButtons()
14638 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14640 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14641 struct GadgetInfo *gi;
14644 unsigned long event_mask;
14646 int gd_xoffset, gd_yoffset;
14647 int gd_x1, gd_x2, gd_y1, gd_y2;
14650 x = DX + *gamebutton_info[i].x;
14651 y = DY + *gamebutton_info[i].y;
14652 gd_xoffset = gamebutton_info[i].gd_x;
14653 gd_yoffset = gamebutton_info[i].gd_y;
14654 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14655 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14657 if (id == GAME_CTRL_ID_STOP ||
14658 id == GAME_CTRL_ID_PAUSE ||
14659 id == GAME_CTRL_ID_PLAY)
14661 button_type = GD_TYPE_NORMAL_BUTTON;
14663 event_mask = GD_EVENT_RELEASED;
14664 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14665 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14669 button_type = GD_TYPE_CHECK_BUTTON;
14671 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14672 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14673 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14674 event_mask = GD_EVENT_PRESSED;
14675 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
14676 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14679 gi = CreateGadget(GDI_CUSTOM_ID, id,
14680 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14685 GDI_X, DX + gd_xoffset,
14686 GDI_Y, DY + gd_yoffset,
14688 GDI_WIDTH, GAME_BUTTON_XSIZE,
14689 GDI_HEIGHT, GAME_BUTTON_YSIZE,
14690 GDI_TYPE, button_type,
14691 GDI_STATE, GD_BUTTON_UNPRESSED,
14692 GDI_CHECKED, checked,
14693 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14694 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14695 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14696 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14697 GDI_EVENT_MASK, event_mask,
14698 GDI_CALLBACK_ACTION, HandleGameButtons,
14702 Error(ERR_EXIT, "cannot create gadget");
14704 game_gadget[id] = gi;
14708 void FreeGameButtons()
14712 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14713 FreeGadget(game_gadget[i]);
14716 static void MapGameButtons()
14720 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14721 MapGadget(game_gadget[i]);
14724 void UnmapGameButtons()
14728 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14729 UnmapGadget(game_gadget[i]);
14732 static void HandleGameButtons(struct GadgetInfo *gi)
14734 int id = gi->custom_id;
14736 if (game_status != GAME_MODE_PLAYING)
14741 case GAME_CTRL_ID_STOP:
14745 RequestQuitGame(TRUE);
14748 case GAME_CTRL_ID_PAUSE:
14749 if (options.network)
14751 #if defined(NETWORK_AVALIABLE)
14753 SendToServer_ContinuePlaying();
14755 SendToServer_PausePlaying();
14759 TapeTogglePause(TAPE_TOGGLE_MANUAL);
14762 case GAME_CTRL_ID_PLAY:
14765 #if defined(NETWORK_AVALIABLE)
14766 if (options.network)
14767 SendToServer_ContinuePlaying();
14771 tape.pausing = FALSE;
14772 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14777 case SOUND_CTRL_ID_MUSIC:
14778 if (setup.sound_music)
14780 setup.sound_music = FALSE;
14783 else if (audio.music_available)
14785 setup.sound = setup.sound_music = TRUE;
14787 SetAudioMode(setup.sound);
14793 case SOUND_CTRL_ID_LOOPS:
14794 if (setup.sound_loops)
14795 setup.sound_loops = FALSE;
14796 else if (audio.loops_available)
14798 setup.sound = setup.sound_loops = TRUE;
14799 SetAudioMode(setup.sound);
14803 case SOUND_CTRL_ID_SIMPLE:
14804 if (setup.sound_simple)
14805 setup.sound_simple = FALSE;
14806 else if (audio.sound_available)
14808 setup.sound = setup.sound_simple = TRUE;
14809 SetAudioMode(setup.sound);