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;
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 */
2599 boolean do_fading = (game_status == GAME_MODE_MAIN);
2602 game_status = GAME_MODE_PLAYING;
2606 /* don't play tapes over network */
2607 network_playing = (options.network && !tape.playing);
2609 for (i = 0; i < MAX_PLAYERS; i++)
2611 struct PlayerInfo *player = &stored_player[i];
2613 player->index_nr = i;
2614 player->index_bit = (1 << i);
2615 player->element_nr = EL_PLAYER_1 + i;
2617 player->present = FALSE;
2618 player->active = FALSE;
2619 player->killed = FALSE;
2622 player->effective_action = 0;
2623 player->programmed_action = 0;
2626 player->score_final = 0;
2628 player->gems_still_needed = level.gems_needed;
2629 player->sokobanfields_still_needed = 0;
2630 player->lights_still_needed = 0;
2631 player->friends_still_needed = 0;
2633 for (j = 0; j < MAX_NUM_KEYS; j++)
2634 player->key[j] = FALSE;
2636 player->num_white_keys = 0;
2638 player->dynabomb_count = 0;
2639 player->dynabomb_size = 1;
2640 player->dynabombs_left = 0;
2641 player->dynabomb_xl = FALSE;
2643 player->MovDir = MV_NONE;
2646 player->GfxDir = MV_NONE;
2647 player->GfxAction = ACTION_DEFAULT;
2649 player->StepFrame = 0;
2651 player->use_murphy = FALSE;
2652 player->artwork_element =
2653 (level.use_artwork_element[i] ? level.artwork_element[i] :
2654 player->element_nr);
2656 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2657 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2659 player->gravity = level.initial_player_gravity[i];
2661 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2663 player->actual_frame_counter = 0;
2665 player->step_counter = 0;
2667 player->last_move_dir = MV_NONE;
2669 player->is_active = FALSE;
2671 player->is_waiting = FALSE;
2672 player->is_moving = FALSE;
2673 player->is_auto_moving = FALSE;
2674 player->is_digging = FALSE;
2675 player->is_snapping = FALSE;
2676 player->is_collecting = FALSE;
2677 player->is_pushing = FALSE;
2678 player->is_switching = FALSE;
2679 player->is_dropping = FALSE;
2680 player->is_dropping_pressed = FALSE;
2682 player->is_bored = FALSE;
2683 player->is_sleeping = FALSE;
2685 player->frame_counter_bored = -1;
2686 player->frame_counter_sleeping = -1;
2688 player->anim_delay_counter = 0;
2689 player->post_delay_counter = 0;
2691 player->dir_waiting = MV_NONE;
2692 player->action_waiting = ACTION_DEFAULT;
2693 player->last_action_waiting = ACTION_DEFAULT;
2694 player->special_action_bored = ACTION_DEFAULT;
2695 player->special_action_sleeping = ACTION_DEFAULT;
2697 player->switch_x = -1;
2698 player->switch_y = -1;
2700 player->drop_x = -1;
2701 player->drop_y = -1;
2703 player->show_envelope = 0;
2705 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2707 player->push_delay = -1; /* initialized when pushing starts */
2708 player->push_delay_value = game.initial_push_delay_value;
2710 player->drop_delay = 0;
2711 player->drop_pressed_delay = 0;
2713 player->last_jx = -1;
2714 player->last_jy = -1;
2718 player->shield_normal_time_left = 0;
2719 player->shield_deadly_time_left = 0;
2721 player->inventory_infinite_element = EL_UNDEFINED;
2722 player->inventory_size = 0;
2724 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2725 SnapField(player, 0, 0);
2727 player->LevelSolved = FALSE;
2728 player->GameOver = FALSE;
2730 player->LevelSolved_GameWon = FALSE;
2731 player->LevelSolved_GameEnd = FALSE;
2732 player->LevelSolved_PanelOff = FALSE;
2733 player->LevelSolved_SaveTape = FALSE;
2734 player->LevelSolved_SaveScore = FALSE;
2737 network_player_action_received = FALSE;
2739 #if defined(NETWORK_AVALIABLE)
2740 /* initial null action */
2741 if (network_playing)
2742 SendToServer_MovePlayer(MV_NONE);
2751 TimeLeft = level.time;
2754 ScreenMovDir = MV_NONE;
2758 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2760 AllPlayersGone = FALSE;
2762 game.yamyam_content_nr = 0;
2763 game.magic_wall_active = FALSE;
2764 game.magic_wall_time_left = 0;
2765 game.light_time_left = 0;
2766 game.timegate_time_left = 0;
2767 game.switchgate_pos = 0;
2768 game.wind_direction = level.wind_direction_initial;
2770 #if !USE_PLAYER_GRAVITY
2771 game.gravity = FALSE;
2772 game.explosions_delayed = TRUE;
2775 game.lenses_time_left = 0;
2776 game.magnify_time_left = 0;
2778 game.ball_state = level.ball_state_initial;
2779 game.ball_content_nr = 0;
2781 game.envelope_active = FALSE;
2783 /* set focus to local player for network games, else to all players */
2784 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2785 game.centered_player_nr_next = game.centered_player_nr;
2786 game.set_centered_player = FALSE;
2788 if (network_playing && tape.recording)
2790 /* store client dependent player focus when recording network games */
2791 tape.centered_player_nr_next = game.centered_player_nr_next;
2792 tape.set_centered_player = TRUE;
2795 for (i = 0; i < NUM_BELTS; i++)
2797 game.belt_dir[i] = MV_NONE;
2798 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2801 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2802 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2804 SCAN_PLAYFIELD(x, y)
2806 Feld[x][y] = level.field[x][y];
2807 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2808 ChangeDelay[x][y] = 0;
2809 ChangePage[x][y] = -1;
2810 #if USE_NEW_CUSTOM_VALUE
2811 CustomValue[x][y] = 0; /* initialized in InitField() */
2813 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2815 WasJustMoving[x][y] = 0;
2816 WasJustFalling[x][y] = 0;
2817 CheckCollision[x][y] = 0;
2818 CheckImpact[x][y] = 0;
2820 Pushed[x][y] = FALSE;
2822 ChangeCount[x][y] = 0;
2823 ChangeEvent[x][y] = -1;
2825 ExplodePhase[x][y] = 0;
2826 ExplodeDelay[x][y] = 0;
2827 ExplodeField[x][y] = EX_TYPE_NONE;
2829 RunnerVisit[x][y] = 0;
2830 PlayerVisit[x][y] = 0;
2833 GfxRandom[x][y] = INIT_GFX_RANDOM();
2834 GfxElement[x][y] = EL_UNDEFINED;
2835 GfxAction[x][y] = ACTION_DEFAULT;
2836 GfxDir[x][y] = MV_NONE;
2839 SCAN_PLAYFIELD(x, y)
2841 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2843 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2845 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2848 InitField(x, y, TRUE);
2853 for (i = 0; i < MAX_PLAYERS; i++)
2855 struct PlayerInfo *player = &stored_player[i];
2857 /* set number of special actions for bored and sleeping animation */
2858 player->num_special_action_bored =
2859 get_num_special_action(player->artwork_element,
2860 ACTION_BORING_1, ACTION_BORING_LAST);
2861 player->num_special_action_sleeping =
2862 get_num_special_action(player->artwork_element,
2863 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2866 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2867 emulate_sb ? EMU_SOKOBAN :
2868 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2870 #if USE_NEW_ALL_SLIPPERY
2871 /* initialize type of slippery elements */
2872 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2874 if (!IS_CUSTOM_ELEMENT(i))
2876 /* default: elements slip down either to the left or right randomly */
2877 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2879 /* SP style elements prefer to slip down on the left side */
2880 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2881 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2883 /* BD style elements prefer to slip down on the left side */
2884 if (game.emulation == EMU_BOULDERDASH)
2885 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2890 /* initialize explosion and ignition delay */
2891 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2893 if (!IS_CUSTOM_ELEMENT(i))
2896 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2897 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2898 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2899 int last_phase = (num_phase + 1) * delay;
2900 int half_phase = (num_phase / 2) * delay;
2902 element_info[i].explosion_delay = last_phase - 1;
2903 element_info[i].ignition_delay = half_phase;
2905 if (i == EL_BLACK_ORB)
2906 element_info[i].ignition_delay = 1;
2910 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2911 element_info[i].explosion_delay = 1;
2913 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2914 element_info[i].ignition_delay = 1;
2918 /* correct non-moving belts to start moving left */
2919 for (i = 0; i < NUM_BELTS; i++)
2920 if (game.belt_dir[i] == MV_NONE)
2921 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2923 /* check if any connected player was not found in playfield */
2924 for (i = 0; i < MAX_PLAYERS; i++)
2926 struct PlayerInfo *player = &stored_player[i];
2928 if (player->connected && !player->present)
2930 for (j = 0; j < MAX_PLAYERS; j++)
2932 struct PlayerInfo *some_player = &stored_player[j];
2933 int jx = some_player->jx, jy = some_player->jy;
2935 /* assign first free player found that is present in the playfield */
2936 if (some_player->present && !some_player->connected)
2938 player->present = TRUE;
2939 player->active = TRUE;
2941 some_player->present = FALSE;
2942 some_player->active = FALSE;
2944 player->artwork_element = some_player->artwork_element;
2946 player->block_last_field = some_player->block_last_field;
2947 player->block_delay_adjustment = some_player->block_delay_adjustment;
2949 StorePlayer[jx][jy] = player->element_nr;
2950 player->jx = player->last_jx = jx;
2951 player->jy = player->last_jy = jy;
2961 /* when playing a tape, eliminate all players who do not participate */
2963 for (i = 0; i < MAX_PLAYERS; i++)
2965 if (stored_player[i].active && !tape.player_participates[i])
2967 struct PlayerInfo *player = &stored_player[i];
2968 int jx = player->jx, jy = player->jy;
2970 player->active = FALSE;
2971 StorePlayer[jx][jy] = 0;
2972 Feld[jx][jy] = EL_EMPTY;
2976 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2978 /* when in single player mode, eliminate all but the first active player */
2980 for (i = 0; i < MAX_PLAYERS; i++)
2982 if (stored_player[i].active)
2984 for (j = i + 1; j < MAX_PLAYERS; j++)
2986 if (stored_player[j].active)
2988 struct PlayerInfo *player = &stored_player[j];
2989 int jx = player->jx, jy = player->jy;
2991 player->active = FALSE;
2992 player->present = FALSE;
2994 StorePlayer[jx][jy] = 0;
2995 Feld[jx][jy] = EL_EMPTY;
3002 /* when recording the game, store which players take part in the game */
3005 for (i = 0; i < MAX_PLAYERS; i++)
3006 if (stored_player[i].active)
3007 tape.player_participates[i] = TRUE;
3012 for (i = 0; i < MAX_PLAYERS; i++)
3014 struct PlayerInfo *player = &stored_player[i];
3016 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3021 if (local_player == player)
3022 printf("Player %d is local player.\n", i+1);
3026 if (BorderElement == EL_EMPTY)
3029 SBX_Right = lev_fieldx - SCR_FIELDX;
3031 SBY_Lower = lev_fieldy - SCR_FIELDY;
3036 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3038 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3041 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3042 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3044 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3045 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3047 /* if local player not found, look for custom element that might create
3048 the player (make some assumptions about the right custom element) */
3049 if (!local_player->present)
3051 int start_x = 0, start_y = 0;
3052 int found_rating = 0;
3053 int found_element = EL_UNDEFINED;
3054 int player_nr = local_player->index_nr;
3056 SCAN_PLAYFIELD(x, y)
3058 int element = Feld[x][y];
3063 if (level.use_start_element[player_nr] &&
3064 level.start_element[player_nr] == element &&
3071 found_element = element;
3074 if (!IS_CUSTOM_ELEMENT(element))
3077 if (CAN_CHANGE(element))
3079 for (i = 0; i < element_info[element].num_change_pages; i++)
3081 /* check for player created from custom element as single target */
3082 content = element_info[element].change_page[i].target_element;
3083 is_player = ELEM_IS_PLAYER(content);
3085 if (is_player && (found_rating < 3 || element < found_element))
3091 found_element = element;
3096 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3098 /* check for player created from custom element as explosion content */
3099 content = element_info[element].content.e[xx][yy];
3100 is_player = ELEM_IS_PLAYER(content);
3102 if (is_player && (found_rating < 2 || element < found_element))
3104 start_x = x + xx - 1;
3105 start_y = y + yy - 1;
3108 found_element = element;
3111 if (!CAN_CHANGE(element))
3114 for (i = 0; i < element_info[element].num_change_pages; i++)
3116 /* check for player created from custom element as extended target */
3118 element_info[element].change_page[i].target_content.e[xx][yy];
3120 is_player = ELEM_IS_PLAYER(content);
3122 if (is_player && (found_rating < 1 || element < found_element))
3124 start_x = x + xx - 1;
3125 start_y = y + yy - 1;
3128 found_element = element;
3134 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3135 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3138 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3139 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3144 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3145 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3146 local_player->jx - MIDPOSX);
3148 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3149 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3150 local_player->jy - MIDPOSY);
3155 if (!game.restart_level)
3156 CloseDoor(DOOR_CLOSE_1);
3159 FadeOut(REDRAW_FIELD);
3161 /* !!! FIX THIS (START) !!! */
3162 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3164 InitGameEngine_EM();
3166 /* blit playfield from scroll buffer to normal back buffer for fading in */
3167 BlitScreenToBitmap_EM(backbuffer);
3174 /* after drawing the level, correct some elements */
3175 if (game.timegate_time_left == 0)
3176 CloseAllOpenTimegates();
3178 /* blit playfield from scroll buffer to normal back buffer for fading in */
3179 if (setup.soft_scrolling)
3180 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3182 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3184 /* !!! FIX THIS (END) !!! */
3187 FadeIn(REDRAW_FIELD);
3191 if (!game.restart_level)
3193 /* copy default game door content to main double buffer */
3194 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3195 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3198 SetPanelBackground();
3199 SetDrawBackgroundMask(REDRAW_DOOR_1);
3201 DrawGameDoorValues();
3203 if (!game.restart_level)
3207 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3208 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3209 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3213 /* copy actual game door content to door double buffer for OpenDoor() */
3214 BlitBitmap(drawto, bitmap_db_door,
3215 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3217 OpenDoor(DOOR_OPEN_ALL);
3219 PlaySound(SND_GAME_STARTING);
3221 if (setup.sound_music)
3224 KeyboardAutoRepeatOffUnlessAutoplay();
3228 for (i = 0; i < MAX_PLAYERS; i++)
3229 printf("Player %d %sactive.\n",
3230 i + 1, (stored_player[i].active ? "" : "not "));
3241 game.restart_level = FALSE;
3244 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3246 /* this is used for non-R'n'D game engines to update certain engine values */
3248 /* needed to determine if sounds are played within the visible screen area */
3249 scroll_x = actual_scroll_x;
3250 scroll_y = actual_scroll_y;
3253 void InitMovDir(int x, int y)
3255 int i, element = Feld[x][y];
3256 static int xy[4][2] =
3263 static int direction[3][4] =
3265 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3266 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3267 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3276 Feld[x][y] = EL_BUG;
3277 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3280 case EL_SPACESHIP_RIGHT:
3281 case EL_SPACESHIP_UP:
3282 case EL_SPACESHIP_LEFT:
3283 case EL_SPACESHIP_DOWN:
3284 Feld[x][y] = EL_SPACESHIP;
3285 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3288 case EL_BD_BUTTERFLY_RIGHT:
3289 case EL_BD_BUTTERFLY_UP:
3290 case EL_BD_BUTTERFLY_LEFT:
3291 case EL_BD_BUTTERFLY_DOWN:
3292 Feld[x][y] = EL_BD_BUTTERFLY;
3293 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3296 case EL_BD_FIREFLY_RIGHT:
3297 case EL_BD_FIREFLY_UP:
3298 case EL_BD_FIREFLY_LEFT:
3299 case EL_BD_FIREFLY_DOWN:
3300 Feld[x][y] = EL_BD_FIREFLY;
3301 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3304 case EL_PACMAN_RIGHT:
3306 case EL_PACMAN_LEFT:
3307 case EL_PACMAN_DOWN:
3308 Feld[x][y] = EL_PACMAN;
3309 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3312 case EL_YAMYAM_LEFT:
3313 case EL_YAMYAM_RIGHT:
3315 case EL_YAMYAM_DOWN:
3316 Feld[x][y] = EL_YAMYAM;
3317 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3320 case EL_SP_SNIKSNAK:
3321 MovDir[x][y] = MV_UP;
3324 case EL_SP_ELECTRON:
3325 MovDir[x][y] = MV_LEFT;
3332 Feld[x][y] = EL_MOLE;
3333 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3337 if (IS_CUSTOM_ELEMENT(element))
3339 struct ElementInfo *ei = &element_info[element];
3340 int move_direction_initial = ei->move_direction_initial;
3341 int move_pattern = ei->move_pattern;
3343 if (move_direction_initial == MV_START_PREVIOUS)
3345 if (MovDir[x][y] != MV_NONE)
3348 move_direction_initial = MV_START_AUTOMATIC;
3351 if (move_direction_initial == MV_START_RANDOM)
3352 MovDir[x][y] = 1 << RND(4);
3353 else if (move_direction_initial & MV_ANY_DIRECTION)
3354 MovDir[x][y] = move_direction_initial;
3355 else if (move_pattern == MV_ALL_DIRECTIONS ||
3356 move_pattern == MV_TURNING_LEFT ||
3357 move_pattern == MV_TURNING_RIGHT ||
3358 move_pattern == MV_TURNING_LEFT_RIGHT ||
3359 move_pattern == MV_TURNING_RIGHT_LEFT ||
3360 move_pattern == MV_TURNING_RANDOM)
3361 MovDir[x][y] = 1 << RND(4);
3362 else if (move_pattern == MV_HORIZONTAL)
3363 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3364 else if (move_pattern == MV_VERTICAL)
3365 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3366 else if (move_pattern & MV_ANY_DIRECTION)
3367 MovDir[x][y] = element_info[element].move_pattern;
3368 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3369 move_pattern == MV_ALONG_RIGHT_SIDE)
3371 /* use random direction as default start direction */
3372 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3373 MovDir[x][y] = 1 << RND(4);
3375 for (i = 0; i < NUM_DIRECTIONS; i++)
3377 int x1 = x + xy[i][0];
3378 int y1 = y + xy[i][1];
3380 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3382 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3383 MovDir[x][y] = direction[0][i];
3385 MovDir[x][y] = direction[1][i];
3394 MovDir[x][y] = 1 << RND(4);
3396 if (element != EL_BUG &&
3397 element != EL_SPACESHIP &&
3398 element != EL_BD_BUTTERFLY &&
3399 element != EL_BD_FIREFLY)
3402 for (i = 0; i < NUM_DIRECTIONS; i++)
3404 int x1 = x + xy[i][0];
3405 int y1 = y + xy[i][1];
3407 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3409 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3411 MovDir[x][y] = direction[0][i];
3414 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3415 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3417 MovDir[x][y] = direction[1][i];
3426 GfxDir[x][y] = MovDir[x][y];
3429 void InitAmoebaNr(int x, int y)
3432 int group_nr = AmoebeNachbarNr(x, y);
3436 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3438 if (AmoebaCnt[i] == 0)
3446 AmoebaNr[x][y] = group_nr;
3447 AmoebaCnt[group_nr]++;
3448 AmoebaCnt2[group_nr]++;
3451 static void PlayerWins(struct PlayerInfo *player)
3453 player->LevelSolved = TRUE;
3454 player->GameOver = TRUE;
3456 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3457 level.native_em_level->lev->score : player->score);
3462 static int time, time_final;
3463 static int score, score_final;
3464 static int game_over_delay_1 = 0;
3465 static int game_over_delay_2 = 0;
3466 int game_over_delay_value_1 = 50;
3467 int game_over_delay_value_2 = 50;
3469 if (!local_player->LevelSolved_GameWon)
3473 /* do not start end game actions before the player stops moving (to exit) */
3474 if (local_player->MovPos)
3477 local_player->LevelSolved_GameWon = TRUE;
3478 local_player->LevelSolved_SaveTape = tape.recording;
3479 local_player->LevelSolved_SaveScore = !tape.playing;
3481 if (tape.auto_play) /* tape might already be stopped here */
3482 tape.auto_play_level_solved = TRUE;
3488 game_over_delay_1 = game_over_delay_value_1;
3489 game_over_delay_2 = game_over_delay_value_2;
3491 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3492 score = score_final = local_player->score_final;
3497 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3499 else if (level.time == 0 && TimePlayed < 999)
3502 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3505 local_player->score_final = score_final;
3507 if (level_editor_test_game)
3510 score = score_final;
3512 DrawGameValue_Time(time);
3513 DrawGameValue_Score(score);
3516 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3518 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3520 /* close exit door after last player */
3521 if ((AllPlayersGone &&
3522 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3523 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3524 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3525 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3526 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3528 int element = Feld[ExitX][ExitY];
3531 if (element == EL_EM_EXIT_OPEN ||
3532 element == EL_EM_STEEL_EXIT_OPEN)
3539 Feld[ExitX][ExitY] =
3540 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3541 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3542 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3543 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3544 EL_EM_STEEL_EXIT_CLOSING);
3546 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3550 /* player disappears */
3551 DrawLevelField(ExitX, ExitY);
3554 for (i = 0; i < MAX_PLAYERS; i++)
3556 struct PlayerInfo *player = &stored_player[i];
3558 if (player->present)
3560 RemovePlayer(player);
3562 /* player disappears */
3563 DrawLevelField(player->jx, player->jy);
3568 PlaySound(SND_GAME_WINNING);
3571 if (game_over_delay_1 > 0)
3573 game_over_delay_1--;
3578 if (time != time_final)
3580 int time_to_go = ABS(time_final - time);
3581 int time_count_dir = (time < time_final ? +1 : -1);
3582 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3584 time += time_count_steps * time_count_dir;
3585 score += time_count_steps * level.score[SC_TIME_BONUS];
3587 DrawGameValue_Time(time);
3588 DrawGameValue_Score(score);
3590 if (time == time_final)
3591 StopSound(SND_GAME_LEVELTIME_BONUS);
3592 else if (setup.sound_loops)
3593 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3595 PlaySound(SND_GAME_LEVELTIME_BONUS);
3600 local_player->LevelSolved_PanelOff = TRUE;
3602 if (game_over_delay_2 > 0)
3604 game_over_delay_2--;
3617 boolean raise_level = FALSE;
3619 local_player->LevelSolved_GameEnd = TRUE;
3621 CloseDoor(DOOR_CLOSE_1);
3623 if (local_player->LevelSolved_SaveTape)
3630 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3632 SaveTape(tape.level_nr); /* ask to save tape */
3636 if (level_editor_test_game)
3638 game_status = GAME_MODE_MAIN;
3645 if (!local_player->LevelSolved_SaveScore)
3647 FadeOut(REDRAW_FIELD);
3649 game_status = GAME_MODE_MAIN;
3651 DrawAndFadeInMainMenu(REDRAW_FIELD);
3656 if (level_nr == leveldir_current->handicap_level)
3658 leveldir_current->handicap_level++;
3659 SaveLevelSetup_SeriesInfo();
3662 if (level_nr < leveldir_current->last_level)
3663 raise_level = TRUE; /* advance to next level */
3665 if ((hi_pos = NewHiScore()) >= 0)
3667 game_status = GAME_MODE_SCORES;
3669 DrawHallOfFame(hi_pos);
3679 FadeOut(REDRAW_FIELD);
3681 game_status = GAME_MODE_MAIN;
3689 DrawAndFadeInMainMenu(REDRAW_FIELD);
3698 LoadScore(level_nr);
3700 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3701 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3704 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3706 if (local_player->score_final > highscore[k].Score)
3708 /* player has made it to the hall of fame */
3710 if (k < MAX_SCORE_ENTRIES - 1)
3712 int m = MAX_SCORE_ENTRIES - 1;
3715 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3716 if (strEqual(setup.player_name, highscore[l].Name))
3718 if (m == k) /* player's new highscore overwrites his old one */
3722 for (l = m; l > k; l--)
3724 strcpy(highscore[l].Name, highscore[l - 1].Name);
3725 highscore[l].Score = highscore[l - 1].Score;
3732 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3733 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3734 highscore[k].Score = local_player->score_final;
3740 else if (!strncmp(setup.player_name, highscore[k].Name,
3741 MAX_PLAYER_NAME_LEN))
3742 break; /* player already there with a higher score */
3748 SaveScore(level_nr);
3753 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3755 int element = Feld[x][y];
3756 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3757 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3758 int horiz_move = (dx != 0);
3759 int sign = (horiz_move ? dx : dy);
3760 int step = sign * element_info[element].move_stepsize;
3762 /* special values for move stepsize for spring and things on conveyor belt */
3765 if (CAN_FALL(element) &&
3766 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3767 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3768 else if (element == EL_SPRING)
3769 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3775 inline static int getElementMoveStepsize(int x, int y)
3777 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3780 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3782 if (player->GfxAction != action || player->GfxDir != dir)
3785 printf("Player frame reset! (%d => %d, %d => %d)\n",
3786 player->GfxAction, action, player->GfxDir, dir);
3789 player->GfxAction = action;
3790 player->GfxDir = dir;
3792 player->StepFrame = 0;
3796 #if USE_GFX_RESET_GFX_ANIMATION
3797 static void ResetGfxFrame(int x, int y, boolean redraw)
3799 int element = Feld[x][y];
3800 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3801 int last_gfx_frame = GfxFrame[x][y];
3803 if (graphic_info[graphic].anim_global_sync)
3804 GfxFrame[x][y] = FrameCounter;
3805 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3806 GfxFrame[x][y] = CustomValue[x][y];
3807 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3808 GfxFrame[x][y] = element_info[element].collect_score;
3809 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3810 GfxFrame[x][y] = ChangeDelay[x][y];
3812 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3813 DrawLevelGraphicAnimation(x, y, graphic);
3817 static void ResetGfxAnimation(int x, int y)
3819 GfxAction[x][y] = ACTION_DEFAULT;
3820 GfxDir[x][y] = MovDir[x][y];
3823 #if USE_GFX_RESET_GFX_ANIMATION
3824 ResetGfxFrame(x, y, FALSE);
3828 static void ResetRandomAnimationValue(int x, int y)
3830 GfxRandom[x][y] = INIT_GFX_RANDOM();
3833 void InitMovingField(int x, int y, int direction)
3835 int element = Feld[x][y];
3836 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3837 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3840 boolean is_moving_before, is_moving_after;
3842 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3845 /* check if element was/is moving or being moved before/after mode change */
3848 is_moving_before = (WasJustMoving[x][y] != 0);
3850 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3851 is_moving_before = WasJustMoving[x][y];
3854 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3856 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3858 /* reset animation only for moving elements which change direction of moving
3859 or which just started or stopped moving
3860 (else CEs with property "can move" / "not moving" are reset each frame) */
3861 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3863 if (is_moving_before != is_moving_after ||
3864 direction != MovDir[x][y])
3865 ResetGfxAnimation(x, y);
3867 if ((is_moving_before || is_moving_after) && !continues_moving)
3868 ResetGfxAnimation(x, y);
3871 if (!continues_moving)
3872 ResetGfxAnimation(x, y);
3875 MovDir[x][y] = direction;
3876 GfxDir[x][y] = direction;
3878 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3879 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3880 direction == MV_DOWN && CAN_FALL(element) ?
3881 ACTION_FALLING : ACTION_MOVING);
3883 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3884 ACTION_FALLING : ACTION_MOVING);
3887 /* this is needed for CEs with property "can move" / "not moving" */
3889 if (is_moving_after)
3891 if (Feld[newx][newy] == EL_EMPTY)
3892 Feld[newx][newy] = EL_BLOCKED;
3894 MovDir[newx][newy] = MovDir[x][y];
3896 #if USE_NEW_CUSTOM_VALUE
3897 CustomValue[newx][newy] = CustomValue[x][y];
3900 GfxFrame[newx][newy] = GfxFrame[x][y];
3901 GfxRandom[newx][newy] = GfxRandom[x][y];
3902 GfxAction[newx][newy] = GfxAction[x][y];
3903 GfxDir[newx][newy] = GfxDir[x][y];
3907 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3909 int direction = MovDir[x][y];
3910 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3911 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3917 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3919 int oldx = x, oldy = y;
3920 int direction = MovDir[x][y];
3922 if (direction == MV_LEFT)
3924 else if (direction == MV_RIGHT)
3926 else if (direction == MV_UP)
3928 else if (direction == MV_DOWN)
3931 *comes_from_x = oldx;
3932 *comes_from_y = oldy;
3935 int MovingOrBlocked2Element(int x, int y)
3937 int element = Feld[x][y];
3939 if (element == EL_BLOCKED)
3943 Blocked2Moving(x, y, &oldx, &oldy);
3944 return Feld[oldx][oldy];
3950 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3952 /* like MovingOrBlocked2Element(), but if element is moving
3953 and (x,y) is the field the moving element is just leaving,
3954 return EL_BLOCKED instead of the element value */
3955 int element = Feld[x][y];
3957 if (IS_MOVING(x, y))
3959 if (element == EL_BLOCKED)
3963 Blocked2Moving(x, y, &oldx, &oldy);
3964 return Feld[oldx][oldy];
3973 static void RemoveField(int x, int y)
3975 Feld[x][y] = EL_EMPTY;
3981 #if USE_NEW_CUSTOM_VALUE
3982 CustomValue[x][y] = 0;
3986 ChangeDelay[x][y] = 0;
3987 ChangePage[x][y] = -1;
3988 Pushed[x][y] = FALSE;
3991 ExplodeField[x][y] = EX_TYPE_NONE;
3994 GfxElement[x][y] = EL_UNDEFINED;
3995 GfxAction[x][y] = ACTION_DEFAULT;
3996 GfxDir[x][y] = MV_NONE;
3999 void RemoveMovingField(int x, int y)
4001 int oldx = x, oldy = y, newx = x, newy = y;
4002 int element = Feld[x][y];
4003 int next_element = EL_UNDEFINED;
4005 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4008 if (IS_MOVING(x, y))
4010 Moving2Blocked(x, y, &newx, &newy);
4012 if (Feld[newx][newy] != EL_BLOCKED)
4014 /* element is moving, but target field is not free (blocked), but
4015 already occupied by something different (example: acid pool);
4016 in this case, only remove the moving field, but not the target */
4018 RemoveField(oldx, oldy);
4020 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4022 DrawLevelField(oldx, oldy);
4027 else if (element == EL_BLOCKED)
4029 Blocked2Moving(x, y, &oldx, &oldy);
4030 if (!IS_MOVING(oldx, oldy))
4034 if (element == EL_BLOCKED &&
4035 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4036 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4037 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4038 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4039 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4040 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4041 next_element = get_next_element(Feld[oldx][oldy]);
4043 RemoveField(oldx, oldy);
4044 RemoveField(newx, newy);
4046 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4048 if (next_element != EL_UNDEFINED)
4049 Feld[oldx][oldy] = next_element;
4051 DrawLevelField(oldx, oldy);
4052 DrawLevelField(newx, newy);
4055 void DrawDynamite(int x, int y)
4057 int sx = SCREENX(x), sy = SCREENY(y);
4058 int graphic = el2img(Feld[x][y]);
4061 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4064 if (IS_WALKABLE_INSIDE(Back[x][y]))
4068 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4069 else if (Store[x][y])
4070 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4072 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4074 if (Back[x][y] || Store[x][y])
4075 DrawGraphicThruMask(sx, sy, graphic, frame);
4077 DrawGraphic(sx, sy, graphic, frame);
4080 void CheckDynamite(int x, int y)
4082 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4086 if (MovDelay[x][y] != 0)
4089 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4095 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4100 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4102 boolean num_checked_players = 0;
4105 for (i = 0; i < MAX_PLAYERS; i++)
4107 if (stored_player[i].active)
4109 int sx = stored_player[i].jx;
4110 int sy = stored_player[i].jy;
4112 if (num_checked_players == 0)
4119 *sx1 = MIN(*sx1, sx);
4120 *sy1 = MIN(*sy1, sy);
4121 *sx2 = MAX(*sx2, sx);
4122 *sy2 = MAX(*sy2, sy);
4125 num_checked_players++;
4130 static boolean checkIfAllPlayersFitToScreen_RND()
4132 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4134 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4136 return (sx2 - sx1 < SCR_FIELDX &&
4137 sy2 - sy1 < SCR_FIELDY);
4140 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4142 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4144 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4146 *sx = (sx1 + sx2) / 2;
4147 *sy = (sy1 + sy2) / 2;
4150 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4151 boolean center_screen, boolean quick_relocation)
4153 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4154 boolean no_delay = (tape.warp_forward);
4155 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4156 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4158 if (quick_relocation)
4160 int offset = (setup.scroll_delay ? 3 : 0);
4162 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4166 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4167 x > SBX_Right + MIDPOSX ? SBX_Right :
4170 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4171 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4176 /* quick relocation (without scrolling), but do not center screen */
4178 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4179 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4182 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4183 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4186 int offset_x = x + (scroll_x - center_scroll_x);
4187 int offset_y = y + (scroll_y - center_scroll_y);
4189 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4190 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4191 offset_x - MIDPOSX);
4193 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4194 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4195 offset_y - MIDPOSY);
4200 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4201 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4202 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4204 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4205 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4206 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4208 /* don't scroll over playfield boundaries */
4209 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4210 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4212 /* don't scroll over playfield boundaries */
4213 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4214 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4217 RedrawPlayfield(TRUE, 0,0,0,0);
4221 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4222 x > SBX_Right + MIDPOSX ? SBX_Right :
4225 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4226 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4229 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4231 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4234 int fx = FX, fy = FY;
4236 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4237 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4239 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4245 fx += dx * TILEX / 2;
4246 fy += dy * TILEY / 2;
4248 ScrollLevel(dx, dy);
4251 /* scroll in two steps of half tile size to make things smoother */
4252 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4254 Delay(wait_delay_value);
4256 /* scroll second step to align at full tile size */
4258 Delay(wait_delay_value);
4263 Delay(wait_delay_value);
4267 void RelocatePlayer(int jx, int jy, int el_player_raw)
4269 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4270 int player_nr = GET_PLAYER_NR(el_player);
4271 struct PlayerInfo *player = &stored_player[player_nr];
4272 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4273 boolean no_delay = (tape.warp_forward);
4274 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4275 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4276 int old_jx = player->jx;
4277 int old_jy = player->jy;
4278 int old_element = Feld[old_jx][old_jy];
4279 int element = Feld[jx][jy];
4280 boolean player_relocated = (old_jx != jx || old_jy != jy);
4282 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4283 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4284 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4285 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4286 int leave_side_horiz = move_dir_horiz;
4287 int leave_side_vert = move_dir_vert;
4288 int enter_side = enter_side_horiz | enter_side_vert;
4289 int leave_side = leave_side_horiz | leave_side_vert;
4291 if (player->GameOver) /* do not reanimate dead player */
4294 if (!player_relocated) /* no need to relocate the player */
4297 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4299 RemoveField(jx, jy); /* temporarily remove newly placed player */
4300 DrawLevelField(jx, jy);
4303 if (player->present)
4305 while (player->MovPos)
4307 ScrollPlayer(player, SCROLL_GO_ON);
4308 ScrollScreen(NULL, SCROLL_GO_ON);
4310 AdvanceFrameAndPlayerCounters(player->index_nr);
4315 Delay(wait_delay_value);
4318 DrawPlayer(player); /* needed here only to cleanup last field */
4319 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4321 player->is_moving = FALSE;
4324 if (IS_CUSTOM_ELEMENT(old_element))
4325 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4327 player->index_bit, leave_side);
4329 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4331 player->index_bit, leave_side);
4333 Feld[jx][jy] = el_player;
4334 InitPlayerField(jx, jy, el_player, TRUE);
4336 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4338 Feld[jx][jy] = element;
4339 InitField(jx, jy, FALSE);
4342 /* only visually relocate centered player */
4343 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4344 FALSE, level.instant_relocation);
4346 TestIfPlayerTouchesBadThing(jx, jy);
4347 TestIfPlayerTouchesCustomElement(jx, jy);
4349 if (IS_CUSTOM_ELEMENT(element))
4350 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4351 player->index_bit, enter_side);
4353 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4354 player->index_bit, enter_side);
4357 void Explode(int ex, int ey, int phase, int mode)
4363 /* !!! eliminate this variable !!! */
4364 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4366 if (game.explosions_delayed)
4368 ExplodeField[ex][ey] = mode;
4372 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4374 int center_element = Feld[ex][ey];
4375 int artwork_element, explosion_element; /* set these values later */
4378 /* --- This is only really needed (and now handled) in "Impact()". --- */
4379 /* do not explode moving elements that left the explode field in time */
4380 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4381 center_element == EL_EMPTY &&
4382 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4387 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4388 if (mode == EX_TYPE_NORMAL ||
4389 mode == EX_TYPE_CENTER ||
4390 mode == EX_TYPE_CROSS)
4391 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4394 /* remove things displayed in background while burning dynamite */
4395 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4398 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4400 /* put moving element to center field (and let it explode there) */
4401 center_element = MovingOrBlocked2Element(ex, ey);
4402 RemoveMovingField(ex, ey);
4403 Feld[ex][ey] = center_element;
4406 /* now "center_element" is finally determined -- set related values now */
4407 artwork_element = center_element; /* for custom player artwork */
4408 explosion_element = center_element; /* for custom player artwork */
4410 if (IS_PLAYER(ex, ey))
4412 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4414 artwork_element = stored_player[player_nr].artwork_element;
4416 if (level.use_explosion_element[player_nr])
4418 explosion_element = level.explosion_element[player_nr];
4419 artwork_element = explosion_element;
4424 if (mode == EX_TYPE_NORMAL ||
4425 mode == EX_TYPE_CENTER ||
4426 mode == EX_TYPE_CROSS)
4427 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4430 last_phase = element_info[explosion_element].explosion_delay + 1;
4432 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4434 int xx = x - ex + 1;
4435 int yy = y - ey + 1;
4438 if (!IN_LEV_FIELD(x, y) ||
4439 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4440 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4443 element = Feld[x][y];
4445 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4447 element = MovingOrBlocked2Element(x, y);
4449 if (!IS_EXPLOSION_PROOF(element))
4450 RemoveMovingField(x, y);
4453 /* indestructible elements can only explode in center (but not flames) */
4454 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4455 mode == EX_TYPE_BORDER)) ||
4456 element == EL_FLAMES)
4459 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4460 behaviour, for example when touching a yamyam that explodes to rocks
4461 with active deadly shield, a rock is created under the player !!! */
4462 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4464 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4465 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4466 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4468 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4471 if (IS_ACTIVE_BOMB(element))
4473 /* re-activate things under the bomb like gate or penguin */
4474 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4481 /* save walkable background elements while explosion on same tile */
4482 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4483 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4484 Back[x][y] = element;
4486 /* ignite explodable elements reached by other explosion */
4487 if (element == EL_EXPLOSION)
4488 element = Store2[x][y];
4490 if (AmoebaNr[x][y] &&
4491 (element == EL_AMOEBA_FULL ||
4492 element == EL_BD_AMOEBA ||
4493 element == EL_AMOEBA_GROWING))
4495 AmoebaCnt[AmoebaNr[x][y]]--;
4496 AmoebaCnt2[AmoebaNr[x][y]]--;
4501 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4503 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4505 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4507 if (PLAYERINFO(ex, ey)->use_murphy)
4508 Store[x][y] = EL_EMPTY;
4511 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4512 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4513 else if (ELEM_IS_PLAYER(center_element))
4514 Store[x][y] = EL_EMPTY;
4515 else if (center_element == EL_YAMYAM)
4516 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4517 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4518 Store[x][y] = element_info[center_element].content.e[xx][yy];
4520 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4521 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4522 otherwise) -- FIX THIS !!! */
4523 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4524 Store[x][y] = element_info[element].content.e[1][1];
4526 else if (!CAN_EXPLODE(element))
4527 Store[x][y] = element_info[element].content.e[1][1];
4530 Store[x][y] = EL_EMPTY;
4532 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4533 center_element == EL_AMOEBA_TO_DIAMOND)
4534 Store2[x][y] = element;
4536 Feld[x][y] = EL_EXPLOSION;
4537 GfxElement[x][y] = artwork_element;
4539 ExplodePhase[x][y] = 1;
4540 ExplodeDelay[x][y] = last_phase;
4545 if (center_element == EL_YAMYAM)
4546 game.yamyam_content_nr =
4547 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4559 GfxFrame[x][y] = 0; /* restart explosion animation */
4561 last_phase = ExplodeDelay[x][y];
4563 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4567 /* activate this even in non-DEBUG version until cause for crash in
4568 getGraphicAnimationFrame() (see below) is found and eliminated */
4574 /* this can happen if the player leaves an explosion just in time */
4575 if (GfxElement[x][y] == EL_UNDEFINED)
4576 GfxElement[x][y] = EL_EMPTY;
4578 if (GfxElement[x][y] == EL_UNDEFINED)
4581 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4582 printf("Explode(): This should never happen!\n");
4585 GfxElement[x][y] = EL_EMPTY;
4591 border_element = Store2[x][y];
4592 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4593 border_element = StorePlayer[x][y];
4595 if (phase == element_info[border_element].ignition_delay ||
4596 phase == last_phase)
4598 boolean border_explosion = FALSE;
4600 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4601 !PLAYER_EXPLOSION_PROTECTED(x, y))
4603 KillPlayerUnlessExplosionProtected(x, y);
4604 border_explosion = TRUE;
4606 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4608 Feld[x][y] = Store2[x][y];
4611 border_explosion = TRUE;
4613 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4615 AmoebeUmwandeln(x, y);
4617 border_explosion = TRUE;
4620 /* if an element just explodes due to another explosion (chain-reaction),
4621 do not immediately end the new explosion when it was the last frame of
4622 the explosion (as it would be done in the following "if"-statement!) */
4623 if (border_explosion && phase == last_phase)
4627 if (phase == last_phase)
4631 element = Feld[x][y] = Store[x][y];
4632 Store[x][y] = Store2[x][y] = 0;
4633 GfxElement[x][y] = EL_UNDEFINED;
4635 /* player can escape from explosions and might therefore be still alive */
4636 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4637 element <= EL_PLAYER_IS_EXPLODING_4)
4639 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4640 int explosion_element = EL_PLAYER_1 + player_nr;
4641 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4642 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4644 if (level.use_explosion_element[player_nr])
4645 explosion_element = level.explosion_element[player_nr];
4647 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4648 element_info[explosion_element].content.e[xx][yy]);
4651 /* restore probably existing indestructible background element */
4652 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4653 element = Feld[x][y] = Back[x][y];
4656 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4657 GfxDir[x][y] = MV_NONE;
4658 ChangeDelay[x][y] = 0;
4659 ChangePage[x][y] = -1;
4661 #if USE_NEW_CUSTOM_VALUE
4662 CustomValue[x][y] = 0;
4665 InitField_WithBug2(x, y, FALSE);
4667 DrawLevelField(x, y);
4669 TestIfElementTouchesCustomElement(x, y);
4671 if (GFX_CRUMBLED(element))
4672 DrawLevelFieldCrumbledSandNeighbours(x, y);
4674 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4675 StorePlayer[x][y] = 0;
4677 if (ELEM_IS_PLAYER(element))
4678 RelocatePlayer(x, y, element);
4680 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4682 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4683 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4686 DrawLevelFieldCrumbledSand(x, y);
4688 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4690 DrawLevelElement(x, y, Back[x][y]);
4691 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4693 else if (IS_WALKABLE_UNDER(Back[x][y]))
4695 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4696 DrawLevelElementThruMask(x, y, Back[x][y]);
4698 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4699 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4703 void DynaExplode(int ex, int ey)
4706 int dynabomb_element = Feld[ex][ey];
4707 int dynabomb_size = 1;
4708 boolean dynabomb_xl = FALSE;
4709 struct PlayerInfo *player;
4710 static int xy[4][2] =
4718 if (IS_ACTIVE_BOMB(dynabomb_element))
4720 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4721 dynabomb_size = player->dynabomb_size;
4722 dynabomb_xl = player->dynabomb_xl;
4723 player->dynabombs_left++;
4726 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4728 for (i = 0; i < NUM_DIRECTIONS; i++)
4730 for (j = 1; j <= dynabomb_size; j++)
4732 int x = ex + j * xy[i][0];
4733 int y = ey + j * xy[i][1];
4736 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4739 element = Feld[x][y];
4741 /* do not restart explosions of fields with active bombs */
4742 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4745 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4747 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4748 !IS_DIGGABLE(element) && !dynabomb_xl)
4754 void Bang(int x, int y)
4756 int element = MovingOrBlocked2Element(x, y);
4757 int explosion_type = EX_TYPE_NORMAL;
4759 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4761 struct PlayerInfo *player = PLAYERINFO(x, y);
4763 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4764 player->element_nr);
4766 if (level.use_explosion_element[player->index_nr])
4768 int explosion_element = level.explosion_element[player->index_nr];
4770 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4771 explosion_type = EX_TYPE_CROSS;
4772 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4773 explosion_type = EX_TYPE_CENTER;
4781 case EL_BD_BUTTERFLY:
4784 case EL_DARK_YAMYAM:
4788 RaiseScoreElement(element);
4791 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4792 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4793 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4794 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4795 case EL_DYNABOMB_INCREASE_NUMBER:
4796 case EL_DYNABOMB_INCREASE_SIZE:
4797 case EL_DYNABOMB_INCREASE_POWER:
4798 explosion_type = EX_TYPE_DYNA;
4801 case EL_DC_LANDMINE:
4803 case EL_EM_EXIT_OPEN:
4804 case EL_EM_STEEL_EXIT_OPEN:
4806 explosion_type = EX_TYPE_CENTER;
4811 case EL_LAMP_ACTIVE:
4812 case EL_AMOEBA_TO_DIAMOND:
4813 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4814 explosion_type = EX_TYPE_CENTER;
4818 if (element_info[element].explosion_type == EXPLODES_CROSS)
4819 explosion_type = EX_TYPE_CROSS;
4820 else if (element_info[element].explosion_type == EXPLODES_1X1)
4821 explosion_type = EX_TYPE_CENTER;
4825 if (explosion_type == EX_TYPE_DYNA)
4828 Explode(x, y, EX_PHASE_START, explosion_type);
4830 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4833 void SplashAcid(int x, int y)
4835 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4836 (!IN_LEV_FIELD(x - 1, y - 2) ||
4837 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4838 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4840 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4841 (!IN_LEV_FIELD(x + 1, y - 2) ||
4842 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4843 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4845 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4848 static void InitBeltMovement()
4850 static int belt_base_element[4] =
4852 EL_CONVEYOR_BELT_1_LEFT,
4853 EL_CONVEYOR_BELT_2_LEFT,
4854 EL_CONVEYOR_BELT_3_LEFT,
4855 EL_CONVEYOR_BELT_4_LEFT
4857 static int belt_base_active_element[4] =
4859 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4860 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4861 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4862 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4867 /* set frame order for belt animation graphic according to belt direction */
4868 for (i = 0; i < NUM_BELTS; i++)
4872 for (j = 0; j < NUM_BELT_PARTS; j++)
4874 int element = belt_base_active_element[belt_nr] + j;
4875 int graphic = el2img(element);
4877 if (game.belt_dir[i] == MV_LEFT)
4878 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4880 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4884 SCAN_PLAYFIELD(x, y)
4886 int element = Feld[x][y];
4888 for (i = 0; i < NUM_BELTS; i++)
4890 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4892 int e_belt_nr = getBeltNrFromBeltElement(element);
4895 if (e_belt_nr == belt_nr)
4897 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4899 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4906 static void ToggleBeltSwitch(int x, int y)
4908 static int belt_base_element[4] =
4910 EL_CONVEYOR_BELT_1_LEFT,
4911 EL_CONVEYOR_BELT_2_LEFT,
4912 EL_CONVEYOR_BELT_3_LEFT,
4913 EL_CONVEYOR_BELT_4_LEFT
4915 static int belt_base_active_element[4] =
4917 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4918 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4919 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4920 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4922 static int belt_base_switch_element[4] =
4924 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4925 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4926 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4927 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4929 static int belt_move_dir[4] =
4937 int element = Feld[x][y];
4938 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4939 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4940 int belt_dir = belt_move_dir[belt_dir_nr];
4943 if (!IS_BELT_SWITCH(element))
4946 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4947 game.belt_dir[belt_nr] = belt_dir;
4949 if (belt_dir_nr == 3)
4952 /* set frame order for belt animation graphic according to belt direction */
4953 for (i = 0; i < NUM_BELT_PARTS; i++)
4955 int element = belt_base_active_element[belt_nr] + i;
4956 int graphic = el2img(element);
4958 if (belt_dir == MV_LEFT)
4959 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4961 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4964 SCAN_PLAYFIELD(xx, yy)
4966 int element = Feld[xx][yy];
4968 if (IS_BELT_SWITCH(element))
4970 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4972 if (e_belt_nr == belt_nr)
4974 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4975 DrawLevelField(xx, yy);
4978 else if (IS_BELT(element) && belt_dir != MV_NONE)
4980 int e_belt_nr = getBeltNrFromBeltElement(element);
4982 if (e_belt_nr == belt_nr)
4984 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4986 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4987 DrawLevelField(xx, yy);
4990 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4992 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4994 if (e_belt_nr == belt_nr)
4996 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4998 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4999 DrawLevelField(xx, yy);
5005 static void ToggleSwitchgateSwitch(int x, int y)
5009 game.switchgate_pos = !game.switchgate_pos;
5011 SCAN_PLAYFIELD(xx, yy)
5013 int element = Feld[xx][yy];
5015 #if !USE_BOTH_SWITCHGATE_SWITCHES
5016 if (element == EL_SWITCHGATE_SWITCH_UP ||
5017 element == EL_SWITCHGATE_SWITCH_DOWN)
5019 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5020 DrawLevelField(xx, yy);
5022 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5023 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5025 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5026 DrawLevelField(xx, yy);
5029 if (element == EL_SWITCHGATE_SWITCH_UP)
5031 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5032 DrawLevelField(xx, yy);
5034 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5036 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5037 DrawLevelField(xx, yy);
5039 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5041 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5042 DrawLevelField(xx, yy);
5044 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5046 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5047 DrawLevelField(xx, yy);
5050 else if (element == EL_SWITCHGATE_OPEN ||
5051 element == EL_SWITCHGATE_OPENING)
5053 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5055 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5057 else if (element == EL_SWITCHGATE_CLOSED ||
5058 element == EL_SWITCHGATE_CLOSING)
5060 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5062 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5067 static int getInvisibleActiveFromInvisibleElement(int element)
5069 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5070 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5071 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5075 static int getInvisibleFromInvisibleActiveElement(int element)
5077 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5078 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5079 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5083 static void RedrawAllLightSwitchesAndInvisibleElements()
5087 SCAN_PLAYFIELD(x, y)
5089 int element = Feld[x][y];
5091 if (element == EL_LIGHT_SWITCH &&
5092 game.light_time_left > 0)
5094 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5095 DrawLevelField(x, y);
5097 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5098 game.light_time_left == 0)
5100 Feld[x][y] = EL_LIGHT_SWITCH;
5101 DrawLevelField(x, y);
5103 else if (element == EL_EMC_DRIPPER &&
5104 game.light_time_left > 0)
5106 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5107 DrawLevelField(x, y);
5109 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5110 game.light_time_left == 0)
5112 Feld[x][y] = EL_EMC_DRIPPER;
5113 DrawLevelField(x, y);
5115 else if (element == EL_INVISIBLE_STEELWALL ||
5116 element == EL_INVISIBLE_WALL ||
5117 element == EL_INVISIBLE_SAND)
5119 if (game.light_time_left > 0)
5120 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5122 DrawLevelField(x, y);
5124 /* uncrumble neighbour fields, if needed */
5125 if (element == EL_INVISIBLE_SAND)
5126 DrawLevelFieldCrumbledSandNeighbours(x, y);
5128 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5129 element == EL_INVISIBLE_WALL_ACTIVE ||
5130 element == EL_INVISIBLE_SAND_ACTIVE)
5132 if (game.light_time_left == 0)
5133 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5135 DrawLevelField(x, y);
5137 /* re-crumble neighbour fields, if needed */
5138 if (element == EL_INVISIBLE_SAND)
5139 DrawLevelFieldCrumbledSandNeighbours(x, y);
5144 static void RedrawAllInvisibleElementsForLenses()
5148 SCAN_PLAYFIELD(x, y)
5150 int element = Feld[x][y];
5152 if (element == EL_EMC_DRIPPER &&
5153 game.lenses_time_left > 0)
5155 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5156 DrawLevelField(x, y);
5158 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5159 game.lenses_time_left == 0)
5161 Feld[x][y] = EL_EMC_DRIPPER;
5162 DrawLevelField(x, y);
5164 else if (element == EL_INVISIBLE_STEELWALL ||
5165 element == EL_INVISIBLE_WALL ||
5166 element == EL_INVISIBLE_SAND)
5168 if (game.lenses_time_left > 0)
5169 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5171 DrawLevelField(x, y);
5173 /* uncrumble neighbour fields, if needed */
5174 if (element == EL_INVISIBLE_SAND)
5175 DrawLevelFieldCrumbledSandNeighbours(x, y);
5177 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5178 element == EL_INVISIBLE_WALL_ACTIVE ||
5179 element == EL_INVISIBLE_SAND_ACTIVE)
5181 if (game.lenses_time_left == 0)
5182 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5184 DrawLevelField(x, y);
5186 /* re-crumble neighbour fields, if needed */
5187 if (element == EL_INVISIBLE_SAND)
5188 DrawLevelFieldCrumbledSandNeighbours(x, y);
5193 static void RedrawAllInvisibleElementsForMagnifier()
5197 SCAN_PLAYFIELD(x, y)
5199 int element = Feld[x][y];
5201 if (element == EL_EMC_FAKE_GRASS &&
5202 game.magnify_time_left > 0)
5204 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5205 DrawLevelField(x, y);
5207 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5208 game.magnify_time_left == 0)
5210 Feld[x][y] = EL_EMC_FAKE_GRASS;
5211 DrawLevelField(x, y);
5213 else if (IS_GATE_GRAY(element) &&
5214 game.magnify_time_left > 0)
5216 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5217 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5218 IS_EM_GATE_GRAY(element) ?
5219 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5220 IS_EMC_GATE_GRAY(element) ?
5221 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5223 DrawLevelField(x, y);
5225 else if (IS_GATE_GRAY_ACTIVE(element) &&
5226 game.magnify_time_left == 0)
5228 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5229 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5230 IS_EM_GATE_GRAY_ACTIVE(element) ?
5231 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5232 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5233 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5235 DrawLevelField(x, y);
5240 static void ToggleLightSwitch(int x, int y)
5242 int element = Feld[x][y];
5244 game.light_time_left =
5245 (element == EL_LIGHT_SWITCH ?
5246 level.time_light * FRAMES_PER_SECOND : 0);
5248 RedrawAllLightSwitchesAndInvisibleElements();
5251 static void ActivateTimegateSwitch(int x, int y)
5255 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5257 SCAN_PLAYFIELD(xx, yy)
5259 int element = Feld[xx][yy];
5261 if (element == EL_TIMEGATE_CLOSED ||
5262 element == EL_TIMEGATE_CLOSING)
5264 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5265 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5269 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5271 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5272 DrawLevelField(xx, yy);
5279 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5280 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5282 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5286 void Impact(int x, int y)
5288 boolean last_line = (y == lev_fieldy - 1);
5289 boolean object_hit = FALSE;
5290 boolean impact = (last_line || object_hit);
5291 int element = Feld[x][y];
5292 int smashed = EL_STEELWALL;
5294 if (!last_line) /* check if element below was hit */
5296 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5299 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5300 MovDir[x][y + 1] != MV_DOWN ||
5301 MovPos[x][y + 1] <= TILEY / 2));
5303 /* do not smash moving elements that left the smashed field in time */
5304 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5305 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5308 #if USE_QUICKSAND_IMPACT_BUGFIX
5309 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5311 RemoveMovingField(x, y + 1);
5312 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5313 Feld[x][y + 2] = EL_ROCK;
5314 DrawLevelField(x, y + 2);
5319 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5321 RemoveMovingField(x, y + 1);
5322 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5323 Feld[x][y + 2] = EL_ROCK;
5324 DrawLevelField(x, y + 2);
5331 smashed = MovingOrBlocked2Element(x, y + 1);
5333 impact = (last_line || object_hit);
5336 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5338 SplashAcid(x, y + 1);
5342 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5343 /* only reset graphic animation if graphic really changes after impact */
5345 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5347 ResetGfxAnimation(x, y);
5348 DrawLevelField(x, y);
5351 if (impact && CAN_EXPLODE_IMPACT(element))
5356 else if (impact && element == EL_PEARL &&
5357 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5359 ResetGfxAnimation(x, y);
5361 Feld[x][y] = EL_PEARL_BREAKING;
5362 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5365 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5367 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5372 if (impact && element == EL_AMOEBA_DROP)
5374 if (object_hit && IS_PLAYER(x, y + 1))
5375 KillPlayerUnlessEnemyProtected(x, y + 1);
5376 else if (object_hit && smashed == EL_PENGUIN)
5380 Feld[x][y] = EL_AMOEBA_GROWING;
5381 Store[x][y] = EL_AMOEBA_WET;
5383 ResetRandomAnimationValue(x, y);
5388 if (object_hit) /* check which object was hit */
5390 if ((CAN_PASS_MAGIC_WALL(element) &&
5391 (smashed == EL_MAGIC_WALL ||
5392 smashed == EL_BD_MAGIC_WALL)) ||
5393 (CAN_PASS_DC_MAGIC_WALL(element) &&
5394 smashed == EL_DC_MAGIC_WALL))
5397 int activated_magic_wall =
5398 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5399 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5400 EL_DC_MAGIC_WALL_ACTIVE);
5402 /* activate magic wall / mill */
5403 SCAN_PLAYFIELD(xx, yy)
5405 if (Feld[xx][yy] == smashed)
5406 Feld[xx][yy] = activated_magic_wall;
5409 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5410 game.magic_wall_active = TRUE;
5412 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5413 SND_MAGIC_WALL_ACTIVATING :
5414 smashed == EL_BD_MAGIC_WALL ?
5415 SND_BD_MAGIC_WALL_ACTIVATING :
5416 SND_DC_MAGIC_WALL_ACTIVATING));
5419 if (IS_PLAYER(x, y + 1))
5421 if (CAN_SMASH_PLAYER(element))
5423 KillPlayerUnlessEnemyProtected(x, y + 1);
5427 else if (smashed == EL_PENGUIN)
5429 if (CAN_SMASH_PLAYER(element))
5435 else if (element == EL_BD_DIAMOND)
5437 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5443 else if (((element == EL_SP_INFOTRON ||
5444 element == EL_SP_ZONK) &&
5445 (smashed == EL_SP_SNIKSNAK ||
5446 smashed == EL_SP_ELECTRON ||
5447 smashed == EL_SP_DISK_ORANGE)) ||
5448 (element == EL_SP_INFOTRON &&
5449 smashed == EL_SP_DISK_YELLOW))
5454 else if (CAN_SMASH_EVERYTHING(element))
5456 if (IS_CLASSIC_ENEMY(smashed) ||
5457 CAN_EXPLODE_SMASHED(smashed))
5462 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5464 if (smashed == EL_LAMP ||
5465 smashed == EL_LAMP_ACTIVE)
5470 else if (smashed == EL_NUT)
5472 Feld[x][y + 1] = EL_NUT_BREAKING;
5473 PlayLevelSound(x, y, SND_NUT_BREAKING);
5474 RaiseScoreElement(EL_NUT);
5477 else if (smashed == EL_PEARL)
5479 ResetGfxAnimation(x, y);
5481 Feld[x][y + 1] = EL_PEARL_BREAKING;
5482 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5485 else if (smashed == EL_DIAMOND)
5487 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5488 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5491 else if (IS_BELT_SWITCH(smashed))
5493 ToggleBeltSwitch(x, y + 1);
5495 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5496 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5497 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5498 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5500 ToggleSwitchgateSwitch(x, y + 1);
5502 else if (smashed == EL_LIGHT_SWITCH ||
5503 smashed == EL_LIGHT_SWITCH_ACTIVE)
5505 ToggleLightSwitch(x, y + 1);
5510 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5513 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5515 CheckElementChangeBySide(x, y + 1, smashed, element,
5516 CE_SWITCHED, CH_SIDE_TOP);
5517 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5523 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5528 /* play sound of magic wall / mill */
5530 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5531 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5532 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5534 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5535 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5536 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5537 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5538 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5539 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5544 /* play sound of object that hits the ground */
5545 if (last_line || object_hit)
5546 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5549 inline static void TurnRoundExt(int x, int y)
5561 { 0, 0 }, { 0, 0 }, { 0, 0 },
5566 int left, right, back;
5570 { MV_DOWN, MV_UP, MV_RIGHT },
5571 { MV_UP, MV_DOWN, MV_LEFT },
5573 { MV_LEFT, MV_RIGHT, MV_DOWN },
5577 { MV_RIGHT, MV_LEFT, MV_UP }
5580 int element = Feld[x][y];
5581 int move_pattern = element_info[element].move_pattern;
5583 int old_move_dir = MovDir[x][y];
5584 int left_dir = turn[old_move_dir].left;
5585 int right_dir = turn[old_move_dir].right;
5586 int back_dir = turn[old_move_dir].back;
5588 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5589 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5590 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5591 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5593 int left_x = x + left_dx, left_y = y + left_dy;
5594 int right_x = x + right_dx, right_y = y + right_dy;
5595 int move_x = x + move_dx, move_y = y + move_dy;
5599 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5601 TestIfBadThingTouchesOtherBadThing(x, y);
5603 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5604 MovDir[x][y] = right_dir;
5605 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5606 MovDir[x][y] = left_dir;
5608 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5610 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5613 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5615 TestIfBadThingTouchesOtherBadThing(x, y);
5617 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5618 MovDir[x][y] = left_dir;
5619 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5620 MovDir[x][y] = right_dir;
5622 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5624 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5627 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5629 TestIfBadThingTouchesOtherBadThing(x, y);
5631 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5632 MovDir[x][y] = left_dir;
5633 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5634 MovDir[x][y] = right_dir;
5636 if (MovDir[x][y] != old_move_dir)
5639 else if (element == EL_YAMYAM)
5641 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5642 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5644 if (can_turn_left && can_turn_right)
5645 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5646 else if (can_turn_left)
5647 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5648 else if (can_turn_right)
5649 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5651 MovDir[x][y] = back_dir;
5653 MovDelay[x][y] = 16 + 16 * RND(3);
5655 else if (element == EL_DARK_YAMYAM)
5657 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5659 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5662 if (can_turn_left && can_turn_right)
5663 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5664 else if (can_turn_left)
5665 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5666 else if (can_turn_right)
5667 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5669 MovDir[x][y] = back_dir;
5671 MovDelay[x][y] = 16 + 16 * RND(3);
5673 else if (element == EL_PACMAN)
5675 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5676 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5678 if (can_turn_left && can_turn_right)
5679 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5680 else if (can_turn_left)
5681 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5682 else if (can_turn_right)
5683 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5685 MovDir[x][y] = back_dir;
5687 MovDelay[x][y] = 6 + RND(40);
5689 else if (element == EL_PIG)
5691 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5692 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5693 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5694 boolean should_turn_left, should_turn_right, should_move_on;
5696 int rnd = RND(rnd_value);
5698 should_turn_left = (can_turn_left &&
5700 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5701 y + back_dy + left_dy)));
5702 should_turn_right = (can_turn_right &&
5704 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5705 y + back_dy + right_dy)));
5706 should_move_on = (can_move_on &&
5709 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5710 y + move_dy + left_dy) ||
5711 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5712 y + move_dy + right_dy)));
5714 if (should_turn_left || should_turn_right || should_move_on)
5716 if (should_turn_left && should_turn_right && should_move_on)
5717 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5718 rnd < 2 * rnd_value / 3 ? right_dir :
5720 else if (should_turn_left && should_turn_right)
5721 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5722 else if (should_turn_left && should_move_on)
5723 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5724 else if (should_turn_right && should_move_on)
5725 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5726 else if (should_turn_left)
5727 MovDir[x][y] = left_dir;
5728 else if (should_turn_right)
5729 MovDir[x][y] = right_dir;
5730 else if (should_move_on)
5731 MovDir[x][y] = old_move_dir;
5733 else if (can_move_on && rnd > rnd_value / 8)
5734 MovDir[x][y] = old_move_dir;
5735 else if (can_turn_left && can_turn_right)
5736 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5737 else if (can_turn_left && rnd > rnd_value / 8)
5738 MovDir[x][y] = left_dir;
5739 else if (can_turn_right && rnd > rnd_value/8)
5740 MovDir[x][y] = right_dir;
5742 MovDir[x][y] = back_dir;
5744 xx = x + move_xy[MovDir[x][y]].dx;
5745 yy = y + move_xy[MovDir[x][y]].dy;
5747 if (!IN_LEV_FIELD(xx, yy) ||
5748 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5749 MovDir[x][y] = old_move_dir;
5753 else if (element == EL_DRAGON)
5755 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5756 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5757 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5759 int rnd = RND(rnd_value);
5761 if (can_move_on && rnd > rnd_value / 8)
5762 MovDir[x][y] = old_move_dir;
5763 else if (can_turn_left && can_turn_right)
5764 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5765 else if (can_turn_left && rnd > rnd_value / 8)
5766 MovDir[x][y] = left_dir;
5767 else if (can_turn_right && rnd > rnd_value / 8)
5768 MovDir[x][y] = right_dir;
5770 MovDir[x][y] = back_dir;
5772 xx = x + move_xy[MovDir[x][y]].dx;
5773 yy = y + move_xy[MovDir[x][y]].dy;
5775 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5776 MovDir[x][y] = old_move_dir;
5780 else if (element == EL_MOLE)
5782 boolean can_move_on =
5783 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5784 IS_AMOEBOID(Feld[move_x][move_y]) ||
5785 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5788 boolean can_turn_left =
5789 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5790 IS_AMOEBOID(Feld[left_x][left_y])));
5792 boolean can_turn_right =
5793 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5794 IS_AMOEBOID(Feld[right_x][right_y])));
5796 if (can_turn_left && can_turn_right)
5797 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5798 else if (can_turn_left)
5799 MovDir[x][y] = left_dir;
5801 MovDir[x][y] = right_dir;
5804 if (MovDir[x][y] != old_move_dir)
5807 else if (element == EL_BALLOON)
5809 MovDir[x][y] = game.wind_direction;
5812 else if (element == EL_SPRING)
5814 #if USE_NEW_SPRING_BUMPER
5815 if (MovDir[x][y] & MV_HORIZONTAL)
5817 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5818 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5820 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5821 ResetGfxAnimation(move_x, move_y);
5822 DrawLevelField(move_x, move_y);
5824 MovDir[x][y] = back_dir;
5826 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5827 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5828 MovDir[x][y] = MV_NONE;
5831 if (MovDir[x][y] & MV_HORIZONTAL &&
5832 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5833 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5834 MovDir[x][y] = MV_NONE;
5839 else if (element == EL_ROBOT ||
5840 element == EL_SATELLITE ||
5841 element == EL_PENGUIN ||
5842 element == EL_EMC_ANDROID)
5844 int attr_x = -1, attr_y = -1;
5855 for (i = 0; i < MAX_PLAYERS; i++)
5857 struct PlayerInfo *player = &stored_player[i];
5858 int jx = player->jx, jy = player->jy;
5860 if (!player->active)
5864 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5872 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5873 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5874 game.engine_version < VERSION_IDENT(3,1,0,0)))
5880 if (element == EL_PENGUIN)
5883 static int xy[4][2] =
5891 for (i = 0; i < NUM_DIRECTIONS; i++)
5893 int ex = x + xy[i][0];
5894 int ey = y + xy[i][1];
5896 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5897 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5898 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5899 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5908 MovDir[x][y] = MV_NONE;
5910 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5911 else if (attr_x > x)
5912 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5914 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5915 else if (attr_y > y)
5916 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5918 if (element == EL_ROBOT)
5922 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5923 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5924 Moving2Blocked(x, y, &newx, &newy);
5926 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5927 MovDelay[x][y] = 8 + 8 * !RND(3);
5929 MovDelay[x][y] = 16;
5931 else if (element == EL_PENGUIN)
5937 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5939 boolean first_horiz = RND(2);
5940 int new_move_dir = MovDir[x][y];
5943 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5944 Moving2Blocked(x, y, &newx, &newy);
5946 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5950 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5951 Moving2Blocked(x, y, &newx, &newy);
5953 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5956 MovDir[x][y] = old_move_dir;
5960 else if (element == EL_SATELLITE)
5966 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5968 boolean first_horiz = RND(2);
5969 int new_move_dir = MovDir[x][y];
5972 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5973 Moving2Blocked(x, y, &newx, &newy);
5975 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5979 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5980 Moving2Blocked(x, y, &newx, &newy);
5982 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5985 MovDir[x][y] = old_move_dir;
5989 else if (element == EL_EMC_ANDROID)
5991 static int check_pos[16] =
5993 -1, /* 0 => (invalid) */
5994 7, /* 1 => MV_LEFT */
5995 3, /* 2 => MV_RIGHT */
5996 -1, /* 3 => (invalid) */
5998 0, /* 5 => MV_LEFT | MV_UP */
5999 2, /* 6 => MV_RIGHT | MV_UP */
6000 -1, /* 7 => (invalid) */
6001 5, /* 8 => MV_DOWN */
6002 6, /* 9 => MV_LEFT | MV_DOWN */
6003 4, /* 10 => MV_RIGHT | MV_DOWN */
6004 -1, /* 11 => (invalid) */
6005 -1, /* 12 => (invalid) */
6006 -1, /* 13 => (invalid) */
6007 -1, /* 14 => (invalid) */
6008 -1, /* 15 => (invalid) */
6016 { -1, -1, MV_LEFT | MV_UP },
6018 { +1, -1, MV_RIGHT | MV_UP },
6019 { +1, 0, MV_RIGHT },
6020 { +1, +1, MV_RIGHT | MV_DOWN },
6022 { -1, +1, MV_LEFT | MV_DOWN },
6025 int start_pos, check_order;
6026 boolean can_clone = FALSE;
6029 /* check if there is any free field around current position */
6030 for (i = 0; i < 8; i++)
6032 int newx = x + check_xy[i].dx;
6033 int newy = y + check_xy[i].dy;
6035 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6043 if (can_clone) /* randomly find an element to clone */
6047 start_pos = check_pos[RND(8)];
6048 check_order = (RND(2) ? -1 : +1);
6050 for (i = 0; i < 8; i++)
6052 int pos_raw = start_pos + i * check_order;
6053 int pos = (pos_raw + 8) % 8;
6054 int newx = x + check_xy[pos].dx;
6055 int newy = y + check_xy[pos].dy;
6057 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6059 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6060 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6062 Store[x][y] = Feld[newx][newy];
6071 if (can_clone) /* randomly find a direction to move */
6075 start_pos = check_pos[RND(8)];
6076 check_order = (RND(2) ? -1 : +1);
6078 for (i = 0; i < 8; i++)
6080 int pos_raw = start_pos + i * check_order;
6081 int pos = (pos_raw + 8) % 8;
6082 int newx = x + check_xy[pos].dx;
6083 int newy = y + check_xy[pos].dy;
6084 int new_move_dir = check_xy[pos].dir;
6086 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6088 MovDir[x][y] = new_move_dir;
6089 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6098 if (can_clone) /* cloning and moving successful */
6101 /* cannot clone -- try to move towards player */
6103 start_pos = check_pos[MovDir[x][y] & 0x0f];
6104 check_order = (RND(2) ? -1 : +1);
6106 for (i = 0; i < 3; i++)
6108 /* first check start_pos, then previous/next or (next/previous) pos */
6109 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6110 int pos = (pos_raw + 8) % 8;
6111 int newx = x + check_xy[pos].dx;
6112 int newy = y + check_xy[pos].dy;
6113 int new_move_dir = check_xy[pos].dir;
6115 if (IS_PLAYER(newx, newy))
6118 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6120 MovDir[x][y] = new_move_dir;
6121 MovDelay[x][y] = level.android_move_time * 8 + 1;
6128 else if (move_pattern == MV_TURNING_LEFT ||
6129 move_pattern == MV_TURNING_RIGHT ||
6130 move_pattern == MV_TURNING_LEFT_RIGHT ||
6131 move_pattern == MV_TURNING_RIGHT_LEFT ||
6132 move_pattern == MV_TURNING_RANDOM ||
6133 move_pattern == MV_ALL_DIRECTIONS)
6135 boolean can_turn_left =
6136 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6137 boolean can_turn_right =
6138 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6140 if (element_info[element].move_stepsize == 0) /* "not moving" */
6143 if (move_pattern == MV_TURNING_LEFT)
6144 MovDir[x][y] = left_dir;
6145 else if (move_pattern == MV_TURNING_RIGHT)
6146 MovDir[x][y] = right_dir;
6147 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6148 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6149 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6150 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6151 else if (move_pattern == MV_TURNING_RANDOM)
6152 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6153 can_turn_right && !can_turn_left ? right_dir :
6154 RND(2) ? left_dir : right_dir);
6155 else if (can_turn_left && can_turn_right)
6156 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6157 else if (can_turn_left)
6158 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6159 else if (can_turn_right)
6160 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6162 MovDir[x][y] = back_dir;
6164 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6166 else if (move_pattern == MV_HORIZONTAL ||
6167 move_pattern == MV_VERTICAL)
6169 if (move_pattern & old_move_dir)
6170 MovDir[x][y] = back_dir;
6171 else if (move_pattern == MV_HORIZONTAL)
6172 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6173 else if (move_pattern == MV_VERTICAL)
6174 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6176 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6178 else if (move_pattern & MV_ANY_DIRECTION)
6180 MovDir[x][y] = move_pattern;
6181 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6183 else if (move_pattern & MV_WIND_DIRECTION)
6185 MovDir[x][y] = game.wind_direction;
6186 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6188 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6190 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6191 MovDir[x][y] = left_dir;
6192 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6193 MovDir[x][y] = right_dir;
6195 if (MovDir[x][y] != old_move_dir)
6196 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6198 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6200 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6201 MovDir[x][y] = right_dir;
6202 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6203 MovDir[x][y] = left_dir;
6205 if (MovDir[x][y] != old_move_dir)
6206 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6208 else if (move_pattern == MV_TOWARDS_PLAYER ||
6209 move_pattern == MV_AWAY_FROM_PLAYER)
6211 int attr_x = -1, attr_y = -1;
6213 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6224 for (i = 0; i < MAX_PLAYERS; i++)
6226 struct PlayerInfo *player = &stored_player[i];
6227 int jx = player->jx, jy = player->jy;
6229 if (!player->active)
6233 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6241 MovDir[x][y] = MV_NONE;
6243 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6244 else if (attr_x > x)
6245 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6247 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6248 else if (attr_y > y)
6249 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6251 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6253 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6255 boolean first_horiz = RND(2);
6256 int new_move_dir = MovDir[x][y];
6258 if (element_info[element].move_stepsize == 0) /* "not moving" */
6260 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6261 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6267 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6268 Moving2Blocked(x, y, &newx, &newy);
6270 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6274 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6275 Moving2Blocked(x, y, &newx, &newy);
6277 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6280 MovDir[x][y] = old_move_dir;
6283 else if (move_pattern == MV_WHEN_PUSHED ||
6284 move_pattern == MV_WHEN_DROPPED)
6286 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6287 MovDir[x][y] = MV_NONE;
6291 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6293 static int test_xy[7][2] =
6303 static int test_dir[7] =
6313 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6314 int move_preference = -1000000; /* start with very low preference */
6315 int new_move_dir = MV_NONE;
6316 int start_test = RND(4);
6319 for (i = 0; i < NUM_DIRECTIONS; i++)
6321 int move_dir = test_dir[start_test + i];
6322 int move_dir_preference;
6324 xx = x + test_xy[start_test + i][0];
6325 yy = y + test_xy[start_test + i][1];
6327 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6328 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6330 new_move_dir = move_dir;
6335 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6338 move_dir_preference = -1 * RunnerVisit[xx][yy];
6339 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6340 move_dir_preference = PlayerVisit[xx][yy];
6342 if (move_dir_preference > move_preference)
6344 /* prefer field that has not been visited for the longest time */
6345 move_preference = move_dir_preference;
6346 new_move_dir = move_dir;
6348 else if (move_dir_preference == move_preference &&
6349 move_dir == old_move_dir)
6351 /* prefer last direction when all directions are preferred equally */
6352 move_preference = move_dir_preference;
6353 new_move_dir = move_dir;
6357 MovDir[x][y] = new_move_dir;
6358 if (old_move_dir != new_move_dir)
6359 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6363 static void TurnRound(int x, int y)
6365 int direction = MovDir[x][y];
6369 GfxDir[x][y] = MovDir[x][y];
6371 if (direction != MovDir[x][y])
6375 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6377 ResetGfxFrame(x, y, FALSE);
6380 static boolean JustBeingPushed(int x, int y)
6384 for (i = 0; i < MAX_PLAYERS; i++)
6386 struct PlayerInfo *player = &stored_player[i];
6388 if (player->active && player->is_pushing && player->MovPos)
6390 int next_jx = player->jx + (player->jx - player->last_jx);
6391 int next_jy = player->jy + (player->jy - player->last_jy);
6393 if (x == next_jx && y == next_jy)
6401 void StartMoving(int x, int y)
6403 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6404 int element = Feld[x][y];
6409 if (MovDelay[x][y] == 0)
6410 GfxAction[x][y] = ACTION_DEFAULT;
6412 if (CAN_FALL(element) && y < lev_fieldy - 1)
6414 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6415 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6416 if (JustBeingPushed(x, y))
6419 if (element == EL_QUICKSAND_FULL)
6421 if (IS_FREE(x, y + 1))
6423 InitMovingField(x, y, MV_DOWN);
6424 started_moving = TRUE;
6426 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6427 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6428 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6429 Store[x][y] = EL_ROCK;
6431 Store[x][y] = EL_ROCK;
6434 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6436 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6438 if (!MovDelay[x][y])
6439 MovDelay[x][y] = TILEY + 1;
6448 Feld[x][y] = EL_QUICKSAND_EMPTY;
6449 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6450 Store[x][y + 1] = Store[x][y];
6453 PlayLevelSoundAction(x, y, ACTION_FILLING);
6456 else if (element == EL_QUICKSAND_FAST_FULL)
6458 if (IS_FREE(x, y + 1))
6460 InitMovingField(x, y, MV_DOWN);
6461 started_moving = TRUE;
6463 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6464 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6465 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6466 Store[x][y] = EL_ROCK;
6468 Store[x][y] = EL_ROCK;
6471 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6473 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6475 if (!MovDelay[x][y])
6476 MovDelay[x][y] = TILEY + 1;
6485 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6486 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6487 Store[x][y + 1] = Store[x][y];
6490 PlayLevelSoundAction(x, y, ACTION_FILLING);
6493 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6494 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6496 InitMovingField(x, y, MV_DOWN);
6497 started_moving = TRUE;
6499 Feld[x][y] = EL_QUICKSAND_FILLING;
6500 Store[x][y] = element;
6502 PlayLevelSoundAction(x, y, ACTION_FILLING);
6504 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6505 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6507 InitMovingField(x, y, MV_DOWN);
6508 started_moving = TRUE;
6510 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6511 Store[x][y] = element;
6513 PlayLevelSoundAction(x, y, ACTION_FILLING);
6515 else if (element == EL_MAGIC_WALL_FULL)
6517 if (IS_FREE(x, y + 1))
6519 InitMovingField(x, y, MV_DOWN);
6520 started_moving = TRUE;
6522 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6523 Store[x][y] = EL_CHANGED(Store[x][y]);
6525 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6527 if (!MovDelay[x][y])
6528 MovDelay[x][y] = TILEY/4 + 1;
6537 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6538 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6539 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6543 else if (element == EL_BD_MAGIC_WALL_FULL)
6545 if (IS_FREE(x, y + 1))
6547 InitMovingField(x, y, MV_DOWN);
6548 started_moving = TRUE;
6550 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6551 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6553 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6555 if (!MovDelay[x][y])
6556 MovDelay[x][y] = TILEY/4 + 1;
6565 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6566 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6567 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6571 else if (element == EL_DC_MAGIC_WALL_FULL)
6573 if (IS_FREE(x, y + 1))
6575 InitMovingField(x, y, MV_DOWN);
6576 started_moving = TRUE;
6578 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6579 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6581 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6583 if (!MovDelay[x][y])
6584 MovDelay[x][y] = TILEY/4 + 1;
6593 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6594 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6595 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6599 else if ((CAN_PASS_MAGIC_WALL(element) &&
6600 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6601 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6602 (CAN_PASS_DC_MAGIC_WALL(element) &&
6603 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6606 InitMovingField(x, y, MV_DOWN);
6607 started_moving = TRUE;
6610 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6611 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6612 EL_DC_MAGIC_WALL_FILLING);
6613 Store[x][y] = element;
6615 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6617 SplashAcid(x, y + 1);
6619 InitMovingField(x, y, MV_DOWN);
6620 started_moving = TRUE;
6622 Store[x][y] = EL_ACID;
6625 #if USE_FIX_IMPACT_COLLISION
6626 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6627 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6629 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6630 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6632 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6633 CAN_FALL(element) && WasJustFalling[x][y] &&
6634 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6636 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6637 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6638 (Feld[x][y + 1] == EL_BLOCKED)))
6640 /* this is needed for a special case not covered by calling "Impact()"
6641 from "ContinueMoving()": if an element moves to a tile directly below
6642 another element which was just falling on that tile (which was empty
6643 in the previous frame), the falling element above would just stop
6644 instead of smashing the element below (in previous version, the above
6645 element was just checked for "moving" instead of "falling", resulting
6646 in incorrect smashes caused by horizontal movement of the above
6647 element; also, the case of the player being the element to smash was
6648 simply not covered here... :-/ ) */
6650 CheckCollision[x][y] = 0;
6651 CheckImpact[x][y] = 0;
6655 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6657 if (MovDir[x][y] == MV_NONE)
6659 InitMovingField(x, y, MV_DOWN);
6660 started_moving = TRUE;
6663 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6665 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6666 MovDir[x][y] = MV_DOWN;
6668 InitMovingField(x, y, MV_DOWN);
6669 started_moving = TRUE;
6671 else if (element == EL_AMOEBA_DROP)
6673 Feld[x][y] = EL_AMOEBA_GROWING;
6674 Store[x][y] = EL_AMOEBA_WET;
6676 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6677 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6678 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6679 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6681 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6682 (IS_FREE(x - 1, y + 1) ||
6683 Feld[x - 1][y + 1] == EL_ACID));
6684 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6685 (IS_FREE(x + 1, y + 1) ||
6686 Feld[x + 1][y + 1] == EL_ACID));
6687 boolean can_fall_any = (can_fall_left || can_fall_right);
6688 boolean can_fall_both = (can_fall_left && can_fall_right);
6689 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6691 #if USE_NEW_ALL_SLIPPERY
6692 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6694 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6695 can_fall_right = FALSE;
6696 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6697 can_fall_left = FALSE;
6698 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6699 can_fall_right = FALSE;
6700 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6701 can_fall_left = FALSE;
6703 can_fall_any = (can_fall_left || can_fall_right);
6704 can_fall_both = FALSE;
6707 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6709 if (slippery_type == SLIPPERY_ONLY_LEFT)
6710 can_fall_right = FALSE;
6711 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6712 can_fall_left = FALSE;
6713 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6714 can_fall_right = FALSE;
6715 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6716 can_fall_left = FALSE;
6718 can_fall_any = (can_fall_left || can_fall_right);
6719 can_fall_both = (can_fall_left && can_fall_right);
6723 #if USE_NEW_ALL_SLIPPERY
6725 #if USE_NEW_SP_SLIPPERY
6726 /* !!! better use the same properties as for custom elements here !!! */
6727 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6728 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6730 can_fall_right = FALSE; /* slip down on left side */
6731 can_fall_both = FALSE;
6736 #if USE_NEW_ALL_SLIPPERY
6739 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6740 can_fall_right = FALSE; /* slip down on left side */
6742 can_fall_left = !(can_fall_right = RND(2));
6744 can_fall_both = FALSE;
6749 if (game.emulation == EMU_BOULDERDASH ||
6750 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6751 can_fall_right = FALSE; /* slip down on left side */
6753 can_fall_left = !(can_fall_right = RND(2));
6755 can_fall_both = FALSE;
6761 /* if not determined otherwise, prefer left side for slipping down */
6762 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6763 started_moving = TRUE;
6767 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6769 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6772 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6773 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6774 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6775 int belt_dir = game.belt_dir[belt_nr];
6777 if ((belt_dir == MV_LEFT && left_is_free) ||
6778 (belt_dir == MV_RIGHT && right_is_free))
6780 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6782 InitMovingField(x, y, belt_dir);
6783 started_moving = TRUE;
6785 Pushed[x][y] = TRUE;
6786 Pushed[nextx][y] = TRUE;
6788 GfxAction[x][y] = ACTION_DEFAULT;
6792 MovDir[x][y] = 0; /* if element was moving, stop it */
6797 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6799 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6801 if (CAN_MOVE(element) && !started_moving)
6804 int move_pattern = element_info[element].move_pattern;
6809 if (MovDir[x][y] == MV_NONE)
6811 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6812 x, y, element, element_info[element].token_name);
6813 printf("StartMoving(): This should never happen!\n");
6818 Moving2Blocked(x, y, &newx, &newy);
6820 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6823 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6824 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6826 WasJustMoving[x][y] = 0;
6827 CheckCollision[x][y] = 0;
6829 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6831 if (Feld[x][y] != element) /* element has changed */
6835 if (!MovDelay[x][y]) /* start new movement phase */
6837 /* all objects that can change their move direction after each step
6838 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6840 if (element != EL_YAMYAM &&
6841 element != EL_DARK_YAMYAM &&
6842 element != EL_PACMAN &&
6843 !(move_pattern & MV_ANY_DIRECTION) &&
6844 move_pattern != MV_TURNING_LEFT &&
6845 move_pattern != MV_TURNING_RIGHT &&
6846 move_pattern != MV_TURNING_LEFT_RIGHT &&
6847 move_pattern != MV_TURNING_RIGHT_LEFT &&
6848 move_pattern != MV_TURNING_RANDOM)
6852 if (MovDelay[x][y] && (element == EL_BUG ||
6853 element == EL_SPACESHIP ||
6854 element == EL_SP_SNIKSNAK ||
6855 element == EL_SP_ELECTRON ||
6856 element == EL_MOLE))
6857 DrawLevelField(x, y);
6861 if (MovDelay[x][y]) /* wait some time before next movement */
6865 if (element == EL_ROBOT ||
6866 element == EL_YAMYAM ||
6867 element == EL_DARK_YAMYAM)
6869 DrawLevelElementAnimationIfNeeded(x, y, element);
6870 PlayLevelSoundAction(x, y, ACTION_WAITING);
6872 else if (element == EL_SP_ELECTRON)
6873 DrawLevelElementAnimationIfNeeded(x, y, element);
6874 else if (element == EL_DRAGON)
6877 int dir = MovDir[x][y];
6878 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6879 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6880 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6881 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6882 dir == MV_UP ? IMG_FLAMES_1_UP :
6883 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6884 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6886 GfxAction[x][y] = ACTION_ATTACKING;
6888 if (IS_PLAYER(x, y))
6889 DrawPlayerField(x, y);
6891 DrawLevelField(x, y);
6893 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6895 for (i = 1; i <= 3; i++)
6897 int xx = x + i * dx;
6898 int yy = y + i * dy;
6899 int sx = SCREENX(xx);
6900 int sy = SCREENY(yy);
6901 int flame_graphic = graphic + (i - 1);
6903 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6908 int flamed = MovingOrBlocked2Element(xx, yy);
6912 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6914 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6915 RemoveMovingField(xx, yy);
6917 RemoveField(xx, yy);
6919 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6922 RemoveMovingField(xx, yy);
6925 ChangeDelay[xx][yy] = 0;
6927 Feld[xx][yy] = EL_FLAMES;
6929 if (IN_SCR_FIELD(sx, sy))
6931 DrawLevelFieldCrumbledSand(xx, yy);
6932 DrawGraphic(sx, sy, flame_graphic, frame);
6937 if (Feld[xx][yy] == EL_FLAMES)
6938 Feld[xx][yy] = EL_EMPTY;
6939 DrawLevelField(xx, yy);
6944 if (MovDelay[x][y]) /* element still has to wait some time */
6946 PlayLevelSoundAction(x, y, ACTION_WAITING);
6952 /* now make next step */
6954 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6956 if (DONT_COLLIDE_WITH(element) &&
6957 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6958 !PLAYER_ENEMY_PROTECTED(newx, newy))
6960 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6965 else if (CAN_MOVE_INTO_ACID(element) &&
6966 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6967 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6968 (MovDir[x][y] == MV_DOWN ||
6969 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6971 SplashAcid(newx, newy);
6972 Store[x][y] = EL_ACID;
6974 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6976 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6977 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6978 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6979 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6982 DrawLevelField(x, y);
6984 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6985 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6986 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6988 local_player->friends_still_needed--;
6989 if (!local_player->friends_still_needed &&
6990 !local_player->GameOver && AllPlayersGone)
6991 PlayerWins(local_player);
6995 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6997 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6998 DrawLevelField(newx, newy);
7000 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7002 else if (!IS_FREE(newx, newy))
7004 GfxAction[x][y] = ACTION_WAITING;
7006 if (IS_PLAYER(x, y))
7007 DrawPlayerField(x, y);
7009 DrawLevelField(x, y);
7014 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7016 if (IS_FOOD_PIG(Feld[newx][newy]))
7018 if (IS_MOVING(newx, newy))
7019 RemoveMovingField(newx, newy);
7022 Feld[newx][newy] = EL_EMPTY;
7023 DrawLevelField(newx, newy);
7026 PlayLevelSound(x, y, SND_PIG_DIGGING);
7028 else if (!IS_FREE(newx, newy))
7030 if (IS_PLAYER(x, y))
7031 DrawPlayerField(x, y);
7033 DrawLevelField(x, y);
7038 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7040 if (Store[x][y] != EL_EMPTY)
7042 boolean can_clone = FALSE;
7045 /* check if element to clone is still there */
7046 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7048 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7056 /* cannot clone or target field not free anymore -- do not clone */
7057 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7058 Store[x][y] = EL_EMPTY;
7061 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7063 if (IS_MV_DIAGONAL(MovDir[x][y]))
7065 int diagonal_move_dir = MovDir[x][y];
7066 int stored = Store[x][y];
7067 int change_delay = 8;
7070 /* android is moving diagonally */
7072 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7074 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7075 GfxElement[x][y] = EL_EMC_ANDROID;
7076 GfxAction[x][y] = ACTION_SHRINKING;
7077 GfxDir[x][y] = diagonal_move_dir;
7078 ChangeDelay[x][y] = change_delay;
7080 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7083 DrawLevelGraphicAnimation(x, y, graphic);
7084 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7086 if (Feld[newx][newy] == EL_ACID)
7088 SplashAcid(newx, newy);
7093 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7095 Store[newx][newy] = EL_EMC_ANDROID;
7096 GfxElement[newx][newy] = EL_EMC_ANDROID;
7097 GfxAction[newx][newy] = ACTION_GROWING;
7098 GfxDir[newx][newy] = diagonal_move_dir;
7099 ChangeDelay[newx][newy] = change_delay;
7101 graphic = el_act_dir2img(GfxElement[newx][newy],
7102 GfxAction[newx][newy], GfxDir[newx][newy]);
7104 DrawLevelGraphicAnimation(newx, newy, graphic);
7105 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7111 Feld[newx][newy] = EL_EMPTY;
7112 DrawLevelField(newx, newy);
7114 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7117 else if (!IS_FREE(newx, newy))
7120 if (IS_PLAYER(x, y))
7121 DrawPlayerField(x, y);
7123 DrawLevelField(x, y);
7129 else if (IS_CUSTOM_ELEMENT(element) &&
7130 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7132 int new_element = Feld[newx][newy];
7134 if (!IS_FREE(newx, newy))
7136 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7137 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7140 /* no element can dig solid indestructible elements */
7141 if (IS_INDESTRUCTIBLE(new_element) &&
7142 !IS_DIGGABLE(new_element) &&
7143 !IS_COLLECTIBLE(new_element))
7146 if (AmoebaNr[newx][newy] &&
7147 (new_element == EL_AMOEBA_FULL ||
7148 new_element == EL_BD_AMOEBA ||
7149 new_element == EL_AMOEBA_GROWING))
7151 AmoebaCnt[AmoebaNr[newx][newy]]--;
7152 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7155 if (IS_MOVING(newx, newy))
7156 RemoveMovingField(newx, newy);
7159 RemoveField(newx, newy);
7160 DrawLevelField(newx, newy);
7163 /* if digged element was about to explode, prevent the explosion */
7164 ExplodeField[newx][newy] = EX_TYPE_NONE;
7166 PlayLevelSoundAction(x, y, action);
7169 Store[newx][newy] = EL_EMPTY;
7171 /* this makes it possible to leave the removed element again */
7172 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7173 Store[newx][newy] = new_element;
7175 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7177 int move_leave_element = element_info[element].move_leave_element;
7179 /* this makes it possible to leave the removed element again */
7180 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7181 new_element : move_leave_element);
7185 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7187 RunnerVisit[x][y] = FrameCounter;
7188 PlayerVisit[x][y] /= 8; /* expire player visit path */
7191 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7193 if (!IS_FREE(newx, newy))
7195 if (IS_PLAYER(x, y))
7196 DrawPlayerField(x, y);
7198 DrawLevelField(x, y);
7204 boolean wanna_flame = !RND(10);
7205 int dx = newx - x, dy = newy - y;
7206 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7207 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7208 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7209 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7210 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7211 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7214 IS_CLASSIC_ENEMY(element1) ||
7215 IS_CLASSIC_ENEMY(element2)) &&
7216 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7217 element1 != EL_FLAMES && element2 != EL_FLAMES)
7219 ResetGfxAnimation(x, y);
7220 GfxAction[x][y] = ACTION_ATTACKING;
7222 if (IS_PLAYER(x, y))
7223 DrawPlayerField(x, y);
7225 DrawLevelField(x, y);
7227 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7229 MovDelay[x][y] = 50;
7233 RemoveField(newx, newy);
7235 Feld[newx][newy] = EL_FLAMES;
7236 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7239 RemoveField(newx1, newy1);
7241 Feld[newx1][newy1] = EL_FLAMES;
7243 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7246 RemoveField(newx2, newy2);
7248 Feld[newx2][newy2] = EL_FLAMES;
7255 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7256 Feld[newx][newy] == EL_DIAMOND)
7258 if (IS_MOVING(newx, newy))
7259 RemoveMovingField(newx, newy);
7262 Feld[newx][newy] = EL_EMPTY;
7263 DrawLevelField(newx, newy);
7266 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7268 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7269 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7271 if (AmoebaNr[newx][newy])
7273 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7274 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7275 Feld[newx][newy] == EL_BD_AMOEBA)
7276 AmoebaCnt[AmoebaNr[newx][newy]]--;
7281 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7283 RemoveMovingField(newx, newy);
7286 if (IS_MOVING(newx, newy))
7288 RemoveMovingField(newx, newy);
7293 Feld[newx][newy] = EL_EMPTY;
7294 DrawLevelField(newx, newy);
7297 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7299 else if ((element == EL_PACMAN || element == EL_MOLE)
7300 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7302 if (AmoebaNr[newx][newy])
7304 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7305 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7306 Feld[newx][newy] == EL_BD_AMOEBA)
7307 AmoebaCnt[AmoebaNr[newx][newy]]--;
7310 if (element == EL_MOLE)
7312 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7313 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7315 ResetGfxAnimation(x, y);
7316 GfxAction[x][y] = ACTION_DIGGING;
7317 DrawLevelField(x, y);
7319 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7321 return; /* wait for shrinking amoeba */
7323 else /* element == EL_PACMAN */
7325 Feld[newx][newy] = EL_EMPTY;
7326 DrawLevelField(newx, newy);
7327 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7330 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7331 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7332 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7334 /* wait for shrinking amoeba to completely disappear */
7337 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7339 /* object was running against a wall */
7344 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7345 if (move_pattern & MV_ANY_DIRECTION &&
7346 move_pattern == MovDir[x][y])
7348 int blocking_element =
7349 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7351 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7354 element = Feld[x][y]; /* element might have changed */
7358 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7359 DrawLevelElementAnimation(x, y, element);
7361 if (DONT_TOUCH(element))
7362 TestIfBadThingTouchesPlayer(x, y);
7367 InitMovingField(x, y, MovDir[x][y]);
7369 PlayLevelSoundAction(x, y, ACTION_MOVING);
7373 ContinueMoving(x, y);
7376 void ContinueMoving(int x, int y)
7378 int element = Feld[x][y];
7379 struct ElementInfo *ei = &element_info[element];
7380 int direction = MovDir[x][y];
7381 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7382 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7383 int newx = x + dx, newy = y + dy;
7384 int stored = Store[x][y];
7385 int stored_new = Store[newx][newy];
7386 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7387 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7388 boolean last_line = (newy == lev_fieldy - 1);
7390 MovPos[x][y] += getElementMoveStepsize(x, y);
7392 if (pushed_by_player) /* special case: moving object pushed by player */
7393 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7395 if (ABS(MovPos[x][y]) < TILEX)
7398 int ee = Feld[x][y];
7399 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7400 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7402 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7403 x, y, ABS(MovPos[x][y]),
7405 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7408 DrawLevelField(x, y);
7410 return; /* element is still moving */
7413 /* element reached destination field */
7415 Feld[x][y] = EL_EMPTY;
7416 Feld[newx][newy] = element;
7417 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7419 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7421 element = Feld[newx][newy] = EL_ACID;
7423 else if (element == EL_MOLE)
7425 Feld[x][y] = EL_SAND;
7427 DrawLevelFieldCrumbledSandNeighbours(x, y);
7429 else if (element == EL_QUICKSAND_FILLING)
7431 element = Feld[newx][newy] = get_next_element(element);
7432 Store[newx][newy] = Store[x][y];
7434 else if (element == EL_QUICKSAND_EMPTYING)
7436 Feld[x][y] = get_next_element(element);
7437 element = Feld[newx][newy] = Store[x][y];
7439 else if (element == EL_QUICKSAND_FAST_FILLING)
7441 element = Feld[newx][newy] = get_next_element(element);
7442 Store[newx][newy] = Store[x][y];
7444 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7446 Feld[x][y] = get_next_element(element);
7447 element = Feld[newx][newy] = Store[x][y];
7449 else if (element == EL_MAGIC_WALL_FILLING)
7451 element = Feld[newx][newy] = get_next_element(element);
7452 if (!game.magic_wall_active)
7453 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7454 Store[newx][newy] = Store[x][y];
7456 else if (element == EL_MAGIC_WALL_EMPTYING)
7458 Feld[x][y] = get_next_element(element);
7459 if (!game.magic_wall_active)
7460 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7461 element = Feld[newx][newy] = Store[x][y];
7463 #if USE_NEW_CUSTOM_VALUE
7464 InitField(newx, newy, FALSE);
7467 else if (element == EL_BD_MAGIC_WALL_FILLING)
7469 element = Feld[newx][newy] = get_next_element(element);
7470 if (!game.magic_wall_active)
7471 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7472 Store[newx][newy] = Store[x][y];
7474 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7476 Feld[x][y] = get_next_element(element);
7477 if (!game.magic_wall_active)
7478 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7479 element = Feld[newx][newy] = Store[x][y];
7481 #if USE_NEW_CUSTOM_VALUE
7482 InitField(newx, newy, FALSE);
7485 else if (element == EL_DC_MAGIC_WALL_FILLING)
7487 element = Feld[newx][newy] = get_next_element(element);
7488 if (!game.magic_wall_active)
7489 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7490 Store[newx][newy] = Store[x][y];
7492 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7494 Feld[x][y] = get_next_element(element);
7495 if (!game.magic_wall_active)
7496 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7497 element = Feld[newx][newy] = Store[x][y];
7499 #if USE_NEW_CUSTOM_VALUE
7500 InitField(newx, newy, FALSE);
7503 else if (element == EL_AMOEBA_DROPPING)
7505 Feld[x][y] = get_next_element(element);
7506 element = Feld[newx][newy] = Store[x][y];
7508 else if (element == EL_SOKOBAN_OBJECT)
7511 Feld[x][y] = Back[x][y];
7513 if (Back[newx][newy])
7514 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7516 Back[x][y] = Back[newx][newy] = 0;
7519 Store[x][y] = EL_EMPTY;
7524 MovDelay[newx][newy] = 0;
7526 if (CAN_CHANGE_OR_HAS_ACTION(element))
7528 /* copy element change control values to new field */
7529 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7530 ChangePage[newx][newy] = ChangePage[x][y];
7531 ChangeCount[newx][newy] = ChangeCount[x][y];
7532 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7535 #if USE_NEW_CUSTOM_VALUE
7536 CustomValue[newx][newy] = CustomValue[x][y];
7539 ChangeDelay[x][y] = 0;
7540 ChangePage[x][y] = -1;
7541 ChangeCount[x][y] = 0;
7542 ChangeEvent[x][y] = -1;
7544 #if USE_NEW_CUSTOM_VALUE
7545 CustomValue[x][y] = 0;
7548 /* copy animation control values to new field */
7549 GfxFrame[newx][newy] = GfxFrame[x][y];
7550 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7551 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7552 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7554 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7556 /* some elements can leave other elements behind after moving */
7558 if (ei->move_leave_element != EL_EMPTY &&
7559 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7560 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7562 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7563 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7564 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7567 int move_leave_element = ei->move_leave_element;
7571 /* this makes it possible to leave the removed element again */
7572 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7573 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7575 /* this makes it possible to leave the removed element again */
7576 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7577 move_leave_element = stored;
7580 /* this makes it possible to leave the removed element again */
7581 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7582 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7583 move_leave_element = stored;
7586 Feld[x][y] = move_leave_element;
7588 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7589 MovDir[x][y] = direction;
7591 InitField(x, y, FALSE);
7593 if (GFX_CRUMBLED(Feld[x][y]))
7594 DrawLevelFieldCrumbledSandNeighbours(x, y);
7596 if (ELEM_IS_PLAYER(move_leave_element))
7597 RelocatePlayer(x, y, move_leave_element);
7600 /* do this after checking for left-behind element */
7601 ResetGfxAnimation(x, y); /* reset animation values for old field */
7603 if (!CAN_MOVE(element) ||
7604 (CAN_FALL(element) && direction == MV_DOWN &&
7605 (element == EL_SPRING ||
7606 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7607 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7608 GfxDir[x][y] = MovDir[newx][newy] = 0;
7610 DrawLevelField(x, y);
7611 DrawLevelField(newx, newy);
7613 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7615 /* prevent pushed element from moving on in pushed direction */
7616 if (pushed_by_player && CAN_MOVE(element) &&
7617 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7618 !(element_info[element].move_pattern & direction))
7619 TurnRound(newx, newy);
7621 /* prevent elements on conveyor belt from moving on in last direction */
7622 if (pushed_by_conveyor && CAN_FALL(element) &&
7623 direction & MV_HORIZONTAL)
7624 MovDir[newx][newy] = 0;
7626 if (!pushed_by_player)
7628 int nextx = newx + dx, nexty = newy + dy;
7629 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7631 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7633 if (CAN_FALL(element) && direction == MV_DOWN)
7634 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7636 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7637 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7639 #if USE_FIX_IMPACT_COLLISION
7640 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7641 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7645 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7647 TestIfBadThingTouchesPlayer(newx, newy);
7648 TestIfBadThingTouchesFriend(newx, newy);
7650 if (!IS_CUSTOM_ELEMENT(element))
7651 TestIfBadThingTouchesOtherBadThing(newx, newy);
7653 else if (element == EL_PENGUIN)
7654 TestIfFriendTouchesBadThing(newx, newy);
7656 /* give the player one last chance (one more frame) to move away */
7657 if (CAN_FALL(element) && direction == MV_DOWN &&
7658 (last_line || (!IS_FREE(x, newy + 1) &&
7659 (!IS_PLAYER(x, newy + 1) ||
7660 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7663 if (pushed_by_player && !game.use_change_when_pushing_bug)
7665 int push_side = MV_DIR_OPPOSITE(direction);
7666 struct PlayerInfo *player = PLAYERINFO(x, y);
7668 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7669 player->index_bit, push_side);
7670 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7671 player->index_bit, push_side);
7674 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7675 MovDelay[newx][newy] = 1;
7677 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7679 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7682 if (ChangePage[newx][newy] != -1) /* delayed change */
7684 int page = ChangePage[newx][newy];
7685 struct ElementChangeInfo *change = &ei->change_page[page];
7687 ChangePage[newx][newy] = -1;
7689 if (change->can_change)
7691 if (ChangeElement(newx, newy, element, page))
7693 if (change->post_change_function)
7694 change->post_change_function(newx, newy);
7698 if (change->has_action)
7699 ExecuteCustomElementAction(newx, newy, element, page);
7703 TestIfElementHitsCustomElement(newx, newy, direction);
7704 TestIfPlayerTouchesCustomElement(newx, newy);
7705 TestIfElementTouchesCustomElement(newx, newy);
7707 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7708 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7709 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7710 MV_DIR_OPPOSITE(direction));
7713 int AmoebeNachbarNr(int ax, int ay)
7716 int element = Feld[ax][ay];
7718 static int xy[4][2] =
7726 for (i = 0; i < NUM_DIRECTIONS; i++)
7728 int x = ax + xy[i][0];
7729 int y = ay + xy[i][1];
7731 if (!IN_LEV_FIELD(x, y))
7734 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7735 group_nr = AmoebaNr[x][y];
7741 void AmoebenVereinigen(int ax, int ay)
7743 int i, x, y, xx, yy;
7744 int new_group_nr = AmoebaNr[ax][ay];
7745 static int xy[4][2] =
7753 if (new_group_nr == 0)
7756 for (i = 0; i < NUM_DIRECTIONS; i++)
7761 if (!IN_LEV_FIELD(x, y))
7764 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7765 Feld[x][y] == EL_BD_AMOEBA ||
7766 Feld[x][y] == EL_AMOEBA_DEAD) &&
7767 AmoebaNr[x][y] != new_group_nr)
7769 int old_group_nr = AmoebaNr[x][y];
7771 if (old_group_nr == 0)
7774 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7775 AmoebaCnt[old_group_nr] = 0;
7776 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7777 AmoebaCnt2[old_group_nr] = 0;
7779 SCAN_PLAYFIELD(xx, yy)
7781 if (AmoebaNr[xx][yy] == old_group_nr)
7782 AmoebaNr[xx][yy] = new_group_nr;
7788 void AmoebeUmwandeln(int ax, int ay)
7792 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7794 int group_nr = AmoebaNr[ax][ay];
7799 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7800 printf("AmoebeUmwandeln(): This should never happen!\n");
7805 SCAN_PLAYFIELD(x, y)
7807 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7810 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7814 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7815 SND_AMOEBA_TURNING_TO_GEM :
7816 SND_AMOEBA_TURNING_TO_ROCK));
7821 static int xy[4][2] =
7829 for (i = 0; i < NUM_DIRECTIONS; i++)
7834 if (!IN_LEV_FIELD(x, y))
7837 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7839 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7840 SND_AMOEBA_TURNING_TO_GEM :
7841 SND_AMOEBA_TURNING_TO_ROCK));
7848 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7851 int group_nr = AmoebaNr[ax][ay];
7852 boolean done = FALSE;
7857 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7858 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7863 SCAN_PLAYFIELD(x, y)
7865 if (AmoebaNr[x][y] == group_nr &&
7866 (Feld[x][y] == EL_AMOEBA_DEAD ||
7867 Feld[x][y] == EL_BD_AMOEBA ||
7868 Feld[x][y] == EL_AMOEBA_GROWING))
7871 Feld[x][y] = new_element;
7872 InitField(x, y, FALSE);
7873 DrawLevelField(x, y);
7879 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7880 SND_BD_AMOEBA_TURNING_TO_ROCK :
7881 SND_BD_AMOEBA_TURNING_TO_GEM));
7884 void AmoebeWaechst(int x, int y)
7886 static unsigned long sound_delay = 0;
7887 static unsigned long sound_delay_value = 0;
7889 if (!MovDelay[x][y]) /* start new growing cycle */
7893 if (DelayReached(&sound_delay, sound_delay_value))
7895 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7896 sound_delay_value = 30;
7900 if (MovDelay[x][y]) /* wait some time before growing bigger */
7903 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7905 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7906 6 - MovDelay[x][y]);
7908 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7911 if (!MovDelay[x][y])
7913 Feld[x][y] = Store[x][y];
7915 DrawLevelField(x, y);
7920 void AmoebaDisappearing(int x, int y)
7922 static unsigned long sound_delay = 0;
7923 static unsigned long sound_delay_value = 0;
7925 if (!MovDelay[x][y]) /* start new shrinking cycle */
7929 if (DelayReached(&sound_delay, sound_delay_value))
7930 sound_delay_value = 30;
7933 if (MovDelay[x][y]) /* wait some time before shrinking */
7936 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7938 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7939 6 - MovDelay[x][y]);
7941 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7944 if (!MovDelay[x][y])
7946 Feld[x][y] = EL_EMPTY;
7947 DrawLevelField(x, y);
7949 /* don't let mole enter this field in this cycle;
7950 (give priority to objects falling to this field from above) */
7956 void AmoebeAbleger(int ax, int ay)
7959 int element = Feld[ax][ay];
7960 int graphic = el2img(element);
7961 int newax = ax, neway = ay;
7962 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7963 static int xy[4][2] =
7971 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7973 Feld[ax][ay] = EL_AMOEBA_DEAD;
7974 DrawLevelField(ax, ay);
7978 if (IS_ANIMATED(graphic))
7979 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7981 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7982 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7984 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7987 if (MovDelay[ax][ay])
7991 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7994 int x = ax + xy[start][0];
7995 int y = ay + xy[start][1];
7997 if (!IN_LEV_FIELD(x, y))
8000 if (IS_FREE(x, y) ||
8001 CAN_GROW_INTO(Feld[x][y]) ||
8002 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8003 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8009 if (newax == ax && neway == ay)
8012 else /* normal or "filled" (BD style) amoeba */
8015 boolean waiting_for_player = FALSE;
8017 for (i = 0; i < NUM_DIRECTIONS; i++)
8019 int j = (start + i) % 4;
8020 int x = ax + xy[j][0];
8021 int y = ay + xy[j][1];
8023 if (!IN_LEV_FIELD(x, y))
8026 if (IS_FREE(x, y) ||
8027 CAN_GROW_INTO(Feld[x][y]) ||
8028 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8029 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8035 else if (IS_PLAYER(x, y))
8036 waiting_for_player = TRUE;
8039 if (newax == ax && neway == ay) /* amoeba cannot grow */
8041 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8043 Feld[ax][ay] = EL_AMOEBA_DEAD;
8044 DrawLevelField(ax, ay);
8045 AmoebaCnt[AmoebaNr[ax][ay]]--;
8047 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8049 if (element == EL_AMOEBA_FULL)
8050 AmoebeUmwandeln(ax, ay);
8051 else if (element == EL_BD_AMOEBA)
8052 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8057 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8059 /* amoeba gets larger by growing in some direction */
8061 int new_group_nr = AmoebaNr[ax][ay];
8064 if (new_group_nr == 0)
8066 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8067 printf("AmoebeAbleger(): This should never happen!\n");
8072 AmoebaNr[newax][neway] = new_group_nr;
8073 AmoebaCnt[new_group_nr]++;
8074 AmoebaCnt2[new_group_nr]++;
8076 /* if amoeba touches other amoeba(s) after growing, unify them */
8077 AmoebenVereinigen(newax, neway);
8079 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8081 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8087 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8088 (neway == lev_fieldy - 1 && newax != ax))
8090 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8091 Store[newax][neway] = element;
8093 else if (neway == ay || element == EL_EMC_DRIPPER)
8095 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8097 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8101 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8102 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8103 Store[ax][ay] = EL_AMOEBA_DROP;
8104 ContinueMoving(ax, ay);
8108 DrawLevelField(newax, neway);
8111 void Life(int ax, int ay)
8115 int element = Feld[ax][ay];
8116 int graphic = el2img(element);
8117 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8119 boolean changed = FALSE;
8121 if (IS_ANIMATED(graphic))
8122 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8127 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8128 MovDelay[ax][ay] = life_time;
8130 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8133 if (MovDelay[ax][ay])
8137 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8139 int xx = ax+x1, yy = ay+y1;
8142 if (!IN_LEV_FIELD(xx, yy))
8145 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8147 int x = xx+x2, y = yy+y2;
8149 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8152 if (((Feld[x][y] == element ||
8153 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8155 (IS_FREE(x, y) && Stop[x][y]))
8159 if (xx == ax && yy == ay) /* field in the middle */
8161 if (nachbarn < life_parameter[0] ||
8162 nachbarn > life_parameter[1])
8164 Feld[xx][yy] = EL_EMPTY;
8166 DrawLevelField(xx, yy);
8167 Stop[xx][yy] = TRUE;
8171 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8172 { /* free border field */
8173 if (nachbarn >= life_parameter[2] &&
8174 nachbarn <= life_parameter[3])
8176 Feld[xx][yy] = element;
8177 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8179 DrawLevelField(xx, yy);
8180 Stop[xx][yy] = TRUE;
8187 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8188 SND_GAME_OF_LIFE_GROWING);
8191 static void InitRobotWheel(int x, int y)
8193 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8196 static void RunRobotWheel(int x, int y)
8198 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8201 static void StopRobotWheel(int x, int y)
8203 if (ZX == x && ZY == y)
8207 static void InitTimegateWheel(int x, int y)
8209 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8212 static void RunTimegateWheel(int x, int y)
8214 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8217 static void InitMagicBallDelay(int x, int y)
8220 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8222 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8226 static void ActivateMagicBall(int bx, int by)
8230 if (level.ball_random)
8232 int pos_border = RND(8); /* select one of the eight border elements */
8233 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8234 int xx = pos_content % 3;
8235 int yy = pos_content / 3;
8240 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8241 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8245 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8247 int xx = x - bx + 1;
8248 int yy = y - by + 1;
8250 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8251 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8255 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8258 void CheckExit(int x, int y)
8260 if (local_player->gems_still_needed > 0 ||
8261 local_player->sokobanfields_still_needed > 0 ||
8262 local_player->lights_still_needed > 0)
8264 int element = Feld[x][y];
8265 int graphic = el2img(element);
8267 if (IS_ANIMATED(graphic))
8268 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8273 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8276 Feld[x][y] = EL_EXIT_OPENING;
8278 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8281 void CheckExitEM(int x, int y)
8283 if (local_player->gems_still_needed > 0 ||
8284 local_player->sokobanfields_still_needed > 0 ||
8285 local_player->lights_still_needed > 0)
8287 int element = Feld[x][y];
8288 int graphic = el2img(element);
8290 if (IS_ANIMATED(graphic))
8291 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8296 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8299 Feld[x][y] = EL_EM_EXIT_OPENING;
8301 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8304 void CheckExitSteel(int x, int y)
8306 if (local_player->gems_still_needed > 0 ||
8307 local_player->sokobanfields_still_needed > 0 ||
8308 local_player->lights_still_needed > 0)
8310 int element = Feld[x][y];
8311 int graphic = el2img(element);
8313 if (IS_ANIMATED(graphic))
8314 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8319 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8322 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8324 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8327 void CheckExitSteelEM(int x, int y)
8329 if (local_player->gems_still_needed > 0 ||
8330 local_player->sokobanfields_still_needed > 0 ||
8331 local_player->lights_still_needed > 0)
8333 int element = Feld[x][y];
8334 int graphic = el2img(element);
8336 if (IS_ANIMATED(graphic))
8337 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8342 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8345 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8347 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8350 void CheckExitSP(int x, int y)
8352 if (local_player->gems_still_needed > 0)
8354 int element = Feld[x][y];
8355 int graphic = el2img(element);
8357 if (IS_ANIMATED(graphic))
8358 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8363 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8366 Feld[x][y] = EL_SP_EXIT_OPENING;
8368 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8371 static void CloseAllOpenTimegates()
8375 SCAN_PLAYFIELD(x, y)
8377 int element = Feld[x][y];
8379 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8381 Feld[x][y] = EL_TIMEGATE_CLOSING;
8383 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8388 void DrawTwinkleOnField(int x, int y)
8390 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8393 if (Feld[x][y] == EL_BD_DIAMOND)
8396 if (MovDelay[x][y] == 0) /* next animation frame */
8397 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8399 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8403 if (setup.direct_draw && MovDelay[x][y])
8404 SetDrawtoField(DRAW_BUFFERED);
8406 DrawLevelElementAnimation(x, y, Feld[x][y]);
8408 if (MovDelay[x][y] != 0)
8410 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8411 10 - MovDelay[x][y]);
8413 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8415 if (setup.direct_draw)
8419 dest_x = FX + SCREENX(x) * TILEX;
8420 dest_y = FY + SCREENY(y) * TILEY;
8422 BlitBitmap(drawto_field, window,
8423 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8424 SetDrawtoField(DRAW_DIRECT);
8430 void MauerWaechst(int x, int y)
8434 if (!MovDelay[x][y]) /* next animation frame */
8435 MovDelay[x][y] = 3 * delay;
8437 if (MovDelay[x][y]) /* wait some time before next frame */
8441 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8443 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8444 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8446 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8449 if (!MovDelay[x][y])
8451 if (MovDir[x][y] == MV_LEFT)
8453 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8454 DrawLevelField(x - 1, y);
8456 else if (MovDir[x][y] == MV_RIGHT)
8458 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8459 DrawLevelField(x + 1, y);
8461 else if (MovDir[x][y] == MV_UP)
8463 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8464 DrawLevelField(x, y - 1);
8468 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8469 DrawLevelField(x, y + 1);
8472 Feld[x][y] = Store[x][y];
8474 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8475 DrawLevelField(x, y);
8480 void MauerAbleger(int ax, int ay)
8482 int element = Feld[ax][ay];
8483 int graphic = el2img(element);
8484 boolean oben_frei = FALSE, unten_frei = FALSE;
8485 boolean links_frei = FALSE, rechts_frei = FALSE;
8486 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8487 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8488 boolean new_wall = FALSE;
8490 if (IS_ANIMATED(graphic))
8491 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8493 if (!MovDelay[ax][ay]) /* start building new wall */
8494 MovDelay[ax][ay] = 6;
8496 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8499 if (MovDelay[ax][ay])
8503 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8505 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8507 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8509 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8512 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8513 element == EL_EXPANDABLE_WALL_ANY)
8517 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8518 Store[ax][ay-1] = element;
8519 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8520 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8521 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8522 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8527 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8528 Store[ax][ay+1] = element;
8529 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8530 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8531 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8532 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8537 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8538 element == EL_EXPANDABLE_WALL_ANY ||
8539 element == EL_EXPANDABLE_WALL ||
8540 element == EL_BD_EXPANDABLE_WALL)
8544 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8545 Store[ax-1][ay] = element;
8546 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8547 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8548 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8549 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8555 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8556 Store[ax+1][ay] = element;
8557 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8558 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8559 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8560 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8565 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8566 DrawLevelField(ax, ay);
8568 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8570 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8571 unten_massiv = TRUE;
8572 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8573 links_massiv = TRUE;
8574 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8575 rechts_massiv = TRUE;
8577 if (((oben_massiv && unten_massiv) ||
8578 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8579 element == EL_EXPANDABLE_WALL) &&
8580 ((links_massiv && rechts_massiv) ||
8581 element == EL_EXPANDABLE_WALL_VERTICAL))
8582 Feld[ax][ay] = EL_WALL;
8585 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8588 void MauerAblegerStahl(int ax, int ay)
8590 int element = Feld[ax][ay];
8591 int graphic = el2img(element);
8592 boolean oben_frei = FALSE, unten_frei = FALSE;
8593 boolean links_frei = FALSE, rechts_frei = FALSE;
8594 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8595 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8596 boolean new_wall = FALSE;
8598 if (IS_ANIMATED(graphic))
8599 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8601 if (!MovDelay[ax][ay]) /* start building new wall */
8602 MovDelay[ax][ay] = 6;
8604 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8607 if (MovDelay[ax][ay])
8611 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8613 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8615 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8617 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8620 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8621 element == EL_EXPANDABLE_STEELWALL_ANY)
8625 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8626 Store[ax][ay-1] = element;
8627 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8628 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8629 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8630 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8635 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8636 Store[ax][ay+1] = element;
8637 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8638 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8639 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8640 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8645 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8646 element == EL_EXPANDABLE_STEELWALL_ANY)
8650 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8651 Store[ax-1][ay] = element;
8652 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8653 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8654 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8655 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8661 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8662 Store[ax+1][ay] = element;
8663 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8664 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8665 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8666 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8671 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8673 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8674 unten_massiv = TRUE;
8675 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8676 links_massiv = TRUE;
8677 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8678 rechts_massiv = TRUE;
8680 if (((oben_massiv && unten_massiv) ||
8681 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8682 ((links_massiv && rechts_massiv) ||
8683 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8684 Feld[ax][ay] = EL_WALL;
8687 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8690 void CheckForDragon(int x, int y)
8693 boolean dragon_found = FALSE;
8694 static int xy[4][2] =
8702 for (i = 0; i < NUM_DIRECTIONS; i++)
8704 for (j = 0; j < 4; j++)
8706 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8708 if (IN_LEV_FIELD(xx, yy) &&
8709 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8711 if (Feld[xx][yy] == EL_DRAGON)
8712 dragon_found = TRUE;
8721 for (i = 0; i < NUM_DIRECTIONS; i++)
8723 for (j = 0; j < 3; j++)
8725 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8727 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8729 Feld[xx][yy] = EL_EMPTY;
8730 DrawLevelField(xx, yy);
8739 static void InitBuggyBase(int x, int y)
8741 int element = Feld[x][y];
8742 int activating_delay = FRAMES_PER_SECOND / 4;
8745 (element == EL_SP_BUGGY_BASE ?
8746 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8747 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8749 element == EL_SP_BUGGY_BASE_ACTIVE ?
8750 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8753 static void WarnBuggyBase(int x, int y)
8756 static int xy[4][2] =
8764 for (i = 0; i < NUM_DIRECTIONS; i++)
8766 int xx = x + xy[i][0];
8767 int yy = y + xy[i][1];
8769 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8771 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8778 static void InitTrap(int x, int y)
8780 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8783 static void ActivateTrap(int x, int y)
8785 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8788 static void ChangeActiveTrap(int x, int y)
8790 int graphic = IMG_TRAP_ACTIVE;
8792 /* if new animation frame was drawn, correct crumbled sand border */
8793 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8794 DrawLevelFieldCrumbledSand(x, y);
8797 static int getSpecialActionElement(int element, int number, int base_element)
8799 return (element != EL_EMPTY ? element :
8800 number != -1 ? base_element + number - 1 :
8804 static int getModifiedActionNumber(int value_old, int operator, int operand,
8805 int value_min, int value_max)
8807 int value_new = (operator == CA_MODE_SET ? operand :
8808 operator == CA_MODE_ADD ? value_old + operand :
8809 operator == CA_MODE_SUBTRACT ? value_old - operand :
8810 operator == CA_MODE_MULTIPLY ? value_old * operand :
8811 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8812 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8815 return (value_new < value_min ? value_min :
8816 value_new > value_max ? value_max :
8820 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8822 struct ElementInfo *ei = &element_info[element];
8823 struct ElementChangeInfo *change = &ei->change_page[page];
8824 int target_element = change->target_element;
8825 int action_type = change->action_type;
8826 int action_mode = change->action_mode;
8827 int action_arg = change->action_arg;
8830 if (!change->has_action)
8833 /* ---------- determine action paramater values -------------------------- */
8835 int level_time_value =
8836 (level.time > 0 ? TimeLeft :
8839 int action_arg_element =
8840 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8841 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8842 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8845 int action_arg_direction =
8846 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8847 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8848 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8849 change->actual_trigger_side :
8850 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8851 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8854 int action_arg_number_min =
8855 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8858 int action_arg_number_max =
8859 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8860 action_type == CA_SET_LEVEL_GEMS ? 999 :
8861 action_type == CA_SET_LEVEL_TIME ? 9999 :
8862 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8863 action_type == CA_SET_CE_VALUE ? 9999 :
8864 action_type == CA_SET_CE_SCORE ? 9999 :
8867 int action_arg_number_reset =
8868 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8869 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8870 action_type == CA_SET_LEVEL_TIME ? level.time :
8871 action_type == CA_SET_LEVEL_SCORE ? 0 :
8872 #if USE_NEW_CUSTOM_VALUE
8873 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8875 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8877 action_type == CA_SET_CE_SCORE ? 0 :
8880 int action_arg_number =
8881 (action_arg <= CA_ARG_MAX ? action_arg :
8882 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8883 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8884 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8885 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8886 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8887 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8888 #if USE_NEW_CUSTOM_VALUE
8889 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8891 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8893 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8894 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8895 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8896 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8897 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8898 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8899 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8900 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8901 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8902 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8903 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8906 int action_arg_number_old =
8907 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8908 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8909 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8910 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8911 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8914 int action_arg_number_new =
8915 getModifiedActionNumber(action_arg_number_old,
8916 action_mode, action_arg_number,
8917 action_arg_number_min, action_arg_number_max);
8919 int trigger_player_bits =
8920 (change->actual_trigger_player >= EL_PLAYER_1 &&
8921 change->actual_trigger_player <= EL_PLAYER_4 ?
8922 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8925 int action_arg_player_bits =
8926 (action_arg >= CA_ARG_PLAYER_1 &&
8927 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8928 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8931 /* ---------- execute action -------------------------------------------- */
8933 switch (action_type)
8940 /* ---------- level actions ------------------------------------------- */
8942 case CA_RESTART_LEVEL:
8944 game.restart_level = TRUE;
8949 case CA_SHOW_ENVELOPE:
8951 int element = getSpecialActionElement(action_arg_element,
8952 action_arg_number, EL_ENVELOPE_1);
8954 if (IS_ENVELOPE(element))
8955 local_player->show_envelope = element;
8960 case CA_SET_LEVEL_TIME:
8962 if (level.time > 0) /* only modify limited time value */
8964 TimeLeft = action_arg_number_new;
8966 DrawGameValue_Time(TimeLeft);
8968 if (!TimeLeft && setup.time_limit)
8969 for (i = 0; i < MAX_PLAYERS; i++)
8970 KillPlayer(&stored_player[i]);
8976 case CA_SET_LEVEL_SCORE:
8978 local_player->score = action_arg_number_new;
8980 DrawGameValue_Score(local_player->score);
8985 case CA_SET_LEVEL_GEMS:
8987 local_player->gems_still_needed = action_arg_number_new;
8989 DrawGameValue_Emeralds(local_player->gems_still_needed);
8994 #if !USE_PLAYER_GRAVITY
8995 case CA_SET_LEVEL_GRAVITY:
8997 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8998 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8999 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9005 case CA_SET_LEVEL_WIND:
9007 game.wind_direction = action_arg_direction;
9012 /* ---------- player actions ------------------------------------------ */
9014 case CA_MOVE_PLAYER:
9016 /* automatically move to the next field in specified direction */
9017 for (i = 0; i < MAX_PLAYERS; i++)
9018 if (trigger_player_bits & (1 << i))
9019 stored_player[i].programmed_action = action_arg_direction;
9024 case CA_EXIT_PLAYER:
9026 for (i = 0; i < MAX_PLAYERS; i++)
9027 if (action_arg_player_bits & (1 << i))
9028 PlayerWins(&stored_player[i]);
9033 case CA_KILL_PLAYER:
9035 for (i = 0; i < MAX_PLAYERS; i++)
9036 if (action_arg_player_bits & (1 << i))
9037 KillPlayer(&stored_player[i]);
9042 case CA_SET_PLAYER_KEYS:
9044 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9045 int element = getSpecialActionElement(action_arg_element,
9046 action_arg_number, EL_KEY_1);
9048 if (IS_KEY(element))
9050 for (i = 0; i < MAX_PLAYERS; i++)
9052 if (trigger_player_bits & (1 << i))
9054 stored_player[i].key[KEY_NR(element)] = key_state;
9056 DrawGameDoorValues();
9064 case CA_SET_PLAYER_SPEED:
9066 for (i = 0; i < MAX_PLAYERS; i++)
9068 if (trigger_player_bits & (1 << i))
9070 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9072 if (action_arg == CA_ARG_SPEED_FASTER &&
9073 stored_player[i].cannot_move)
9075 action_arg_number = STEPSIZE_VERY_SLOW;
9077 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9078 action_arg == CA_ARG_SPEED_FASTER)
9080 action_arg_number = 2;
9081 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9084 else if (action_arg == CA_ARG_NUMBER_RESET)
9086 action_arg_number = level.initial_player_stepsize[i];
9090 getModifiedActionNumber(move_stepsize,
9093 action_arg_number_min,
9094 action_arg_number_max);
9096 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9103 case CA_SET_PLAYER_SHIELD:
9105 for (i = 0; i < MAX_PLAYERS; i++)
9107 if (trigger_player_bits & (1 << i))
9109 if (action_arg == CA_ARG_SHIELD_OFF)
9111 stored_player[i].shield_normal_time_left = 0;
9112 stored_player[i].shield_deadly_time_left = 0;
9114 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9116 stored_player[i].shield_normal_time_left = 999999;
9118 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9120 stored_player[i].shield_normal_time_left = 999999;
9121 stored_player[i].shield_deadly_time_left = 999999;
9129 #if USE_PLAYER_GRAVITY
9130 case CA_SET_PLAYER_GRAVITY:
9132 for (i = 0; i < MAX_PLAYERS; i++)
9134 if (trigger_player_bits & (1 << i))
9136 stored_player[i].gravity =
9137 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9138 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9139 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9140 stored_player[i].gravity);
9148 case CA_SET_PLAYER_ARTWORK:
9150 for (i = 0; i < MAX_PLAYERS; i++)
9152 if (trigger_player_bits & (1 << i))
9154 int artwork_element = action_arg_element;
9156 if (action_arg == CA_ARG_ELEMENT_RESET)
9158 (level.use_artwork_element[i] ? level.artwork_element[i] :
9159 stored_player[i].element_nr);
9161 #if USE_GFX_RESET_PLAYER_ARTWORK
9162 if (stored_player[i].artwork_element != artwork_element)
9163 stored_player[i].Frame = 0;
9166 stored_player[i].artwork_element = artwork_element;
9168 SetPlayerWaiting(&stored_player[i], FALSE);
9170 /* set number of special actions for bored and sleeping animation */
9171 stored_player[i].num_special_action_bored =
9172 get_num_special_action(artwork_element,
9173 ACTION_BORING_1, ACTION_BORING_LAST);
9174 stored_player[i].num_special_action_sleeping =
9175 get_num_special_action(artwork_element,
9176 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9183 /* ---------- CE actions ---------------------------------------------- */
9185 case CA_SET_CE_VALUE:
9187 #if USE_NEW_CUSTOM_VALUE
9188 int last_ce_value = CustomValue[x][y];
9190 CustomValue[x][y] = action_arg_number_new;
9192 if (CustomValue[x][y] != last_ce_value)
9194 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9195 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9197 if (CustomValue[x][y] == 0)
9199 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9200 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9208 case CA_SET_CE_SCORE:
9210 #if USE_NEW_CUSTOM_VALUE
9211 int last_ce_score = ei->collect_score;
9213 ei->collect_score = action_arg_number_new;
9215 if (ei->collect_score != last_ce_score)
9217 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9218 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9220 if (ei->collect_score == 0)
9224 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9225 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9228 This is a very special case that seems to be a mixture between
9229 CheckElementChange() and CheckTriggeredElementChange(): while
9230 the first one only affects single elements that are triggered
9231 directly, the second one affects multiple elements in the playfield
9232 that are triggered indirectly by another element. This is a third
9233 case: Changing the CE score always affects multiple identical CEs,
9234 so every affected CE must be checked, not only the single CE for
9235 which the CE score was changed in the first place (as every instance
9236 of that CE shares the same CE score, and therefore also can change)!
9238 SCAN_PLAYFIELD(xx, yy)
9240 if (Feld[xx][yy] == element)
9241 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9242 CE_SCORE_GETS_ZERO);
9251 /* ---------- engine actions ------------------------------------------ */
9253 case CA_SET_ENGINE_SCAN_MODE:
9255 InitPlayfieldScanMode(action_arg);
9265 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9267 int old_element = Feld[x][y];
9268 int new_element = GetElementFromGroupElement(element);
9269 int previous_move_direction = MovDir[x][y];
9270 #if USE_NEW_CUSTOM_VALUE
9271 int last_ce_value = CustomValue[x][y];
9273 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9274 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9275 boolean add_player_onto_element = (new_element_is_player &&
9276 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9277 /* this breaks SnakeBite when a snake is
9278 halfway through a door that closes */
9279 /* NOW FIXED AT LEVEL INIT IN files.c */
9280 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9282 IS_WALKABLE(old_element));
9285 /* check if element under the player changes from accessible to unaccessible
9286 (needed for special case of dropping element which then changes) */
9287 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9288 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9296 if (!add_player_onto_element)
9298 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9299 RemoveMovingField(x, y);
9303 Feld[x][y] = new_element;
9305 #if !USE_GFX_RESET_GFX_ANIMATION
9306 ResetGfxAnimation(x, y);
9307 ResetRandomAnimationValue(x, y);
9310 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9311 MovDir[x][y] = previous_move_direction;
9313 #if USE_NEW_CUSTOM_VALUE
9314 if (element_info[new_element].use_last_ce_value)
9315 CustomValue[x][y] = last_ce_value;
9318 InitField_WithBug1(x, y, FALSE);
9320 new_element = Feld[x][y]; /* element may have changed */
9322 #if USE_GFX_RESET_GFX_ANIMATION
9323 ResetGfxAnimation(x, y);
9324 ResetRandomAnimationValue(x, y);
9327 DrawLevelField(x, y);
9329 if (GFX_CRUMBLED(new_element))
9330 DrawLevelFieldCrumbledSandNeighbours(x, y);
9334 /* check if element under the player changes from accessible to unaccessible
9335 (needed for special case of dropping element which then changes) */
9336 /* (must be checked after creating new element for walkable group elements) */
9337 #if USE_FIX_KILLED_BY_NON_WALKABLE
9338 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9339 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9346 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9347 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9356 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9357 if (new_element_is_player)
9358 RelocatePlayer(x, y, new_element);
9361 ChangeCount[x][y]++; /* count number of changes in the same frame */
9363 TestIfBadThingTouchesPlayer(x, y);
9364 TestIfPlayerTouchesCustomElement(x, y);
9365 TestIfElementTouchesCustomElement(x, y);
9368 static void CreateField(int x, int y, int element)
9370 CreateFieldExt(x, y, element, FALSE);
9373 static void CreateElementFromChange(int x, int y, int element)
9375 element = GET_VALID_RUNTIME_ELEMENT(element);
9377 #if USE_STOP_CHANGED_ELEMENTS
9378 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9380 int old_element = Feld[x][y];
9382 /* prevent changed element from moving in same engine frame
9383 unless both old and new element can either fall or move */
9384 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9385 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9390 CreateFieldExt(x, y, element, TRUE);
9393 static boolean ChangeElement(int x, int y, int element, int page)
9395 struct ElementInfo *ei = &element_info[element];
9396 struct ElementChangeInfo *change = &ei->change_page[page];
9397 int ce_value = CustomValue[x][y];
9398 int ce_score = ei->collect_score;
9400 int old_element = Feld[x][y];
9402 /* always use default change event to prevent running into a loop */
9403 if (ChangeEvent[x][y] == -1)
9404 ChangeEvent[x][y] = CE_DELAY;
9406 if (ChangeEvent[x][y] == CE_DELAY)
9408 /* reset actual trigger element, trigger player and action element */
9409 change->actual_trigger_element = EL_EMPTY;
9410 change->actual_trigger_player = EL_PLAYER_1;
9411 change->actual_trigger_side = CH_SIDE_NONE;
9412 change->actual_trigger_ce_value = 0;
9413 change->actual_trigger_ce_score = 0;
9416 /* do not change elements more than a specified maximum number of changes */
9417 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9420 ChangeCount[x][y]++; /* count number of changes in the same frame */
9422 if (change->explode)
9429 if (change->use_target_content)
9431 boolean complete_replace = TRUE;
9432 boolean can_replace[3][3];
9435 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9438 boolean is_walkable;
9439 boolean is_diggable;
9440 boolean is_collectible;
9441 boolean is_removable;
9442 boolean is_destructible;
9443 int ex = x + xx - 1;
9444 int ey = y + yy - 1;
9445 int content_element = change->target_content.e[xx][yy];
9448 can_replace[xx][yy] = TRUE;
9450 if (ex == x && ey == y) /* do not check changing element itself */
9453 if (content_element == EL_EMPTY_SPACE)
9455 can_replace[xx][yy] = FALSE; /* do not replace border with space */
9460 if (!IN_LEV_FIELD(ex, ey))
9462 can_replace[xx][yy] = FALSE;
9463 complete_replace = FALSE;
9470 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9471 e = MovingOrBlocked2Element(ex, ey);
9473 is_empty = (IS_FREE(ex, ey) ||
9474 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9476 is_walkable = (is_empty || IS_WALKABLE(e));
9477 is_diggable = (is_empty || IS_DIGGABLE(e));
9478 is_collectible = (is_empty || IS_COLLECTIBLE(e));
9479 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9480 is_removable = (is_diggable || is_collectible);
9482 can_replace[xx][yy] =
9483 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9484 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9485 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9486 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9487 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9488 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9489 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9491 if (!can_replace[xx][yy])
9492 complete_replace = FALSE;
9495 if (!change->only_if_complete || complete_replace)
9497 boolean something_has_changed = FALSE;
9499 if (change->only_if_complete && change->use_random_replace &&
9500 RND(100) < change->random_percentage)
9503 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9505 int ex = x + xx - 1;
9506 int ey = y + yy - 1;
9507 int content_element;
9509 if (can_replace[xx][yy] && (!change->use_random_replace ||
9510 RND(100) < change->random_percentage))
9512 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9513 RemoveMovingField(ex, ey);
9515 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9517 content_element = change->target_content.e[xx][yy];
9518 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9519 ce_value, ce_score);
9521 CreateElementFromChange(ex, ey, target_element);
9523 something_has_changed = TRUE;
9525 /* for symmetry reasons, freeze newly created border elements */
9526 if (ex != x || ey != y)
9527 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9531 if (something_has_changed)
9533 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9534 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9540 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9541 ce_value, ce_score);
9543 if (element == EL_DIAGONAL_GROWING ||
9544 element == EL_DIAGONAL_SHRINKING)
9546 target_element = Store[x][y];
9548 Store[x][y] = EL_EMPTY;
9551 CreateElementFromChange(x, y, target_element);
9553 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9554 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9557 /* this uses direct change before indirect change */
9558 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9563 #if USE_NEW_DELAYED_ACTION
9565 static void HandleElementChange(int x, int y, int page)
9567 int element = MovingOrBlocked2Element(x, y);
9568 struct ElementInfo *ei = &element_info[element];
9569 struct ElementChangeInfo *change = &ei->change_page[page];
9572 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9573 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9576 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9577 x, y, element, element_info[element].token_name);
9578 printf("HandleElementChange(): This should never happen!\n");
9583 /* this can happen with classic bombs on walkable, changing elements */
9584 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9587 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9588 ChangeDelay[x][y] = 0;
9594 if (ChangeDelay[x][y] == 0) /* initialize element change */
9596 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9598 if (change->can_change)
9601 /* !!! not clear why graphic animation should be reset at all here !!! */
9602 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9603 #if USE_GFX_RESET_WHEN_NOT_MOVING
9604 /* when a custom element is about to change (for example by change delay),
9605 do not reset graphic animation when the custom element is moving */
9606 if (!IS_MOVING(x, y))
9609 ResetGfxAnimation(x, y);
9610 ResetRandomAnimationValue(x, y);
9614 if (change->pre_change_function)
9615 change->pre_change_function(x, y);
9619 ChangeDelay[x][y]--;
9621 if (ChangeDelay[x][y] != 0) /* continue element change */
9623 if (change->can_change)
9625 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9627 if (IS_ANIMATED(graphic))
9628 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9630 if (change->change_function)
9631 change->change_function(x, y);
9634 else /* finish element change */
9636 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9638 page = ChangePage[x][y];
9639 ChangePage[x][y] = -1;
9641 change = &ei->change_page[page];
9644 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9646 ChangeDelay[x][y] = 1; /* try change after next move step */
9647 ChangePage[x][y] = page; /* remember page to use for change */
9652 if (change->can_change)
9654 if (ChangeElement(x, y, element, page))
9656 if (change->post_change_function)
9657 change->post_change_function(x, y);
9661 if (change->has_action)
9662 ExecuteCustomElementAction(x, y, element, page);
9668 static void HandleElementChange(int x, int y, int page)
9670 int element = MovingOrBlocked2Element(x, y);
9671 struct ElementInfo *ei = &element_info[element];
9672 struct ElementChangeInfo *change = &ei->change_page[page];
9675 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9678 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9679 x, y, element, element_info[element].token_name);
9680 printf("HandleElementChange(): This should never happen!\n");
9685 /* this can happen with classic bombs on walkable, changing elements */
9686 if (!CAN_CHANGE(element))
9689 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9690 ChangeDelay[x][y] = 0;
9696 if (ChangeDelay[x][y] == 0) /* initialize element change */
9698 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9700 ResetGfxAnimation(x, y);
9701 ResetRandomAnimationValue(x, y);
9703 if (change->pre_change_function)
9704 change->pre_change_function(x, y);
9707 ChangeDelay[x][y]--;
9709 if (ChangeDelay[x][y] != 0) /* continue element change */
9711 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9713 if (IS_ANIMATED(graphic))
9714 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9716 if (change->change_function)
9717 change->change_function(x, y);
9719 else /* finish element change */
9721 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9723 page = ChangePage[x][y];
9724 ChangePage[x][y] = -1;
9726 change = &ei->change_page[page];
9729 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9731 ChangeDelay[x][y] = 1; /* try change after next move step */
9732 ChangePage[x][y] = page; /* remember page to use for change */
9737 if (ChangeElement(x, y, element, page))
9739 if (change->post_change_function)
9740 change->post_change_function(x, y);
9747 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9748 int trigger_element,
9754 boolean change_done_any = FALSE;
9755 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9758 if (!(trigger_events[trigger_element][trigger_event]))
9762 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9763 trigger_event, recursion_loop_depth, recursion_loop_detected,
9764 recursion_loop_element, EL_NAME(recursion_loop_element));
9767 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9769 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9771 int element = EL_CUSTOM_START + i;
9772 boolean change_done = FALSE;
9775 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9776 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9779 for (p = 0; p < element_info[element].num_change_pages; p++)
9781 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9783 if (change->can_change_or_has_action &&
9784 change->has_event[trigger_event] &&
9785 change->trigger_side & trigger_side &&
9786 change->trigger_player & trigger_player &&
9787 change->trigger_page & trigger_page_bits &&
9788 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9790 change->actual_trigger_element = trigger_element;
9791 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9792 change->actual_trigger_side = trigger_side;
9793 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9794 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9796 if ((change->can_change && !change_done) || change->has_action)
9800 SCAN_PLAYFIELD(x, y)
9802 if (Feld[x][y] == element)
9804 if (change->can_change && !change_done)
9806 ChangeDelay[x][y] = 1;
9807 ChangeEvent[x][y] = trigger_event;
9809 HandleElementChange(x, y, p);
9811 #if USE_NEW_DELAYED_ACTION
9812 else if (change->has_action)
9814 ExecuteCustomElementAction(x, y, element, p);
9815 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9818 if (change->has_action)
9820 ExecuteCustomElementAction(x, y, element, p);
9821 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9827 if (change->can_change)
9830 change_done_any = TRUE;
9837 RECURSION_LOOP_DETECTION_END();
9839 return change_done_any;
9842 static boolean CheckElementChangeExt(int x, int y,
9844 int trigger_element,
9849 boolean change_done = FALSE;
9852 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9853 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9856 if (Feld[x][y] == EL_BLOCKED)
9858 Blocked2Moving(x, y, &x, &y);
9859 element = Feld[x][y];
9863 /* check if element has already changed */
9864 if (Feld[x][y] != element)
9867 /* check if element has already changed or is about to change after moving */
9868 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9869 Feld[x][y] != element) ||
9871 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9872 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9873 ChangePage[x][y] != -1)))
9878 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9879 trigger_event, recursion_loop_depth, recursion_loop_detected,
9880 recursion_loop_element, EL_NAME(recursion_loop_element));
9883 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9885 for (p = 0; p < element_info[element].num_change_pages; p++)
9887 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9889 /* check trigger element for all events where the element that is checked
9890 for changing interacts with a directly adjacent element -- this is
9891 different to element changes that affect other elements to change on the
9892 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9893 boolean check_trigger_element =
9894 (trigger_event == CE_TOUCHING_X ||
9895 trigger_event == CE_HITTING_X ||
9896 trigger_event == CE_HIT_BY_X ||
9898 /* this one was forgotten until 3.2.3 */
9899 trigger_event == CE_DIGGING_X);
9902 if (change->can_change_or_has_action &&
9903 change->has_event[trigger_event] &&
9904 change->trigger_side & trigger_side &&
9905 change->trigger_player & trigger_player &&
9906 (!check_trigger_element ||
9907 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9909 change->actual_trigger_element = trigger_element;
9910 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9911 change->actual_trigger_side = trigger_side;
9912 change->actual_trigger_ce_value = CustomValue[x][y];
9913 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9915 /* special case: trigger element not at (x,y) position for some events */
9916 if (check_trigger_element)
9928 { 0, 0 }, { 0, 0 }, { 0, 0 },
9932 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9933 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9935 change->actual_trigger_ce_value = CustomValue[xx][yy];
9936 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9939 if (change->can_change && !change_done)
9941 ChangeDelay[x][y] = 1;
9942 ChangeEvent[x][y] = trigger_event;
9944 HandleElementChange(x, y, p);
9948 #if USE_NEW_DELAYED_ACTION
9949 else if (change->has_action)
9951 ExecuteCustomElementAction(x, y, element, p);
9952 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9955 if (change->has_action)
9957 ExecuteCustomElementAction(x, y, element, p);
9958 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9964 RECURSION_LOOP_DETECTION_END();
9969 static void PlayPlayerSound(struct PlayerInfo *player)
9971 int jx = player->jx, jy = player->jy;
9972 int sound_element = player->artwork_element;
9973 int last_action = player->last_action_waiting;
9974 int action = player->action_waiting;
9976 if (player->is_waiting)
9978 if (action != last_action)
9979 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9981 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9985 if (action != last_action)
9986 StopSound(element_info[sound_element].sound[last_action]);
9988 if (last_action == ACTION_SLEEPING)
9989 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9993 static void PlayAllPlayersSound()
9997 for (i = 0; i < MAX_PLAYERS; i++)
9998 if (stored_player[i].active)
9999 PlayPlayerSound(&stored_player[i]);
10002 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10004 boolean last_waiting = player->is_waiting;
10005 int move_dir = player->MovDir;
10007 player->dir_waiting = move_dir;
10008 player->last_action_waiting = player->action_waiting;
10012 if (!last_waiting) /* not waiting -> waiting */
10014 player->is_waiting = TRUE;
10016 player->frame_counter_bored =
10018 game.player_boring_delay_fixed +
10019 GetSimpleRandom(game.player_boring_delay_random);
10020 player->frame_counter_sleeping =
10022 game.player_sleeping_delay_fixed +
10023 GetSimpleRandom(game.player_sleeping_delay_random);
10025 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10028 if (game.player_sleeping_delay_fixed +
10029 game.player_sleeping_delay_random > 0 &&
10030 player->anim_delay_counter == 0 &&
10031 player->post_delay_counter == 0 &&
10032 FrameCounter >= player->frame_counter_sleeping)
10033 player->is_sleeping = TRUE;
10034 else if (game.player_boring_delay_fixed +
10035 game.player_boring_delay_random > 0 &&
10036 FrameCounter >= player->frame_counter_bored)
10037 player->is_bored = TRUE;
10039 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10040 player->is_bored ? ACTION_BORING :
10043 if (player->is_sleeping && player->use_murphy)
10045 /* special case for sleeping Murphy when leaning against non-free tile */
10047 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10048 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10049 !IS_MOVING(player->jx - 1, player->jy)))
10050 move_dir = MV_LEFT;
10051 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10052 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10053 !IS_MOVING(player->jx + 1, player->jy)))
10054 move_dir = MV_RIGHT;
10056 player->is_sleeping = FALSE;
10058 player->dir_waiting = move_dir;
10061 if (player->is_sleeping)
10063 if (player->num_special_action_sleeping > 0)
10065 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10067 int last_special_action = player->special_action_sleeping;
10068 int num_special_action = player->num_special_action_sleeping;
10069 int special_action =
10070 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10071 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10072 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10073 last_special_action + 1 : ACTION_SLEEPING);
10074 int special_graphic =
10075 el_act_dir2img(player->artwork_element, special_action, move_dir);
10077 player->anim_delay_counter =
10078 graphic_info[special_graphic].anim_delay_fixed +
10079 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10080 player->post_delay_counter =
10081 graphic_info[special_graphic].post_delay_fixed +
10082 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10084 player->special_action_sleeping = special_action;
10087 if (player->anim_delay_counter > 0)
10089 player->action_waiting = player->special_action_sleeping;
10090 player->anim_delay_counter--;
10092 else if (player->post_delay_counter > 0)
10094 player->post_delay_counter--;
10098 else if (player->is_bored)
10100 if (player->num_special_action_bored > 0)
10102 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10104 int special_action =
10105 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10106 int special_graphic =
10107 el_act_dir2img(player->artwork_element, special_action, move_dir);
10109 player->anim_delay_counter =
10110 graphic_info[special_graphic].anim_delay_fixed +
10111 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10112 player->post_delay_counter =
10113 graphic_info[special_graphic].post_delay_fixed +
10114 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10116 player->special_action_bored = special_action;
10119 if (player->anim_delay_counter > 0)
10121 player->action_waiting = player->special_action_bored;
10122 player->anim_delay_counter--;
10124 else if (player->post_delay_counter > 0)
10126 player->post_delay_counter--;
10131 else if (last_waiting) /* waiting -> not waiting */
10133 player->is_waiting = FALSE;
10134 player->is_bored = FALSE;
10135 player->is_sleeping = FALSE;
10137 player->frame_counter_bored = -1;
10138 player->frame_counter_sleeping = -1;
10140 player->anim_delay_counter = 0;
10141 player->post_delay_counter = 0;
10143 player->dir_waiting = player->MovDir;
10144 player->action_waiting = ACTION_DEFAULT;
10146 player->special_action_bored = ACTION_DEFAULT;
10147 player->special_action_sleeping = ACTION_DEFAULT;
10151 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10153 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10154 int left = player_action & JOY_LEFT;
10155 int right = player_action & JOY_RIGHT;
10156 int up = player_action & JOY_UP;
10157 int down = player_action & JOY_DOWN;
10158 int button1 = player_action & JOY_BUTTON_1;
10159 int button2 = player_action & JOY_BUTTON_2;
10160 int dx = (left ? -1 : right ? 1 : 0);
10161 int dy = (up ? -1 : down ? 1 : 0);
10163 if (!player->active || tape.pausing)
10169 snapped = SnapField(player, dx, dy);
10173 dropped = DropElement(player);
10175 moved = MovePlayer(player, dx, dy);
10178 if (tape.single_step && tape.recording && !tape.pausing)
10180 if (button1 || (dropped && !moved))
10182 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10183 SnapField(player, 0, 0); /* stop snapping */
10187 SetPlayerWaiting(player, FALSE);
10189 return player_action;
10193 /* no actions for this player (no input at player's configured device) */
10195 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10196 SnapField(player, 0, 0);
10197 CheckGravityMovementWhenNotMoving(player);
10199 if (player->MovPos == 0)
10200 SetPlayerWaiting(player, TRUE);
10202 if (player->MovPos == 0) /* needed for tape.playing */
10203 player->is_moving = FALSE;
10205 player->is_dropping = FALSE;
10206 player->is_dropping_pressed = FALSE;
10207 player->drop_pressed_delay = 0;
10213 static void CheckLevelTime()
10217 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10219 if (level.native_em_level->lev->home == 0) /* all players at home */
10221 PlayerWins(local_player);
10223 AllPlayersGone = TRUE;
10225 level.native_em_level->lev->home = -1;
10228 if (level.native_em_level->ply[0]->alive == 0 &&
10229 level.native_em_level->ply[1]->alive == 0 &&
10230 level.native_em_level->ply[2]->alive == 0 &&
10231 level.native_em_level->ply[3]->alive == 0) /* all dead */
10232 AllPlayersGone = TRUE;
10235 if (TimeFrames >= FRAMES_PER_SECOND)
10240 for (i = 0; i < MAX_PLAYERS; i++)
10242 struct PlayerInfo *player = &stored_player[i];
10244 if (SHIELD_ON(player))
10246 player->shield_normal_time_left--;
10248 if (player->shield_deadly_time_left > 0)
10249 player->shield_deadly_time_left--;
10253 if (!local_player->LevelSolved && !level.use_step_counter)
10261 if (TimeLeft <= 10 && setup.time_limit)
10262 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10264 DrawGameValue_Time(TimeLeft);
10266 if (!TimeLeft && setup.time_limit)
10268 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10269 level.native_em_level->lev->killed_out_of_time = TRUE;
10271 for (i = 0; i < MAX_PLAYERS; i++)
10272 KillPlayer(&stored_player[i]);
10275 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10276 DrawGameValue_Time(TimePlayed);
10278 level.native_em_level->lev->time =
10279 (level.time == 0 ? TimePlayed : TimeLeft);
10282 if (tape.recording || tape.playing)
10283 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10287 void AdvanceFrameAndPlayerCounters(int player_nr)
10291 /* advance frame counters (global frame counter and time frame counter) */
10295 /* advance player counters (counters for move delay, move animation etc.) */
10296 for (i = 0; i < MAX_PLAYERS; i++)
10298 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10299 int move_delay_value = stored_player[i].move_delay_value;
10300 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10302 if (!advance_player_counters) /* not all players may be affected */
10305 #if USE_NEW_PLAYER_ANIM
10306 if (move_frames == 0) /* less than one move per game frame */
10308 int stepsize = TILEX / move_delay_value;
10309 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10310 int count = (stored_player[i].is_moving ?
10311 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10313 if (count % delay == 0)
10318 stored_player[i].Frame += move_frames;
10320 if (stored_player[i].MovPos != 0)
10321 stored_player[i].StepFrame += move_frames;
10323 if (stored_player[i].move_delay > 0)
10324 stored_player[i].move_delay--;
10326 /* due to bugs in previous versions, counter must count up, not down */
10327 if (stored_player[i].push_delay != -1)
10328 stored_player[i].push_delay++;
10330 if (stored_player[i].drop_delay > 0)
10331 stored_player[i].drop_delay--;
10333 if (stored_player[i].is_dropping_pressed)
10334 stored_player[i].drop_pressed_delay++;
10338 void StartGameActions(boolean init_network_game, boolean record_tape,
10341 unsigned long new_random_seed = InitRND(random_seed);
10344 TapeStartRecording(new_random_seed);
10346 #if defined(NETWORK_AVALIABLE)
10347 if (init_network_game)
10349 SendToServer_StartPlaying();
10360 static unsigned long game_frame_delay = 0;
10361 unsigned long game_frame_delay_value;
10362 byte *recorded_player_action;
10363 byte summarized_player_action = 0;
10364 byte tape_action[MAX_PLAYERS];
10367 /* detect endless loops, caused by custom element programming */
10368 if (recursion_loop_detected && recursion_loop_depth == 0)
10370 char *message = getStringCat3("Internal Error ! Element ",
10371 EL_NAME(recursion_loop_element),
10372 " caused endless loop ! Quit the game ?");
10374 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10375 EL_NAME(recursion_loop_element));
10377 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10379 recursion_loop_detected = FALSE; /* if game should be continued */
10386 if (game.restart_level)
10387 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10391 if (level.native_em_level->lev->home == 0) /* all players at home */
10393 PlayerWins(local_player);
10395 AllPlayersGone = TRUE;
10397 level.native_em_level->lev->home = -1;
10400 if (level.native_em_level->ply[0]->alive == 0 &&
10401 level.native_em_level->ply[1]->alive == 0 &&
10402 level.native_em_level->ply[2]->alive == 0 &&
10403 level.native_em_level->ply[3]->alive == 0) /* all dead */
10404 AllPlayersGone = TRUE;
10407 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10410 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10413 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10416 game_frame_delay_value =
10417 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10419 if (tape.playing && tape.warp_forward && !tape.pausing)
10420 game_frame_delay_value = 0;
10422 /* ---------- main game synchronization point ---------- */
10424 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10426 if (network_playing && !network_player_action_received)
10428 /* try to get network player actions in time */
10430 #if defined(NETWORK_AVALIABLE)
10431 /* last chance to get network player actions without main loop delay */
10432 HandleNetworking();
10435 /* game was quit by network peer */
10436 if (game_status != GAME_MODE_PLAYING)
10439 if (!network_player_action_received)
10440 return; /* failed to get network player actions in time */
10442 /* do not yet reset "network_player_action_received" (for tape.pausing) */
10448 /* at this point we know that we really continue executing the game */
10450 network_player_action_received = FALSE;
10452 /* when playing tape, read previously recorded player input from tape data */
10453 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10456 /* TapePlayAction() may return NULL when toggling to "pause before death" */
10461 if (tape.set_centered_player)
10463 game.centered_player_nr_next = tape.centered_player_nr_next;
10464 game.set_centered_player = TRUE;
10467 for (i = 0; i < MAX_PLAYERS; i++)
10469 summarized_player_action |= stored_player[i].action;
10471 if (!network_playing)
10472 stored_player[i].effective_action = stored_player[i].action;
10475 #if defined(NETWORK_AVALIABLE)
10476 if (network_playing)
10477 SendToServer_MovePlayer(summarized_player_action);
10480 if (!options.network && !setup.team_mode)
10481 local_player->effective_action = summarized_player_action;
10483 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10485 for (i = 0; i < MAX_PLAYERS; i++)
10486 stored_player[i].effective_action =
10487 (i == game.centered_player_nr ? summarized_player_action : 0);
10490 if (recorded_player_action != NULL)
10491 for (i = 0; i < MAX_PLAYERS; i++)
10492 stored_player[i].effective_action = recorded_player_action[i];
10494 for (i = 0; i < MAX_PLAYERS; i++)
10496 tape_action[i] = stored_player[i].effective_action;
10498 /* (this can only happen in the R'n'D game engine) */
10499 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10500 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10503 /* only record actions from input devices, but not programmed actions */
10504 if (tape.recording)
10505 TapeRecordAction(tape_action);
10507 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10509 GameActions_EM_Main();
10517 void GameActions_EM_Main()
10519 byte effective_action[MAX_PLAYERS];
10520 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10523 for (i = 0; i < MAX_PLAYERS; i++)
10524 effective_action[i] = stored_player[i].effective_action;
10526 GameActions_EM(effective_action, warp_mode);
10530 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10533 void GameActions_RND()
10535 int magic_wall_x = 0, magic_wall_y = 0;
10536 int i, x, y, element, graphic;
10538 InitPlayfieldScanModeVars();
10540 #if USE_ONE_MORE_CHANGE_PER_FRAME
10541 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10543 SCAN_PLAYFIELD(x, y)
10545 ChangeCount[x][y] = 0;
10546 ChangeEvent[x][y] = -1;
10551 if (game.set_centered_player)
10553 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10555 /* switching to "all players" only possible if all players fit to screen */
10556 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10558 game.centered_player_nr_next = game.centered_player_nr;
10559 game.set_centered_player = FALSE;
10562 /* do not switch focus to non-existing (or non-active) player */
10563 if (game.centered_player_nr_next >= 0 &&
10564 !stored_player[game.centered_player_nr_next].active)
10566 game.centered_player_nr_next = game.centered_player_nr;
10567 game.set_centered_player = FALSE;
10571 if (game.set_centered_player &&
10572 ScreenMovPos == 0) /* screen currently aligned at tile position */
10576 if (game.centered_player_nr_next == -1)
10578 setScreenCenteredToAllPlayers(&sx, &sy);
10582 sx = stored_player[game.centered_player_nr_next].jx;
10583 sy = stored_player[game.centered_player_nr_next].jy;
10586 game.centered_player_nr = game.centered_player_nr_next;
10587 game.set_centered_player = FALSE;
10589 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10590 DrawGameDoorValues();
10593 for (i = 0; i < MAX_PLAYERS; i++)
10595 int actual_player_action = stored_player[i].effective_action;
10598 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10599 - rnd_equinox_tetrachloride 048
10600 - rnd_equinox_tetrachloride_ii 096
10601 - rnd_emanuel_schmieg 002
10602 - doctor_sloan_ww 001, 020
10604 if (stored_player[i].MovPos == 0)
10605 CheckGravityMovement(&stored_player[i]);
10608 /* overwrite programmed action with tape action */
10609 if (stored_player[i].programmed_action)
10610 actual_player_action = stored_player[i].programmed_action;
10612 PlayerActions(&stored_player[i], actual_player_action);
10614 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10617 ScrollScreen(NULL, SCROLL_GO_ON);
10619 /* for backwards compatibility, the following code emulates a fixed bug that
10620 occured when pushing elements (causing elements that just made their last
10621 pushing step to already (if possible) make their first falling step in the
10622 same game frame, which is bad); this code is also needed to use the famous
10623 "spring push bug" which is used in older levels and might be wanted to be
10624 used also in newer levels, but in this case the buggy pushing code is only
10625 affecting the "spring" element and no other elements */
10627 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10629 for (i = 0; i < MAX_PLAYERS; i++)
10631 struct PlayerInfo *player = &stored_player[i];
10632 int x = player->jx;
10633 int y = player->jy;
10635 if (player->active && player->is_pushing && player->is_moving &&
10637 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10638 Feld[x][y] == EL_SPRING))
10640 ContinueMoving(x, y);
10642 /* continue moving after pushing (this is actually a bug) */
10643 if (!IS_MOVING(x, y))
10644 Stop[x][y] = FALSE;
10650 debug_print_timestamp(0, "start main loop profiling");
10653 SCAN_PLAYFIELD(x, y)
10655 ChangeCount[x][y] = 0;
10656 ChangeEvent[x][y] = -1;
10658 /* this must be handled before main playfield loop */
10659 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10662 if (MovDelay[x][y] <= 0)
10666 #if USE_NEW_SNAP_DELAY
10667 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10670 if (MovDelay[x][y] <= 0)
10673 DrawLevelField(x, y);
10675 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10681 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10683 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10684 printf("GameActions(): This should never happen!\n");
10686 ChangePage[x][y] = -1;
10690 Stop[x][y] = FALSE;
10691 if (WasJustMoving[x][y] > 0)
10692 WasJustMoving[x][y]--;
10693 if (WasJustFalling[x][y] > 0)
10694 WasJustFalling[x][y]--;
10695 if (CheckCollision[x][y] > 0)
10696 CheckCollision[x][y]--;
10697 if (CheckImpact[x][y] > 0)
10698 CheckImpact[x][y]--;
10702 /* reset finished pushing action (not done in ContinueMoving() to allow
10703 continuous pushing animation for elements with zero push delay) */
10704 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10706 ResetGfxAnimation(x, y);
10707 DrawLevelField(x, y);
10711 if (IS_BLOCKED(x, y))
10715 Blocked2Moving(x, y, &oldx, &oldy);
10716 if (!IS_MOVING(oldx, oldy))
10718 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10719 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10720 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10721 printf("GameActions(): This should never happen!\n");
10728 debug_print_timestamp(0, "- time for pre-main loop:");
10731 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
10732 SCAN_PLAYFIELD(x, y)
10734 element = Feld[x][y];
10735 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10740 int element2 = element;
10741 int graphic2 = graphic;
10743 int element2 = Feld[x][y];
10744 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10746 int last_gfx_frame = GfxFrame[x][y];
10748 if (graphic_info[graphic2].anim_global_sync)
10749 GfxFrame[x][y] = FrameCounter;
10750 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10751 GfxFrame[x][y] = CustomValue[x][y];
10752 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10753 GfxFrame[x][y] = element_info[element2].collect_score;
10754 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10755 GfxFrame[x][y] = ChangeDelay[x][y];
10757 if (redraw && GfxFrame[x][y] != last_gfx_frame)
10758 DrawLevelGraphicAnimation(x, y, graphic2);
10761 ResetGfxFrame(x, y, TRUE);
10765 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10766 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10767 ResetRandomAnimationValue(x, y);
10771 SetRandomAnimationValue(x, y);
10775 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10778 #endif // -------------------- !!! TEST ONLY !!! --------------------
10781 debug_print_timestamp(0, "- time for TEST loop: -->");
10784 SCAN_PLAYFIELD(x, y)
10786 element = Feld[x][y];
10787 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10789 ResetGfxFrame(x, y, TRUE);
10791 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10792 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10793 ResetRandomAnimationValue(x, y);
10795 SetRandomAnimationValue(x, y);
10797 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10799 if (IS_INACTIVE(element))
10801 if (IS_ANIMATED(graphic))
10802 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10807 /* this may take place after moving, so 'element' may have changed */
10808 if (IS_CHANGING(x, y) &&
10809 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10811 int page = element_info[element].event_page_nr[CE_DELAY];
10814 HandleElementChange(x, y, page);
10816 if (CAN_CHANGE(element))
10817 HandleElementChange(x, y, page);
10819 if (HAS_ACTION(element))
10820 ExecuteCustomElementAction(x, y, element, page);
10823 element = Feld[x][y];
10824 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10827 #if 0 // ---------------------------------------------------------------------
10829 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10833 element = Feld[x][y];
10834 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10836 if (IS_ANIMATED(graphic) &&
10837 !IS_MOVING(x, y) &&
10839 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10841 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10842 DrawTwinkleOnField(x, y);
10844 else if (IS_MOVING(x, y))
10845 ContinueMoving(x, y);
10852 case EL_EM_EXIT_OPEN:
10853 case EL_SP_EXIT_OPEN:
10854 case EL_STEEL_EXIT_OPEN:
10855 case EL_EM_STEEL_EXIT_OPEN:
10856 case EL_SP_TERMINAL:
10857 case EL_SP_TERMINAL_ACTIVE:
10858 case EL_EXTRA_TIME:
10859 case EL_SHIELD_NORMAL:
10860 case EL_SHIELD_DEADLY:
10861 if (IS_ANIMATED(graphic))
10862 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10865 case EL_DYNAMITE_ACTIVE:
10866 case EL_EM_DYNAMITE_ACTIVE:
10867 case EL_DYNABOMB_PLAYER_1_ACTIVE:
10868 case EL_DYNABOMB_PLAYER_2_ACTIVE:
10869 case EL_DYNABOMB_PLAYER_3_ACTIVE:
10870 case EL_DYNABOMB_PLAYER_4_ACTIVE:
10871 case EL_SP_DISK_RED_ACTIVE:
10872 CheckDynamite(x, y);
10875 case EL_AMOEBA_GROWING:
10876 AmoebeWaechst(x, y);
10879 case EL_AMOEBA_SHRINKING:
10880 AmoebaDisappearing(x, y);
10883 #if !USE_NEW_AMOEBA_CODE
10884 case EL_AMOEBA_WET:
10885 case EL_AMOEBA_DRY:
10886 case EL_AMOEBA_FULL:
10888 case EL_EMC_DRIPPER:
10889 AmoebeAbleger(x, y);
10893 case EL_GAME_OF_LIFE:
10898 case EL_EXIT_CLOSED:
10902 case EL_EM_EXIT_CLOSED:
10906 case EL_STEEL_EXIT_CLOSED:
10907 CheckExitSteel(x, y);
10910 case EL_EM_STEEL_EXIT_CLOSED:
10911 CheckExitSteelEM(x, y);
10914 case EL_SP_EXIT_CLOSED:
10918 case EL_EXPANDABLE_WALL_GROWING:
10919 case EL_EXPANDABLE_STEELWALL_GROWING:
10920 MauerWaechst(x, y);
10923 case EL_EXPANDABLE_WALL:
10924 case EL_EXPANDABLE_WALL_HORIZONTAL:
10925 case EL_EXPANDABLE_WALL_VERTICAL:
10926 case EL_EXPANDABLE_WALL_ANY:
10927 case EL_BD_EXPANDABLE_WALL:
10928 MauerAbleger(x, y);
10931 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10932 case EL_EXPANDABLE_STEELWALL_VERTICAL:
10933 case EL_EXPANDABLE_STEELWALL_ANY:
10934 MauerAblegerStahl(x, y);
10938 CheckForDragon(x, y);
10944 case EL_ELEMENT_SNAPPING:
10945 case EL_DIAGONAL_SHRINKING:
10946 case EL_DIAGONAL_GROWING:
10949 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10951 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10956 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10957 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10962 #else // ---------------------------------------------------------------------
10964 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10968 element = Feld[x][y];
10969 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10971 if (IS_ANIMATED(graphic) &&
10972 !IS_MOVING(x, y) &&
10974 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10976 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10977 DrawTwinkleOnField(x, y);
10979 else if ((element == EL_ACID ||
10980 element == EL_EXIT_OPEN ||
10981 element == EL_EM_EXIT_OPEN ||
10982 element == EL_SP_EXIT_OPEN ||
10983 element == EL_STEEL_EXIT_OPEN ||
10984 element == EL_EM_STEEL_EXIT_OPEN ||
10985 element == EL_SP_TERMINAL ||
10986 element == EL_SP_TERMINAL_ACTIVE ||
10987 element == EL_EXTRA_TIME ||
10988 element == EL_SHIELD_NORMAL ||
10989 element == EL_SHIELD_DEADLY) &&
10990 IS_ANIMATED(graphic))
10991 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10992 else if (IS_MOVING(x, y))
10993 ContinueMoving(x, y);
10994 else if (IS_ACTIVE_BOMB(element))
10995 CheckDynamite(x, y);
10996 else if (element == EL_AMOEBA_GROWING)
10997 AmoebeWaechst(x, y);
10998 else if (element == EL_AMOEBA_SHRINKING)
10999 AmoebaDisappearing(x, y);
11001 #if !USE_NEW_AMOEBA_CODE
11002 else if (IS_AMOEBALIVE(element))
11003 AmoebeAbleger(x, y);
11006 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11008 else if (element == EL_EXIT_CLOSED)
11010 else if (element == EL_EM_EXIT_CLOSED)
11012 else if (element == EL_STEEL_EXIT_CLOSED)
11013 CheckExitSteel(x, y);
11014 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11015 CheckExitSteelEM(x, y);
11016 else if (element == EL_SP_EXIT_CLOSED)
11018 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11019 element == EL_EXPANDABLE_STEELWALL_GROWING)
11020 MauerWaechst(x, y);
11021 else if (element == EL_EXPANDABLE_WALL ||
11022 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11023 element == EL_EXPANDABLE_WALL_VERTICAL ||
11024 element == EL_EXPANDABLE_WALL_ANY ||
11025 element == EL_BD_EXPANDABLE_WALL)
11026 MauerAbleger(x, y);
11027 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11028 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11029 element == EL_EXPANDABLE_STEELWALL_ANY)
11030 MauerAblegerStahl(x, y);
11031 else if (element == EL_FLAMES)
11032 CheckForDragon(x, y);
11033 else if (element == EL_EXPLOSION)
11034 ; /* drawing of correct explosion animation is handled separately */
11035 else if (element == EL_ELEMENT_SNAPPING ||
11036 element == EL_DIAGONAL_SHRINKING ||
11037 element == EL_DIAGONAL_GROWING)
11039 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11041 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11043 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11044 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11046 #endif // ---------------------------------------------------------------------
11048 if (IS_BELT_ACTIVE(element))
11049 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11051 if (game.magic_wall_active)
11053 int jx = local_player->jx, jy = local_player->jy;
11055 /* play the element sound at the position nearest to the player */
11056 if ((element == EL_MAGIC_WALL_FULL ||
11057 element == EL_MAGIC_WALL_ACTIVE ||
11058 element == EL_MAGIC_WALL_EMPTYING ||
11059 element == EL_BD_MAGIC_WALL_FULL ||
11060 element == EL_BD_MAGIC_WALL_ACTIVE ||
11061 element == EL_BD_MAGIC_WALL_EMPTYING ||
11062 element == EL_DC_MAGIC_WALL_FULL ||
11063 element == EL_DC_MAGIC_WALL_ACTIVE ||
11064 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11065 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11074 debug_print_timestamp(0, "- time for MAIN loop: -->");
11077 #if USE_NEW_AMOEBA_CODE
11078 /* new experimental amoeba growth stuff */
11079 if (!(FrameCounter % 8))
11081 static unsigned long random = 1684108901;
11083 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11085 x = RND(lev_fieldx);
11086 y = RND(lev_fieldy);
11087 element = Feld[x][y];
11089 if (!IS_PLAYER(x,y) &&
11090 (element == EL_EMPTY ||
11091 CAN_GROW_INTO(element) ||
11092 element == EL_QUICKSAND_EMPTY ||
11093 element == EL_QUICKSAND_FAST_EMPTY ||
11094 element == EL_ACID_SPLASH_LEFT ||
11095 element == EL_ACID_SPLASH_RIGHT))
11097 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11098 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11099 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11100 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11101 Feld[x][y] = EL_AMOEBA_DROP;
11104 random = random * 129 + 1;
11110 if (game.explosions_delayed)
11113 game.explosions_delayed = FALSE;
11115 SCAN_PLAYFIELD(x, y)
11117 element = Feld[x][y];
11119 if (ExplodeField[x][y])
11120 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11121 else if (element == EL_EXPLOSION)
11122 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11124 ExplodeField[x][y] = EX_TYPE_NONE;
11127 game.explosions_delayed = TRUE;
11130 if (game.magic_wall_active)
11132 if (!(game.magic_wall_time_left % 4))
11134 int element = Feld[magic_wall_x][magic_wall_y];
11136 if (element == EL_BD_MAGIC_WALL_FULL ||
11137 element == EL_BD_MAGIC_WALL_ACTIVE ||
11138 element == EL_BD_MAGIC_WALL_EMPTYING)
11139 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11140 else if (element == EL_DC_MAGIC_WALL_FULL ||
11141 element == EL_DC_MAGIC_WALL_ACTIVE ||
11142 element == EL_DC_MAGIC_WALL_EMPTYING)
11143 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11145 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11148 if (game.magic_wall_time_left > 0)
11150 game.magic_wall_time_left--;
11151 if (!game.magic_wall_time_left)
11153 SCAN_PLAYFIELD(x, y)
11155 element = Feld[x][y];
11157 if (element == EL_MAGIC_WALL_ACTIVE ||
11158 element == EL_MAGIC_WALL_FULL)
11160 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11161 DrawLevelField(x, y);
11163 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11164 element == EL_BD_MAGIC_WALL_FULL)
11166 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11167 DrawLevelField(x, y);
11169 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11170 element == EL_DC_MAGIC_WALL_FULL)
11172 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11173 DrawLevelField(x, y);
11177 game.magic_wall_active = FALSE;
11182 if (game.light_time_left > 0)
11184 game.light_time_left--;
11186 if (game.light_time_left == 0)
11187 RedrawAllLightSwitchesAndInvisibleElements();
11190 if (game.timegate_time_left > 0)
11192 game.timegate_time_left--;
11194 if (game.timegate_time_left == 0)
11195 CloseAllOpenTimegates();
11198 if (game.lenses_time_left > 0)
11200 game.lenses_time_left--;
11202 if (game.lenses_time_left == 0)
11203 RedrawAllInvisibleElementsForLenses();
11206 if (game.magnify_time_left > 0)
11208 game.magnify_time_left--;
11210 if (game.magnify_time_left == 0)
11211 RedrawAllInvisibleElementsForMagnifier();
11214 for (i = 0; i < MAX_PLAYERS; i++)
11216 struct PlayerInfo *player = &stored_player[i];
11218 if (SHIELD_ON(player))
11220 if (player->shield_deadly_time_left)
11221 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11222 else if (player->shield_normal_time_left)
11223 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11230 PlayAllPlayersSound();
11232 if (options.debug) /* calculate frames per second */
11234 static unsigned long fps_counter = 0;
11235 static int fps_frames = 0;
11236 unsigned long fps_delay_ms = Counter() - fps_counter;
11240 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11242 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11245 fps_counter = Counter();
11248 redraw_mask |= REDRAW_FPS;
11251 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11253 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11255 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11257 local_player->show_envelope = 0;
11261 debug_print_timestamp(0, "stop main loop profiling ");
11262 printf("----------------------------------------------------------\n");
11265 /* use random number generator in every frame to make it less predictable */
11266 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11270 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11272 int min_x = x, min_y = y, max_x = x, max_y = y;
11275 for (i = 0; i < MAX_PLAYERS; i++)
11277 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11279 if (!stored_player[i].active || &stored_player[i] == player)
11282 min_x = MIN(min_x, jx);
11283 min_y = MIN(min_y, jy);
11284 max_x = MAX(max_x, jx);
11285 max_y = MAX(max_y, jy);
11288 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11291 static boolean AllPlayersInVisibleScreen()
11295 for (i = 0; i < MAX_PLAYERS; i++)
11297 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11299 if (!stored_player[i].active)
11302 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11309 void ScrollLevel(int dx, int dy)
11312 static Bitmap *bitmap_db_field2 = NULL;
11313 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11320 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11321 /* only horizontal XOR vertical scroll direction allowed */
11322 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11327 if (bitmap_db_field2 == NULL)
11328 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11330 /* needed when blitting directly to same bitmap -- should not be needed with
11331 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11332 BlitBitmap(drawto_field, bitmap_db_field2,
11333 FX + TILEX * (dx == -1) - softscroll_offset,
11334 FY + TILEY * (dy == -1) - softscroll_offset,
11335 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11336 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11337 FX + TILEX * (dx == 1) - softscroll_offset,
11338 FY + TILEY * (dy == 1) - softscroll_offset);
11339 BlitBitmap(bitmap_db_field2, drawto_field,
11340 FX + TILEX * (dx == 1) - softscroll_offset,
11341 FY + TILEY * (dy == 1) - softscroll_offset,
11342 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11343 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11344 FX + TILEX * (dx == 1) - softscroll_offset,
11345 FY + TILEY * (dy == 1) - softscroll_offset);
11350 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11351 int xsize = (BX2 - BX1 + 1);
11352 int ysize = (BY2 - BY1 + 1);
11353 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11354 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11355 int step = (start < end ? +1 : -1);
11357 for (i = start; i != end; i += step)
11359 BlitBitmap(drawto_field, drawto_field,
11360 FX + TILEX * (dx != 0 ? i + step : 0),
11361 FY + TILEY * (dy != 0 ? i + step : 0),
11362 TILEX * (dx != 0 ? 1 : xsize),
11363 TILEY * (dy != 0 ? 1 : ysize),
11364 FX + TILEX * (dx != 0 ? i : 0),
11365 FY + TILEY * (dy != 0 ? i : 0));
11370 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11372 BlitBitmap(drawto_field, drawto_field,
11373 FX + TILEX * (dx == -1) - softscroll_offset,
11374 FY + TILEY * (dy == -1) - softscroll_offset,
11375 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11376 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11377 FX + TILEX * (dx == 1) - softscroll_offset,
11378 FY + TILEY * (dy == 1) - softscroll_offset);
11384 x = (dx == 1 ? BX1 : BX2);
11385 for (y = BY1; y <= BY2; y++)
11386 DrawScreenField(x, y);
11391 y = (dy == 1 ? BY1 : BY2);
11392 for (x = BX1; x <= BX2; x++)
11393 DrawScreenField(x, y);
11396 redraw_mask |= REDRAW_FIELD;
11399 static boolean canFallDown(struct PlayerInfo *player)
11401 int jx = player->jx, jy = player->jy;
11403 return (IN_LEV_FIELD(jx, jy + 1) &&
11404 (IS_FREE(jx, jy + 1) ||
11405 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11406 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11407 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11410 static boolean canPassField(int x, int y, int move_dir)
11412 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11413 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11414 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11415 int nextx = x + dx;
11416 int nexty = y + dy;
11417 int element = Feld[x][y];
11419 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11420 !CAN_MOVE(element) &&
11421 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11422 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11423 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11426 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11428 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11429 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11430 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11434 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11435 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11436 (IS_DIGGABLE(Feld[newx][newy]) ||
11437 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11438 canPassField(newx, newy, move_dir)));
11441 static void CheckGravityMovement(struct PlayerInfo *player)
11443 #if USE_PLAYER_GRAVITY
11444 if (player->gravity && !player->programmed_action)
11446 if (game.gravity && !player->programmed_action)
11449 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11450 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11451 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11452 int jx = player->jx, jy = player->jy;
11453 boolean player_is_moving_to_valid_field =
11454 (!player_is_snapping &&
11455 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11456 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11457 boolean player_can_fall_down = canFallDown(player);
11459 if (player_can_fall_down &&
11460 !player_is_moving_to_valid_field)
11461 player->programmed_action = MV_DOWN;
11465 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11467 return CheckGravityMovement(player);
11469 #if USE_PLAYER_GRAVITY
11470 if (player->gravity && !player->programmed_action)
11472 if (game.gravity && !player->programmed_action)
11475 int jx = player->jx, jy = player->jy;
11476 boolean field_under_player_is_free =
11477 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11478 boolean player_is_standing_on_valid_field =
11479 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11480 (IS_WALKABLE(Feld[jx][jy]) &&
11481 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11483 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11484 player->programmed_action = MV_DOWN;
11489 MovePlayerOneStep()
11490 -----------------------------------------------------------------------------
11491 dx, dy: direction (non-diagonal) to try to move the player to
11492 real_dx, real_dy: direction as read from input device (can be diagonal)
11495 boolean MovePlayerOneStep(struct PlayerInfo *player,
11496 int dx, int dy, int real_dx, int real_dy)
11498 int jx = player->jx, jy = player->jy;
11499 int new_jx = jx + dx, new_jy = jy + dy;
11500 #if !USE_FIXED_DONT_RUN_INTO
11504 boolean player_can_move = !player->cannot_move;
11506 if (!player->active || (!dx && !dy))
11507 return MP_NO_ACTION;
11509 player->MovDir = (dx < 0 ? MV_LEFT :
11510 dx > 0 ? MV_RIGHT :
11512 dy > 0 ? MV_DOWN : MV_NONE);
11514 if (!IN_LEV_FIELD(new_jx, new_jy))
11515 return MP_NO_ACTION;
11517 if (!player_can_move)
11519 if (player->MovPos == 0)
11521 player->is_moving = FALSE;
11522 player->is_digging = FALSE;
11523 player->is_collecting = FALSE;
11524 player->is_snapping = FALSE;
11525 player->is_pushing = FALSE;
11530 if (!options.network && game.centered_player_nr == -1 &&
11531 !AllPlayersInSight(player, new_jx, new_jy))
11532 return MP_NO_ACTION;
11534 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11535 return MP_NO_ACTION;
11538 #if !USE_FIXED_DONT_RUN_INTO
11539 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11541 /* (moved to DigField()) */
11542 if (player_can_move && DONT_RUN_INTO(element))
11544 if (element == EL_ACID && dx == 0 && dy == 1)
11546 SplashAcid(new_jx, new_jy);
11547 Feld[jx][jy] = EL_PLAYER_1;
11548 InitMovingField(jx, jy, MV_DOWN);
11549 Store[jx][jy] = EL_ACID;
11550 ContinueMoving(jx, jy);
11551 BuryPlayer(player);
11554 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11560 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11561 if (can_move != MP_MOVING)
11564 /* check if DigField() has caused relocation of the player */
11565 if (player->jx != jx || player->jy != jy)
11566 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11568 StorePlayer[jx][jy] = 0;
11569 player->last_jx = jx;
11570 player->last_jy = jy;
11571 player->jx = new_jx;
11572 player->jy = new_jy;
11573 StorePlayer[new_jx][new_jy] = player->element_nr;
11575 if (player->move_delay_value_next != -1)
11577 player->move_delay_value = player->move_delay_value_next;
11578 player->move_delay_value_next = -1;
11582 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11584 player->step_counter++;
11586 PlayerVisit[jx][jy] = FrameCounter;
11588 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11589 player->is_moving = TRUE;
11593 /* should better be called in MovePlayer(), but this breaks some tapes */
11594 ScrollPlayer(player, SCROLL_INIT);
11600 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11602 int jx = player->jx, jy = player->jy;
11603 int old_jx = jx, old_jy = jy;
11604 int moved = MP_NO_ACTION;
11606 if (!player->active)
11611 if (player->MovPos == 0)
11613 player->is_moving = FALSE;
11614 player->is_digging = FALSE;
11615 player->is_collecting = FALSE;
11616 player->is_snapping = FALSE;
11617 player->is_pushing = FALSE;
11623 if (player->move_delay > 0)
11626 player->move_delay = -1; /* set to "uninitialized" value */
11628 /* store if player is automatically moved to next field */
11629 player->is_auto_moving = (player->programmed_action != MV_NONE);
11631 /* remove the last programmed player action */
11632 player->programmed_action = 0;
11634 if (player->MovPos)
11636 /* should only happen if pre-1.2 tape recordings are played */
11637 /* this is only for backward compatibility */
11639 int original_move_delay_value = player->move_delay_value;
11642 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11646 /* scroll remaining steps with finest movement resolution */
11647 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11649 while (player->MovPos)
11651 ScrollPlayer(player, SCROLL_GO_ON);
11652 ScrollScreen(NULL, SCROLL_GO_ON);
11654 AdvanceFrameAndPlayerCounters(player->index_nr);
11660 player->move_delay_value = original_move_delay_value;
11663 player->is_active = FALSE;
11665 if (player->last_move_dir & MV_HORIZONTAL)
11667 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11668 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11672 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11673 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11676 #if USE_FIXED_BORDER_RUNNING_GFX
11677 if (!moved && !player->is_active)
11679 player->is_moving = FALSE;
11680 player->is_digging = FALSE;
11681 player->is_collecting = FALSE;
11682 player->is_snapping = FALSE;
11683 player->is_pushing = FALSE;
11691 if (moved & MP_MOVING && !ScreenMovPos &&
11692 (player->index_nr == game.centered_player_nr ||
11693 game.centered_player_nr == -1))
11695 if (moved & MP_MOVING && !ScreenMovPos &&
11696 (player == local_player || !options.network))
11699 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11700 int offset = (setup.scroll_delay ? 3 : 0);
11702 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11704 /* actual player has left the screen -- scroll in that direction */
11705 if (jx != old_jx) /* player has moved horizontally */
11706 scroll_x += (jx - old_jx);
11707 else /* player has moved vertically */
11708 scroll_y += (jy - old_jy);
11712 if (jx != old_jx) /* player has moved horizontally */
11714 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
11715 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11716 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11718 /* don't scroll over playfield boundaries */
11719 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11720 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11722 /* don't scroll more than one field at a time */
11723 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11725 /* don't scroll against the player's moving direction */
11726 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
11727 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11728 scroll_x = old_scroll_x;
11730 else /* player has moved vertically */
11732 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
11733 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11734 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11736 /* don't scroll over playfield boundaries */
11737 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11738 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11740 /* don't scroll more than one field at a time */
11741 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11743 /* don't scroll against the player's moving direction */
11744 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
11745 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11746 scroll_y = old_scroll_y;
11750 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11753 if (!options.network && game.centered_player_nr == -1 &&
11754 !AllPlayersInVisibleScreen())
11756 scroll_x = old_scroll_x;
11757 scroll_y = old_scroll_y;
11761 if (!options.network && !AllPlayersInVisibleScreen())
11763 scroll_x = old_scroll_x;
11764 scroll_y = old_scroll_y;
11769 ScrollScreen(player, SCROLL_INIT);
11770 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11775 player->StepFrame = 0;
11777 if (moved & MP_MOVING)
11779 if (old_jx != jx && old_jy == jy)
11780 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11781 else if (old_jx == jx && old_jy != jy)
11782 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11784 DrawLevelField(jx, jy); /* for "crumbled sand" */
11786 player->last_move_dir = player->MovDir;
11787 player->is_moving = TRUE;
11788 player->is_snapping = FALSE;
11789 player->is_switching = FALSE;
11790 player->is_dropping = FALSE;
11791 player->is_dropping_pressed = FALSE;
11792 player->drop_pressed_delay = 0;
11795 /* should better be called here than above, but this breaks some tapes */
11796 ScrollPlayer(player, SCROLL_INIT);
11801 CheckGravityMovementWhenNotMoving(player);
11803 player->is_moving = FALSE;
11805 /* at this point, the player is allowed to move, but cannot move right now
11806 (e.g. because of something blocking the way) -- ensure that the player
11807 is also allowed to move in the next frame (in old versions before 3.1.1,
11808 the player was forced to wait again for eight frames before next try) */
11810 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11811 player->move_delay = 0; /* allow direct movement in the next frame */
11814 if (player->move_delay == -1) /* not yet initialized by DigField() */
11815 player->move_delay = player->move_delay_value;
11817 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11819 TestIfPlayerTouchesBadThing(jx, jy);
11820 TestIfPlayerTouchesCustomElement(jx, jy);
11823 if (!player->active)
11824 RemovePlayer(player);
11829 void ScrollPlayer(struct PlayerInfo *player, int mode)
11831 int jx = player->jx, jy = player->jy;
11832 int last_jx = player->last_jx, last_jy = player->last_jy;
11833 int move_stepsize = TILEX / player->move_delay_value;
11835 #if USE_NEW_PLAYER_SPEED
11836 if (!player->active)
11839 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11842 if (!player->active || player->MovPos == 0)
11846 if (mode == SCROLL_INIT)
11848 player->actual_frame_counter = FrameCounter;
11849 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11851 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11852 Feld[last_jx][last_jy] == EL_EMPTY)
11854 int last_field_block_delay = 0; /* start with no blocking at all */
11855 int block_delay_adjustment = player->block_delay_adjustment;
11857 /* if player blocks last field, add delay for exactly one move */
11858 if (player->block_last_field)
11860 last_field_block_delay += player->move_delay_value;
11862 /* when blocking enabled, prevent moving up despite gravity */
11863 #if USE_PLAYER_GRAVITY
11864 if (player->gravity && player->MovDir == MV_UP)
11865 block_delay_adjustment = -1;
11867 if (game.gravity && player->MovDir == MV_UP)
11868 block_delay_adjustment = -1;
11872 /* add block delay adjustment (also possible when not blocking) */
11873 last_field_block_delay += block_delay_adjustment;
11875 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11876 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11879 #if USE_NEW_PLAYER_SPEED
11880 if (player->MovPos != 0) /* player has not yet reached destination */
11886 else if (!FrameReached(&player->actual_frame_counter, 1))
11889 #if USE_NEW_PLAYER_SPEED
11890 if (player->MovPos != 0)
11892 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11893 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11895 /* before DrawPlayer() to draw correct player graphic for this case */
11896 if (player->MovPos == 0)
11897 CheckGravityMovement(player);
11900 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11901 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11903 /* before DrawPlayer() to draw correct player graphic for this case */
11904 if (player->MovPos == 0)
11905 CheckGravityMovement(player);
11908 if (player->MovPos == 0) /* player reached destination field */
11910 if (player->move_delay_reset_counter > 0)
11912 player->move_delay_reset_counter--;
11914 if (player->move_delay_reset_counter == 0)
11916 /* continue with normal speed after quickly moving through gate */
11917 HALVE_PLAYER_SPEED(player);
11919 /* be able to make the next move without delay */
11920 player->move_delay = 0;
11924 player->last_jx = jx;
11925 player->last_jy = jy;
11927 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11928 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11929 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11930 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11931 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11932 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11934 DrawPlayer(player); /* needed here only to cleanup last field */
11935 RemovePlayer(player);
11937 if (local_player->friends_still_needed == 0 ||
11938 IS_SP_ELEMENT(Feld[jx][jy]))
11939 PlayerWins(player);
11942 /* this breaks one level: "machine", level 000 */
11944 int move_direction = player->MovDir;
11945 int enter_side = MV_DIR_OPPOSITE(move_direction);
11946 int leave_side = move_direction;
11947 int old_jx = last_jx;
11948 int old_jy = last_jy;
11949 int old_element = Feld[old_jx][old_jy];
11950 int new_element = Feld[jx][jy];
11952 if (IS_CUSTOM_ELEMENT(old_element))
11953 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11955 player->index_bit, leave_side);
11957 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11958 CE_PLAYER_LEAVES_X,
11959 player->index_bit, leave_side);
11961 if (IS_CUSTOM_ELEMENT(new_element))
11962 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11963 player->index_bit, enter_side);
11965 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11966 CE_PLAYER_ENTERS_X,
11967 player->index_bit, enter_side);
11969 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11970 CE_MOVE_OF_X, move_direction);
11973 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11975 TestIfPlayerTouchesBadThing(jx, jy);
11976 TestIfPlayerTouchesCustomElement(jx, jy);
11978 /* needed because pushed element has not yet reached its destination,
11979 so it would trigger a change event at its previous field location */
11980 if (!player->is_pushing)
11981 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11983 if (!player->active)
11984 RemovePlayer(player);
11987 if (!local_player->LevelSolved && level.use_step_counter)
11997 if (TimeLeft <= 10 && setup.time_limit)
11998 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12000 DrawGameValue_Time(TimeLeft);
12002 if (!TimeLeft && setup.time_limit)
12003 for (i = 0; i < MAX_PLAYERS; i++)
12004 KillPlayer(&stored_player[i]);
12006 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12007 DrawGameValue_Time(TimePlayed);
12010 if (tape.single_step && tape.recording && !tape.pausing &&
12011 !player->programmed_action)
12012 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12016 void ScrollScreen(struct PlayerInfo *player, int mode)
12018 static unsigned long screen_frame_counter = 0;
12020 if (mode == SCROLL_INIT)
12022 /* set scrolling step size according to actual player's moving speed */
12023 ScrollStepSize = TILEX / player->move_delay_value;
12025 screen_frame_counter = FrameCounter;
12026 ScreenMovDir = player->MovDir;
12027 ScreenMovPos = player->MovPos;
12028 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12031 else if (!FrameReached(&screen_frame_counter, 1))
12036 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12037 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12038 redraw_mask |= REDRAW_FIELD;
12041 ScreenMovDir = MV_NONE;
12044 void TestIfPlayerTouchesCustomElement(int x, int y)
12046 static int xy[4][2] =
12053 static int trigger_sides[4][2] =
12055 /* center side border side */
12056 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12057 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12058 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12059 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12061 static int touch_dir[4] =
12063 MV_LEFT | MV_RIGHT,
12068 int center_element = Feld[x][y]; /* should always be non-moving! */
12071 for (i = 0; i < NUM_DIRECTIONS; i++)
12073 int xx = x + xy[i][0];
12074 int yy = y + xy[i][1];
12075 int center_side = trigger_sides[i][0];
12076 int border_side = trigger_sides[i][1];
12077 int border_element;
12079 if (!IN_LEV_FIELD(xx, yy))
12082 if (IS_PLAYER(x, y))
12084 struct PlayerInfo *player = PLAYERINFO(x, y);
12086 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12087 border_element = Feld[xx][yy]; /* may be moving! */
12088 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12089 border_element = Feld[xx][yy];
12090 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12091 border_element = MovingOrBlocked2Element(xx, yy);
12093 continue; /* center and border element do not touch */
12095 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12096 player->index_bit, border_side);
12097 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12098 CE_PLAYER_TOUCHES_X,
12099 player->index_bit, border_side);
12101 else if (IS_PLAYER(xx, yy))
12103 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12105 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12107 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12108 continue; /* center and border element do not touch */
12111 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12112 player->index_bit, center_side);
12113 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12114 CE_PLAYER_TOUCHES_X,
12115 player->index_bit, center_side);
12121 #if USE_ELEMENT_TOUCHING_BUGFIX
12123 void TestIfElementTouchesCustomElement(int x, int y)
12125 static int xy[4][2] =
12132 static int trigger_sides[4][2] =
12134 /* center side border side */
12135 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12136 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12137 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12138 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12140 static int touch_dir[4] =
12142 MV_LEFT | MV_RIGHT,
12147 boolean change_center_element = FALSE;
12148 int center_element = Feld[x][y]; /* should always be non-moving! */
12149 int border_element_old[NUM_DIRECTIONS];
12152 for (i = 0; i < NUM_DIRECTIONS; i++)
12154 int xx = x + xy[i][0];
12155 int yy = y + xy[i][1];
12156 int border_element;
12158 border_element_old[i] = -1;
12160 if (!IN_LEV_FIELD(xx, yy))
12163 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12164 border_element = Feld[xx][yy]; /* may be moving! */
12165 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12166 border_element = Feld[xx][yy];
12167 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12168 border_element = MovingOrBlocked2Element(xx, yy);
12170 continue; /* center and border element do not touch */
12172 border_element_old[i] = border_element;
12175 for (i = 0; i < NUM_DIRECTIONS; i++)
12177 int xx = x + xy[i][0];
12178 int yy = y + xy[i][1];
12179 int center_side = trigger_sides[i][0];
12180 int border_element = border_element_old[i];
12182 if (border_element == -1)
12185 /* check for change of border element */
12186 CheckElementChangeBySide(xx, yy, border_element, center_element,
12187 CE_TOUCHING_X, center_side);
12190 for (i = 0; i < NUM_DIRECTIONS; i++)
12192 int border_side = trigger_sides[i][1];
12193 int border_element = border_element_old[i];
12195 if (border_element == -1)
12198 /* check for change of center element (but change it only once) */
12199 if (!change_center_element)
12200 change_center_element =
12201 CheckElementChangeBySide(x, y, center_element, border_element,
12202 CE_TOUCHING_X, border_side);
12208 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12210 static int xy[4][2] =
12217 static int trigger_sides[4][2] =
12219 /* center side border side */
12220 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12221 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12222 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12223 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12225 static int touch_dir[4] =
12227 MV_LEFT | MV_RIGHT,
12232 boolean change_center_element = FALSE;
12233 int center_element = Feld[x][y]; /* should always be non-moving! */
12236 for (i = 0; i < NUM_DIRECTIONS; i++)
12238 int xx = x + xy[i][0];
12239 int yy = y + xy[i][1];
12240 int center_side = trigger_sides[i][0];
12241 int border_side = trigger_sides[i][1];
12242 int border_element;
12244 if (!IN_LEV_FIELD(xx, yy))
12247 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12248 border_element = Feld[xx][yy]; /* may be moving! */
12249 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12250 border_element = Feld[xx][yy];
12251 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12252 border_element = MovingOrBlocked2Element(xx, yy);
12254 continue; /* center and border element do not touch */
12256 /* check for change of center element (but change it only once) */
12257 if (!change_center_element)
12258 change_center_element =
12259 CheckElementChangeBySide(x, y, center_element, border_element,
12260 CE_TOUCHING_X, border_side);
12262 /* check for change of border element */
12263 CheckElementChangeBySide(xx, yy, border_element, center_element,
12264 CE_TOUCHING_X, center_side);
12270 void TestIfElementHitsCustomElement(int x, int y, int direction)
12272 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12273 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12274 int hitx = x + dx, hity = y + dy;
12275 int hitting_element = Feld[x][y];
12276 int touched_element;
12278 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12281 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12282 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12284 if (IN_LEV_FIELD(hitx, hity))
12286 int opposite_direction = MV_DIR_OPPOSITE(direction);
12287 int hitting_side = direction;
12288 int touched_side = opposite_direction;
12289 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12290 MovDir[hitx][hity] != direction ||
12291 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12297 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12298 CE_HITTING_X, touched_side);
12300 CheckElementChangeBySide(hitx, hity, touched_element,
12301 hitting_element, CE_HIT_BY_X, hitting_side);
12303 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12304 CE_HIT_BY_SOMETHING, opposite_direction);
12308 /* "hitting something" is also true when hitting the playfield border */
12309 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12310 CE_HITTING_SOMETHING, direction);
12314 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12316 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12317 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12318 int hitx = x + dx, hity = y + dy;
12319 int hitting_element = Feld[x][y];
12320 int touched_element;
12322 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12323 !IS_FREE(hitx, hity) &&
12324 (!IS_MOVING(hitx, hity) ||
12325 MovDir[hitx][hity] != direction ||
12326 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12329 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12333 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12337 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12338 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12340 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12341 EP_CAN_SMASH_EVERYTHING, direction);
12343 if (IN_LEV_FIELD(hitx, hity))
12345 int opposite_direction = MV_DIR_OPPOSITE(direction);
12346 int hitting_side = direction;
12347 int touched_side = opposite_direction;
12349 int touched_element = MovingOrBlocked2Element(hitx, hity);
12352 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12353 MovDir[hitx][hity] != direction ||
12354 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12363 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12364 CE_SMASHED_BY_SOMETHING, opposite_direction);
12366 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12367 CE_OTHER_IS_SMASHING, touched_side);
12369 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12370 CE_OTHER_GETS_SMASHED, hitting_side);
12376 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12378 int i, kill_x = -1, kill_y = -1;
12380 int bad_element = -1;
12381 static int test_xy[4][2] =
12388 static int test_dir[4] =
12396 for (i = 0; i < NUM_DIRECTIONS; i++)
12398 int test_x, test_y, test_move_dir, test_element;
12400 test_x = good_x + test_xy[i][0];
12401 test_y = good_y + test_xy[i][1];
12403 if (!IN_LEV_FIELD(test_x, test_y))
12407 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12409 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12411 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12412 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12414 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12415 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12419 bad_element = test_element;
12425 if (kill_x != -1 || kill_y != -1)
12427 if (IS_PLAYER(good_x, good_y))
12429 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12431 if (player->shield_deadly_time_left > 0 &&
12432 !IS_INDESTRUCTIBLE(bad_element))
12433 Bang(kill_x, kill_y);
12434 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12435 KillPlayer(player);
12438 Bang(good_x, good_y);
12442 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12444 int i, kill_x = -1, kill_y = -1;
12445 int bad_element = Feld[bad_x][bad_y];
12446 static int test_xy[4][2] =
12453 static int touch_dir[4] =
12455 MV_LEFT | MV_RIGHT,
12460 static int test_dir[4] =
12468 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12471 for (i = 0; i < NUM_DIRECTIONS; i++)
12473 int test_x, test_y, test_move_dir, test_element;
12475 test_x = bad_x + test_xy[i][0];
12476 test_y = bad_y + test_xy[i][1];
12477 if (!IN_LEV_FIELD(test_x, test_y))
12481 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12483 test_element = Feld[test_x][test_y];
12485 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12486 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12488 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12489 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12491 /* good thing is player or penguin that does not move away */
12492 if (IS_PLAYER(test_x, test_y))
12494 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12496 if (bad_element == EL_ROBOT && player->is_moving)
12497 continue; /* robot does not kill player if he is moving */
12499 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12501 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12502 continue; /* center and border element do not touch */
12509 else if (test_element == EL_PENGUIN)
12518 if (kill_x != -1 || kill_y != -1)
12520 if (IS_PLAYER(kill_x, kill_y))
12522 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12524 if (player->shield_deadly_time_left > 0 &&
12525 !IS_INDESTRUCTIBLE(bad_element))
12526 Bang(bad_x, bad_y);
12527 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12528 KillPlayer(player);
12531 Bang(kill_x, kill_y);
12535 void TestIfPlayerTouchesBadThing(int x, int y)
12537 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12540 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12542 TestIfGoodThingHitsBadThing(x, y, move_dir);
12545 void TestIfBadThingTouchesPlayer(int x, int y)
12547 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12550 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12552 TestIfBadThingHitsGoodThing(x, y, move_dir);
12555 void TestIfFriendTouchesBadThing(int x, int y)
12557 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12560 void TestIfBadThingTouchesFriend(int x, int y)
12562 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12565 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12567 int i, kill_x = bad_x, kill_y = bad_y;
12568 static int xy[4][2] =
12576 for (i = 0; i < NUM_DIRECTIONS; i++)
12580 x = bad_x + xy[i][0];
12581 y = bad_y + xy[i][1];
12582 if (!IN_LEV_FIELD(x, y))
12585 element = Feld[x][y];
12586 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12587 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12595 if (kill_x != bad_x || kill_y != bad_y)
12596 Bang(bad_x, bad_y);
12599 void KillPlayer(struct PlayerInfo *player)
12601 int jx = player->jx, jy = player->jy;
12603 if (!player->active)
12606 /* the following code was introduced to prevent an infinite loop when calling
12608 -> CheckTriggeredElementChangeExt()
12609 -> ExecuteCustomElementAction()
12611 -> (infinitely repeating the above sequence of function calls)
12612 which occurs when killing the player while having a CE with the setting
12613 "kill player X when explosion of <player X>"; the solution using a new
12614 field "player->killed" was chosen for backwards compatibility, although
12615 clever use of the fields "player->active" etc. would probably also work */
12617 if (player->killed)
12621 player->killed = TRUE;
12623 /* remove accessible field at the player's position */
12624 Feld[jx][jy] = EL_EMPTY;
12626 /* deactivate shield (else Bang()/Explode() would not work right) */
12627 player->shield_normal_time_left = 0;
12628 player->shield_deadly_time_left = 0;
12631 BuryPlayer(player);
12634 static void KillPlayerUnlessEnemyProtected(int x, int y)
12636 if (!PLAYER_ENEMY_PROTECTED(x, y))
12637 KillPlayer(PLAYERINFO(x, y));
12640 static void KillPlayerUnlessExplosionProtected(int x, int y)
12642 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12643 KillPlayer(PLAYERINFO(x, y));
12646 void BuryPlayer(struct PlayerInfo *player)
12648 int jx = player->jx, jy = player->jy;
12650 if (!player->active)
12653 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12654 PlayLevelSound(jx, jy, SND_GAME_LOSING);
12656 player->GameOver = TRUE;
12657 RemovePlayer(player);
12660 void RemovePlayer(struct PlayerInfo *player)
12662 int jx = player->jx, jy = player->jy;
12663 int i, found = FALSE;
12665 player->present = FALSE;
12666 player->active = FALSE;
12668 if (!ExplodeField[jx][jy])
12669 StorePlayer[jx][jy] = 0;
12671 if (player->is_moving)
12672 DrawLevelField(player->last_jx, player->last_jy);
12674 for (i = 0; i < MAX_PLAYERS; i++)
12675 if (stored_player[i].active)
12679 AllPlayersGone = TRUE;
12685 #if USE_NEW_SNAP_DELAY
12686 static void setFieldForSnapping(int x, int y, int element, int direction)
12688 struct ElementInfo *ei = &element_info[element];
12689 int direction_bit = MV_DIR_TO_BIT(direction);
12690 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12691 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12692 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12694 Feld[x][y] = EL_ELEMENT_SNAPPING;
12695 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12697 ResetGfxAnimation(x, y);
12699 GfxElement[x][y] = element;
12700 GfxAction[x][y] = action;
12701 GfxDir[x][y] = direction;
12702 GfxFrame[x][y] = -1;
12707 =============================================================================
12708 checkDiagonalPushing()
12709 -----------------------------------------------------------------------------
12710 check if diagonal input device direction results in pushing of object
12711 (by checking if the alternative direction is walkable, diggable, ...)
12712 =============================================================================
12715 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12716 int x, int y, int real_dx, int real_dy)
12718 int jx, jy, dx, dy, xx, yy;
12720 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
12723 /* diagonal direction: check alternative direction */
12728 xx = jx + (dx == 0 ? real_dx : 0);
12729 yy = jy + (dy == 0 ? real_dy : 0);
12731 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12735 =============================================================================
12737 -----------------------------------------------------------------------------
12738 x, y: field next to player (non-diagonal) to try to dig to
12739 real_dx, real_dy: direction as read from input device (can be diagonal)
12740 =============================================================================
12743 int DigField(struct PlayerInfo *player,
12744 int oldx, int oldy, int x, int y,
12745 int real_dx, int real_dy, int mode)
12747 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12748 boolean player_was_pushing = player->is_pushing;
12749 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12750 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12751 int jx = oldx, jy = oldy;
12752 int dx = x - jx, dy = y - jy;
12753 int nextx = x + dx, nexty = y + dy;
12754 int move_direction = (dx == -1 ? MV_LEFT :
12755 dx == +1 ? MV_RIGHT :
12757 dy == +1 ? MV_DOWN : MV_NONE);
12758 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12759 int dig_side = MV_DIR_OPPOSITE(move_direction);
12760 int old_element = Feld[jx][jy];
12761 #if USE_FIXED_DONT_RUN_INTO
12762 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12768 if (is_player) /* function can also be called by EL_PENGUIN */
12770 if (player->MovPos == 0)
12772 player->is_digging = FALSE;
12773 player->is_collecting = FALSE;
12776 if (player->MovPos == 0) /* last pushing move finished */
12777 player->is_pushing = FALSE;
12779 if (mode == DF_NO_PUSH) /* player just stopped pushing */
12781 player->is_switching = FALSE;
12782 player->push_delay = -1;
12784 return MP_NO_ACTION;
12788 #if !USE_FIXED_DONT_RUN_INTO
12789 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12790 return MP_NO_ACTION;
12793 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12794 old_element = Back[jx][jy];
12796 /* in case of element dropped at player position, check background */
12797 else if (Back[jx][jy] != EL_EMPTY &&
12798 game.engine_version >= VERSION_IDENT(2,2,0,0))
12799 old_element = Back[jx][jy];
12801 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12802 return MP_NO_ACTION; /* field has no opening in this direction */
12804 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12805 return MP_NO_ACTION; /* field has no opening in this direction */
12807 #if USE_FIXED_DONT_RUN_INTO
12808 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12812 Feld[jx][jy] = player->artwork_element;
12813 InitMovingField(jx, jy, MV_DOWN);
12814 Store[jx][jy] = EL_ACID;
12815 ContinueMoving(jx, jy);
12816 BuryPlayer(player);
12818 return MP_DONT_RUN_INTO;
12822 #if USE_FIXED_DONT_RUN_INTO
12823 if (player_can_move && DONT_RUN_INTO(element))
12825 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12827 return MP_DONT_RUN_INTO;
12831 #if USE_FIXED_DONT_RUN_INTO
12832 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12833 return MP_NO_ACTION;
12836 #if !USE_FIXED_DONT_RUN_INTO
12837 element = Feld[x][y];
12840 collect_count = element_info[element].collect_count_initial;
12842 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12843 return MP_NO_ACTION;
12845 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12846 player_can_move = player_can_move_or_snap;
12848 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12849 game.engine_version >= VERSION_IDENT(2,2,0,0))
12851 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12852 player->index_bit, dig_side);
12853 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12854 player->index_bit, dig_side);
12856 if (element == EL_DC_LANDMINE)
12859 if (Feld[x][y] != element) /* field changed by snapping */
12862 return MP_NO_ACTION;
12865 #if USE_PLAYER_GRAVITY
12866 if (player->gravity && is_player && !player->is_auto_moving &&
12867 canFallDown(player) && move_direction != MV_DOWN &&
12868 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12869 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12871 if (game.gravity && is_player && !player->is_auto_moving &&
12872 canFallDown(player) && move_direction != MV_DOWN &&
12873 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12874 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12877 if (player_can_move &&
12878 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12880 int sound_element = SND_ELEMENT(element);
12881 int sound_action = ACTION_WALKING;
12883 if (IS_RND_GATE(element))
12885 if (!player->key[RND_GATE_NR(element)])
12886 return MP_NO_ACTION;
12888 else if (IS_RND_GATE_GRAY(element))
12890 if (!player->key[RND_GATE_GRAY_NR(element)])
12891 return MP_NO_ACTION;
12893 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12895 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12896 return MP_NO_ACTION;
12898 else if (element == EL_EXIT_OPEN ||
12899 element == EL_EM_EXIT_OPEN ||
12900 element == EL_STEEL_EXIT_OPEN ||
12901 element == EL_EM_STEEL_EXIT_OPEN ||
12902 element == EL_SP_EXIT_OPEN ||
12903 element == EL_SP_EXIT_OPENING)
12905 sound_action = ACTION_PASSING; /* player is passing exit */
12907 else if (element == EL_EMPTY)
12909 sound_action = ACTION_MOVING; /* nothing to walk on */
12912 /* play sound from background or player, whatever is available */
12913 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12914 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12916 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12918 else if (player_can_move &&
12919 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12921 if (!ACCESS_FROM(element, opposite_direction))
12922 return MP_NO_ACTION; /* field not accessible from this direction */
12924 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12925 return MP_NO_ACTION;
12927 if (IS_EM_GATE(element))
12929 if (!player->key[EM_GATE_NR(element)])
12930 return MP_NO_ACTION;
12932 else if (IS_EM_GATE_GRAY(element))
12934 if (!player->key[EM_GATE_GRAY_NR(element)])
12935 return MP_NO_ACTION;
12937 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12939 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12940 return MP_NO_ACTION;
12942 else if (IS_EMC_GATE(element))
12944 if (!player->key[EMC_GATE_NR(element)])
12945 return MP_NO_ACTION;
12947 else if (IS_EMC_GATE_GRAY(element))
12949 if (!player->key[EMC_GATE_GRAY_NR(element)])
12950 return MP_NO_ACTION;
12952 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12954 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12955 return MP_NO_ACTION;
12957 else if (element == EL_DC_GATE_WHITE ||
12958 element == EL_DC_GATE_WHITE_GRAY ||
12959 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12961 if (player->num_white_keys == 0)
12962 return MP_NO_ACTION;
12964 player->num_white_keys--;
12966 else if (IS_SP_PORT(element))
12968 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12969 element == EL_SP_GRAVITY_PORT_RIGHT ||
12970 element == EL_SP_GRAVITY_PORT_UP ||
12971 element == EL_SP_GRAVITY_PORT_DOWN)
12972 #if USE_PLAYER_GRAVITY
12973 player->gravity = !player->gravity;
12975 game.gravity = !game.gravity;
12977 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12978 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12979 element == EL_SP_GRAVITY_ON_PORT_UP ||
12980 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12981 #if USE_PLAYER_GRAVITY
12982 player->gravity = TRUE;
12984 game.gravity = TRUE;
12986 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12987 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12988 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12989 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12990 #if USE_PLAYER_GRAVITY
12991 player->gravity = FALSE;
12993 game.gravity = FALSE;
12997 /* automatically move to the next field with double speed */
12998 player->programmed_action = move_direction;
13000 if (player->move_delay_reset_counter == 0)
13002 player->move_delay_reset_counter = 2; /* two double speed steps */
13004 DOUBLE_PLAYER_SPEED(player);
13007 PlayLevelSoundAction(x, y, ACTION_PASSING);
13009 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13013 if (mode != DF_SNAP)
13015 GfxElement[x][y] = GFX_ELEMENT(element);
13016 player->is_digging = TRUE;
13019 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13021 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13022 player->index_bit, dig_side);
13024 if (mode == DF_SNAP)
13026 #if USE_NEW_SNAP_DELAY
13027 if (level.block_snap_field)
13028 setFieldForSnapping(x, y, element, move_direction);
13030 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13032 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13035 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13036 player->index_bit, dig_side);
13039 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13043 if (is_player && mode != DF_SNAP)
13045 GfxElement[x][y] = element;
13046 player->is_collecting = TRUE;
13049 if (element == EL_SPEED_PILL)
13051 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13053 else if (element == EL_EXTRA_TIME && level.time > 0)
13055 TimeLeft += level.extra_time;
13056 DrawGameValue_Time(TimeLeft);
13058 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13060 player->shield_normal_time_left += level.shield_normal_time;
13061 if (element == EL_SHIELD_DEADLY)
13062 player->shield_deadly_time_left += level.shield_deadly_time;
13064 else if (element == EL_DYNAMITE ||
13065 element == EL_EM_DYNAMITE ||
13066 element == EL_SP_DISK_RED)
13068 if (player->inventory_size < MAX_INVENTORY_SIZE)
13069 player->inventory_element[player->inventory_size++] = element;
13071 DrawGameDoorValues();
13073 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13075 player->dynabomb_count++;
13076 player->dynabombs_left++;
13078 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13080 player->dynabomb_size++;
13082 else if (element == EL_DYNABOMB_INCREASE_POWER)
13084 player->dynabomb_xl = TRUE;
13086 else if (IS_KEY(element))
13088 player->key[KEY_NR(element)] = TRUE;
13090 DrawGameDoorValues();
13092 else if (element == EL_DC_KEY_WHITE)
13094 player->num_white_keys++;
13096 /* display white keys? */
13097 /* DrawGameDoorValues(); */
13099 else if (IS_ENVELOPE(element))
13101 player->show_envelope = element;
13103 else if (element == EL_EMC_LENSES)
13105 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13107 RedrawAllInvisibleElementsForLenses();
13109 else if (element == EL_EMC_MAGNIFIER)
13111 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13113 RedrawAllInvisibleElementsForMagnifier();
13115 else if (IS_DROPPABLE(element) ||
13116 IS_THROWABLE(element)) /* can be collected and dropped */
13120 if (collect_count == 0)
13121 player->inventory_infinite_element = element;
13123 for (i = 0; i < collect_count; i++)
13124 if (player->inventory_size < MAX_INVENTORY_SIZE)
13125 player->inventory_element[player->inventory_size++] = element;
13127 DrawGameDoorValues();
13129 else if (collect_count > 0)
13131 local_player->gems_still_needed -= collect_count;
13132 if (local_player->gems_still_needed < 0)
13133 local_player->gems_still_needed = 0;
13135 DrawGameValue_Emeralds(local_player->gems_still_needed);
13138 RaiseScoreElement(element);
13139 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13142 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13143 player->index_bit, dig_side);
13145 if (mode == DF_SNAP)
13147 #if USE_NEW_SNAP_DELAY
13148 if (level.block_snap_field)
13149 setFieldForSnapping(x, y, element, move_direction);
13151 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13153 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13156 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13157 player->index_bit, dig_side);
13160 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13162 if (mode == DF_SNAP && element != EL_BD_ROCK)
13163 return MP_NO_ACTION;
13165 if (CAN_FALL(element) && dy)
13166 return MP_NO_ACTION;
13168 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13169 !(element == EL_SPRING && level.use_spring_bug))
13170 return MP_NO_ACTION;
13172 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13173 ((move_direction & MV_VERTICAL &&
13174 ((element_info[element].move_pattern & MV_LEFT &&
13175 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13176 (element_info[element].move_pattern & MV_RIGHT &&
13177 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13178 (move_direction & MV_HORIZONTAL &&
13179 ((element_info[element].move_pattern & MV_UP &&
13180 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13181 (element_info[element].move_pattern & MV_DOWN &&
13182 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13183 return MP_NO_ACTION;
13185 /* do not push elements already moving away faster than player */
13186 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13187 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13188 return MP_NO_ACTION;
13190 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13192 if (player->push_delay_value == -1 || !player_was_pushing)
13193 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13195 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13197 if (player->push_delay_value == -1)
13198 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13200 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13202 if (!player->is_pushing)
13203 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13206 player->is_pushing = TRUE;
13207 player->is_active = TRUE;
13209 if (!(IN_LEV_FIELD(nextx, nexty) &&
13210 (IS_FREE(nextx, nexty) ||
13211 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13212 IS_SB_ELEMENT(element)))))
13213 return MP_NO_ACTION;
13215 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13216 return MP_NO_ACTION;
13218 if (player->push_delay == -1) /* new pushing; restart delay */
13219 player->push_delay = 0;
13221 if (player->push_delay < player->push_delay_value &&
13222 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13223 element != EL_SPRING && element != EL_BALLOON)
13225 /* make sure that there is no move delay before next try to push */
13226 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13227 player->move_delay = 0;
13229 return MP_NO_ACTION;
13232 if (IS_SB_ELEMENT(element))
13234 if (element == EL_SOKOBAN_FIELD_FULL)
13236 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13237 local_player->sokobanfields_still_needed++;
13240 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13242 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13243 local_player->sokobanfields_still_needed--;
13246 Feld[x][y] = EL_SOKOBAN_OBJECT;
13248 if (Back[x][y] == Back[nextx][nexty])
13249 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13250 else if (Back[x][y] != 0)
13251 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13254 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13257 if (local_player->sokobanfields_still_needed == 0 &&
13258 game.emulation == EMU_SOKOBAN)
13260 PlayerWins(player);
13262 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13266 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13268 InitMovingField(x, y, move_direction);
13269 GfxAction[x][y] = ACTION_PUSHING;
13271 if (mode == DF_SNAP)
13272 ContinueMoving(x, y);
13274 MovPos[x][y] = (dx != 0 ? dx : dy);
13276 Pushed[x][y] = TRUE;
13277 Pushed[nextx][nexty] = TRUE;
13279 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13280 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13282 player->push_delay_value = -1; /* get new value later */
13284 /* check for element change _after_ element has been pushed */
13285 if (game.use_change_when_pushing_bug)
13287 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13288 player->index_bit, dig_side);
13289 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13290 player->index_bit, dig_side);
13293 else if (IS_SWITCHABLE(element))
13295 if (PLAYER_SWITCHING(player, x, y))
13297 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13298 player->index_bit, dig_side);
13303 player->is_switching = TRUE;
13304 player->switch_x = x;
13305 player->switch_y = y;
13307 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13309 if (element == EL_ROBOT_WHEEL)
13311 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13315 DrawLevelField(x, y);
13317 else if (element == EL_SP_TERMINAL)
13321 SCAN_PLAYFIELD(xx, yy)
13323 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13325 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13326 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13329 else if (IS_BELT_SWITCH(element))
13331 ToggleBeltSwitch(x, y);
13333 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13334 element == EL_SWITCHGATE_SWITCH_DOWN ||
13335 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13336 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13338 ToggleSwitchgateSwitch(x, y);
13340 else if (element == EL_LIGHT_SWITCH ||
13341 element == EL_LIGHT_SWITCH_ACTIVE)
13343 ToggleLightSwitch(x, y);
13345 else if (element == EL_TIMEGATE_SWITCH ||
13346 element == EL_DC_TIMEGATE_SWITCH)
13348 ActivateTimegateSwitch(x, y);
13350 else if (element == EL_BALLOON_SWITCH_LEFT ||
13351 element == EL_BALLOON_SWITCH_RIGHT ||
13352 element == EL_BALLOON_SWITCH_UP ||
13353 element == EL_BALLOON_SWITCH_DOWN ||
13354 element == EL_BALLOON_SWITCH_NONE ||
13355 element == EL_BALLOON_SWITCH_ANY)
13357 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13358 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13359 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13360 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13361 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13364 else if (element == EL_LAMP)
13366 Feld[x][y] = EL_LAMP_ACTIVE;
13367 local_player->lights_still_needed--;
13369 ResetGfxAnimation(x, y);
13370 DrawLevelField(x, y);
13372 else if (element == EL_TIME_ORB_FULL)
13374 Feld[x][y] = EL_TIME_ORB_EMPTY;
13376 if (level.time > 0 || level.use_time_orb_bug)
13378 TimeLeft += level.time_orb_time;
13379 DrawGameValue_Time(TimeLeft);
13382 ResetGfxAnimation(x, y);
13383 DrawLevelField(x, y);
13385 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13386 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13390 game.ball_state = !game.ball_state;
13392 SCAN_PLAYFIELD(xx, yy)
13394 int e = Feld[xx][yy];
13396 if (game.ball_state)
13398 if (e == EL_EMC_MAGIC_BALL)
13399 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13400 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13401 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13405 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13406 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13407 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13408 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13413 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13414 player->index_bit, dig_side);
13416 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13417 player->index_bit, dig_side);
13419 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13420 player->index_bit, dig_side);
13426 if (!PLAYER_SWITCHING(player, x, y))
13428 player->is_switching = TRUE;
13429 player->switch_x = x;
13430 player->switch_y = y;
13432 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13433 player->index_bit, dig_side);
13434 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13435 player->index_bit, dig_side);
13437 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13438 player->index_bit, dig_side);
13439 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13440 player->index_bit, dig_side);
13443 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13444 player->index_bit, dig_side);
13445 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13446 player->index_bit, dig_side);
13448 return MP_NO_ACTION;
13451 player->push_delay = -1;
13453 if (is_player) /* function can also be called by EL_PENGUIN */
13455 if (Feld[x][y] != element) /* really digged/collected something */
13457 player->is_collecting = !player->is_digging;
13458 player->is_active = TRUE;
13465 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13467 int jx = player->jx, jy = player->jy;
13468 int x = jx + dx, y = jy + dy;
13469 int snap_direction = (dx == -1 ? MV_LEFT :
13470 dx == +1 ? MV_RIGHT :
13472 dy == +1 ? MV_DOWN : MV_NONE);
13473 boolean can_continue_snapping = (level.continuous_snapping &&
13474 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13476 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13479 if (!player->active || !IN_LEV_FIELD(x, y))
13487 if (player->MovPos == 0)
13488 player->is_pushing = FALSE;
13490 player->is_snapping = FALSE;
13492 if (player->MovPos == 0)
13494 player->is_moving = FALSE;
13495 player->is_digging = FALSE;
13496 player->is_collecting = FALSE;
13502 #if USE_NEW_CONTINUOUS_SNAPPING
13503 /* prevent snapping with already pressed snap key when not allowed */
13504 if (player->is_snapping && !can_continue_snapping)
13507 if (player->is_snapping)
13511 player->MovDir = snap_direction;
13513 if (player->MovPos == 0)
13515 player->is_moving = FALSE;
13516 player->is_digging = FALSE;
13517 player->is_collecting = FALSE;
13520 player->is_dropping = FALSE;
13521 player->is_dropping_pressed = FALSE;
13522 player->drop_pressed_delay = 0;
13524 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13527 player->is_snapping = TRUE;
13528 player->is_active = TRUE;
13530 if (player->MovPos == 0)
13532 player->is_moving = FALSE;
13533 player->is_digging = FALSE;
13534 player->is_collecting = FALSE;
13537 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13538 DrawLevelField(player->last_jx, player->last_jy);
13540 DrawLevelField(x, y);
13545 boolean DropElement(struct PlayerInfo *player)
13547 int old_element, new_element;
13548 int dropx = player->jx, dropy = player->jy;
13549 int drop_direction = player->MovDir;
13550 int drop_side = drop_direction;
13551 int drop_element = (player->inventory_size > 0 ?
13552 player->inventory_element[player->inventory_size - 1] :
13553 player->inventory_infinite_element != EL_UNDEFINED ?
13554 player->inventory_infinite_element :
13555 player->dynabombs_left > 0 ?
13556 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13559 player->is_dropping_pressed = TRUE;
13561 /* do not drop an element on top of another element; when holding drop key
13562 pressed without moving, dropped element must move away before the next
13563 element can be dropped (this is especially important if the next element
13564 is dynamite, which can be placed on background for historical reasons) */
13565 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13568 if (IS_THROWABLE(drop_element))
13570 dropx += GET_DX_FROM_DIR(drop_direction);
13571 dropy += GET_DY_FROM_DIR(drop_direction);
13573 if (!IN_LEV_FIELD(dropx, dropy))
13577 old_element = Feld[dropx][dropy]; /* old element at dropping position */
13578 new_element = drop_element; /* default: no change when dropping */
13580 /* check if player is active, not moving and ready to drop */
13581 if (!player->active || player->MovPos || player->drop_delay > 0)
13584 /* check if player has anything that can be dropped */
13585 if (new_element == EL_UNDEFINED)
13588 /* check if drop key was pressed long enough for EM style dynamite */
13589 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13592 /* check if anything can be dropped at the current position */
13593 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13596 /* collected custom elements can only be dropped on empty fields */
13597 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13600 if (old_element != EL_EMPTY)
13601 Back[dropx][dropy] = old_element; /* store old element on this field */
13603 ResetGfxAnimation(dropx, dropy);
13604 ResetRandomAnimationValue(dropx, dropy);
13606 if (player->inventory_size > 0 ||
13607 player->inventory_infinite_element != EL_UNDEFINED)
13609 if (player->inventory_size > 0)
13611 player->inventory_size--;
13613 DrawGameDoorValues();
13615 if (new_element == EL_DYNAMITE)
13616 new_element = EL_DYNAMITE_ACTIVE;
13617 else if (new_element == EL_EM_DYNAMITE)
13618 new_element = EL_EM_DYNAMITE_ACTIVE;
13619 else if (new_element == EL_SP_DISK_RED)
13620 new_element = EL_SP_DISK_RED_ACTIVE;
13623 Feld[dropx][dropy] = new_element;
13625 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13626 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13627 el2img(Feld[dropx][dropy]), 0);
13629 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13631 /* needed if previous element just changed to "empty" in the last frame */
13632 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13634 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13635 player->index_bit, drop_side);
13636 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13638 player->index_bit, drop_side);
13640 TestIfElementTouchesCustomElement(dropx, dropy);
13642 else /* player is dropping a dyna bomb */
13644 player->dynabombs_left--;
13646 Feld[dropx][dropy] = new_element;
13648 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13649 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13650 el2img(Feld[dropx][dropy]), 0);
13652 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13655 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13656 InitField_WithBug1(dropx, dropy, FALSE);
13658 new_element = Feld[dropx][dropy]; /* element might have changed */
13660 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13661 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13663 int move_direction, nextx, nexty;
13665 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13666 MovDir[dropx][dropy] = drop_direction;
13668 move_direction = MovDir[dropx][dropy];
13669 nextx = dropx + GET_DX_FROM_DIR(move_direction);
13670 nexty = dropy + GET_DY_FROM_DIR(move_direction);
13672 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13674 #if USE_FIX_IMPACT_COLLISION
13675 /* do not cause impact style collision by dropping elements that can fall */
13676 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13678 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13682 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13683 player->is_dropping = TRUE;
13685 player->drop_pressed_delay = 0;
13686 player->is_dropping_pressed = FALSE;
13688 player->drop_x = dropx;
13689 player->drop_y = dropy;
13694 /* ------------------------------------------------------------------------- */
13695 /* game sound playing functions */
13696 /* ------------------------------------------------------------------------- */
13698 static int *loop_sound_frame = NULL;
13699 static int *loop_sound_volume = NULL;
13701 void InitPlayLevelSound()
13703 int num_sounds = getSoundListSize();
13705 checked_free(loop_sound_frame);
13706 checked_free(loop_sound_volume);
13708 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
13709 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13712 static void PlayLevelSound(int x, int y, int nr)
13714 int sx = SCREENX(x), sy = SCREENY(y);
13715 int volume, stereo_position;
13716 int max_distance = 8;
13717 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13719 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13720 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13723 if (!IN_LEV_FIELD(x, y) ||
13724 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13725 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13728 volume = SOUND_MAX_VOLUME;
13730 if (!IN_SCR_FIELD(sx, sy))
13732 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13733 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13735 volume -= volume * (dx > dy ? dx : dy) / max_distance;
13738 stereo_position = (SOUND_MAX_LEFT +
13739 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13740 (SCR_FIELDX + 2 * max_distance));
13742 if (IS_LOOP_SOUND(nr))
13744 /* This assures that quieter loop sounds do not overwrite louder ones,
13745 while restarting sound volume comparison with each new game frame. */
13747 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13750 loop_sound_volume[nr] = volume;
13751 loop_sound_frame[nr] = FrameCounter;
13754 PlaySoundExt(nr, volume, stereo_position, type);
13757 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13759 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13760 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13761 y < LEVELY(BY1) ? LEVELY(BY1) :
13762 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13766 static void PlayLevelSoundAction(int x, int y, int action)
13768 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13771 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13773 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13775 if (sound_effect != SND_UNDEFINED)
13776 PlayLevelSound(x, y, sound_effect);
13779 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13782 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13784 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13785 PlayLevelSound(x, y, sound_effect);
13788 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13790 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13792 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13793 PlayLevelSound(x, y, sound_effect);
13796 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13798 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13800 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13801 StopSound(sound_effect);
13804 static void PlayLevelMusic()
13806 if (levelset.music[level_nr] != MUS_UNDEFINED)
13807 PlayMusic(levelset.music[level_nr]); /* from config file */
13809 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13812 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13814 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13815 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13816 int x = xx - 1 - offset;
13817 int y = yy - 1 - offset;
13822 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13826 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13830 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13834 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13838 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13842 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13846 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13849 case SAMPLE_android_clone:
13850 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13853 case SAMPLE_android_move:
13854 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13857 case SAMPLE_spring:
13858 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13862 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13866 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13869 case SAMPLE_eater_eat:
13870 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13874 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13877 case SAMPLE_collect:
13878 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13881 case SAMPLE_diamond:
13882 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13885 case SAMPLE_squash:
13886 /* !!! CHECK THIS !!! */
13888 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13890 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13894 case SAMPLE_wonderfall:
13895 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13899 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13903 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13907 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13911 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13915 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13919 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13922 case SAMPLE_wonder:
13923 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13927 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13930 case SAMPLE_exit_open:
13931 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13934 case SAMPLE_exit_leave:
13935 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13938 case SAMPLE_dynamite:
13939 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13943 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13947 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13951 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13955 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13959 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13963 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13967 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13973 void ChangeTime(int value)
13975 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13979 /* EMC game engine uses value from time counter of RND game engine */
13980 level.native_em_level->lev->time = *time;
13982 DrawGameValue_Time(*time);
13985 void RaiseScore(int value)
13987 /* EMC game engine and RND game engine have separate score counters */
13988 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13989 &level.native_em_level->lev->score : &local_player->score);
13993 DrawGameValue_Score(*score);
13997 void RaiseScore(int value)
13999 local_player->score += value;
14001 DrawGameValue_Score(local_player->score);
14004 void RaiseScoreElement(int element)
14009 case EL_BD_DIAMOND:
14010 case EL_EMERALD_YELLOW:
14011 case EL_EMERALD_RED:
14012 case EL_EMERALD_PURPLE:
14013 case EL_SP_INFOTRON:
14014 RaiseScore(level.score[SC_EMERALD]);
14017 RaiseScore(level.score[SC_DIAMOND]);
14020 RaiseScore(level.score[SC_CRYSTAL]);
14023 RaiseScore(level.score[SC_PEARL]);
14026 case EL_BD_BUTTERFLY:
14027 case EL_SP_ELECTRON:
14028 RaiseScore(level.score[SC_BUG]);
14031 case EL_BD_FIREFLY:
14032 case EL_SP_SNIKSNAK:
14033 RaiseScore(level.score[SC_SPACESHIP]);
14036 case EL_DARK_YAMYAM:
14037 RaiseScore(level.score[SC_YAMYAM]);
14040 RaiseScore(level.score[SC_ROBOT]);
14043 RaiseScore(level.score[SC_PACMAN]);
14046 RaiseScore(level.score[SC_NUT]);
14049 case EL_EM_DYNAMITE:
14050 case EL_SP_DISK_RED:
14051 case EL_DYNABOMB_INCREASE_NUMBER:
14052 case EL_DYNABOMB_INCREASE_SIZE:
14053 case EL_DYNABOMB_INCREASE_POWER:
14054 RaiseScore(level.score[SC_DYNAMITE]);
14056 case EL_SHIELD_NORMAL:
14057 case EL_SHIELD_DEADLY:
14058 RaiseScore(level.score[SC_SHIELD]);
14060 case EL_EXTRA_TIME:
14061 RaiseScore(level.extra_time_score);
14075 case EL_DC_KEY_WHITE:
14076 RaiseScore(level.score[SC_KEY]);
14079 RaiseScore(element_info[element].collect_score);
14084 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14086 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14088 #if defined(NETWORK_AVALIABLE)
14089 if (options.network)
14090 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14096 game_status = GAME_MODE_MAIN;
14102 FadeOut(REDRAW_FIELD);
14104 game_status = GAME_MODE_MAIN;
14106 DrawAndFadeInMainMenu(REDRAW_FIELD);
14110 else /* continue playing the game */
14112 if (tape.playing && tape.deactivate_display)
14113 TapeDeactivateDisplayOff(TRUE);
14115 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14117 if (tape.playing && tape.deactivate_display)
14118 TapeDeactivateDisplayOn();
14122 void RequestQuitGame(boolean ask_if_really_quit)
14124 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14125 boolean skip_request = AllPlayersGone || quick_quit;
14127 RequestQuitGameExt(skip_request, quick_quit,
14128 "Do you really want to quit the game ?");
14132 /* ------------------------------------------------------------------------- */
14133 /* random generator functions */
14134 /* ------------------------------------------------------------------------- */
14136 unsigned int InitEngineRandom_RND(long seed)
14138 game.num_random_calls = 0;
14141 unsigned int rnd_seed = InitEngineRandom(seed);
14143 printf("::: START RND: %d\n", rnd_seed);
14148 return InitEngineRandom(seed);
14154 unsigned int RND(int max)
14158 game.num_random_calls++;
14160 return GetEngineRandom(max);
14167 /* ------------------------------------------------------------------------- */
14168 /* game engine snapshot handling functions */
14169 /* ------------------------------------------------------------------------- */
14171 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14173 struct EngineSnapshotInfo
14175 /* runtime values for custom element collect score */
14176 int collect_score[NUM_CUSTOM_ELEMENTS];
14178 /* runtime values for group element choice position */
14179 int choice_pos[NUM_GROUP_ELEMENTS];
14181 /* runtime values for belt position animations */
14182 int belt_graphic[4 * NUM_BELT_PARTS];
14183 int belt_anim_mode[4 * NUM_BELT_PARTS];
14186 struct EngineSnapshotNodeInfo
14193 static struct EngineSnapshotInfo engine_snapshot_rnd;
14194 static ListNode *engine_snapshot_list = NULL;
14195 static char *snapshot_level_identifier = NULL;
14196 static int snapshot_level_nr = -1;
14198 void FreeEngineSnapshot()
14200 while (engine_snapshot_list != NULL)
14201 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14204 setString(&snapshot_level_identifier, NULL);
14205 snapshot_level_nr = -1;
14208 static void SaveEngineSnapshotValues_RND()
14210 static int belt_base_active_element[4] =
14212 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14213 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14214 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14215 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14219 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14221 int element = EL_CUSTOM_START + i;
14223 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14226 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14228 int element = EL_GROUP_START + i;
14230 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14233 for (i = 0; i < 4; i++)
14235 for (j = 0; j < NUM_BELT_PARTS; j++)
14237 int element = belt_base_active_element[i] + j;
14238 int graphic = el2img(element);
14239 int anim_mode = graphic_info[graphic].anim_mode;
14241 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14242 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14247 static void LoadEngineSnapshotValues_RND()
14249 unsigned long num_random_calls = game.num_random_calls;
14252 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14254 int element = EL_CUSTOM_START + i;
14256 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14259 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14261 int element = EL_GROUP_START + i;
14263 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14266 for (i = 0; i < 4; i++)
14268 for (j = 0; j < NUM_BELT_PARTS; j++)
14270 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14271 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14273 graphic_info[graphic].anim_mode = anim_mode;
14277 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14279 InitRND(tape.random_seed);
14280 for (i = 0; i < num_random_calls; i++)
14284 if (game.num_random_calls != num_random_calls)
14286 Error(ERR_INFO, "number of random calls out of sync");
14287 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14288 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14289 Error(ERR_EXIT, "this should not happen -- please debug");
14293 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14295 struct EngineSnapshotNodeInfo *bi =
14296 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14298 bi->buffer_orig = buffer;
14299 bi->buffer_copy = checked_malloc(size);
14302 memcpy(bi->buffer_copy, buffer, size);
14304 addNodeToList(&engine_snapshot_list, NULL, bi);
14307 void SaveEngineSnapshot()
14309 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14311 if (level_editor_test_game) /* do not save snapshots from editor */
14314 /* copy some special values to a structure better suited for the snapshot */
14316 SaveEngineSnapshotValues_RND();
14317 SaveEngineSnapshotValues_EM();
14319 /* save values stored in special snapshot structure */
14321 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14322 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14324 /* save further RND engine values */
14326 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14327 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14328 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14330 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14331 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14332 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14333 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14335 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14336 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14337 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14338 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14339 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14341 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14342 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14343 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14345 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14347 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14349 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14350 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14352 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14353 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14354 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14355 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14356 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14357 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14358 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14359 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14360 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14361 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14362 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14363 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14364 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14365 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14366 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14367 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14368 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14369 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14371 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14372 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14374 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14375 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14376 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14378 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14379 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14381 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14382 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14383 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14384 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14385 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14387 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14388 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14390 /* save level identification information */
14392 setString(&snapshot_level_identifier, leveldir_current->identifier);
14393 snapshot_level_nr = level_nr;
14396 ListNode *node = engine_snapshot_list;
14399 while (node != NULL)
14401 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14406 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14410 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14412 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14415 void LoadEngineSnapshot()
14417 ListNode *node = engine_snapshot_list;
14419 if (engine_snapshot_list == NULL)
14422 while (node != NULL)
14424 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14429 /* restore special values from snapshot structure */
14431 LoadEngineSnapshotValues_RND();
14432 LoadEngineSnapshotValues_EM();
14435 boolean CheckEngineSnapshot()
14437 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14438 snapshot_level_nr == level_nr);
14442 /* ---------- new game button stuff ---------------------------------------- */
14444 /* graphic position values for game buttons */
14445 #define GAME_BUTTON_XSIZE 30
14446 #define GAME_BUTTON_YSIZE 30
14447 #define GAME_BUTTON_XPOS 5
14448 #define GAME_BUTTON_YPOS 215
14449 #define SOUND_BUTTON_XPOS 5
14450 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14452 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14453 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14454 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14455 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14456 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14457 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14465 } gamebutton_info[NUM_GAME_BUTTONS] =
14469 &game.button.stop.x, &game.button.stop.y,
14470 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14475 &game.button.pause.x, &game.button.pause.y,
14476 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14477 GAME_CTRL_ID_PAUSE,
14481 &game.button.play.x, &game.button.play.y,
14482 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14487 &game.button.sound_music.x, &game.button.sound_music.y,
14488 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14489 SOUND_CTRL_ID_MUSIC,
14490 "background music on/off"
14493 &game.button.sound_loops.x, &game.button.sound_loops.y,
14494 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
14495 SOUND_CTRL_ID_LOOPS,
14496 "sound loops on/off"
14499 &game.button.sound_simple.x,&game.button.sound_simple.y,
14500 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
14501 SOUND_CTRL_ID_SIMPLE,
14502 "normal sounds on/off"
14506 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14511 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14512 GAME_CTRL_ID_PAUSE,
14516 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14521 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14522 SOUND_CTRL_ID_MUSIC,
14523 "background music on/off"
14526 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
14527 SOUND_CTRL_ID_LOOPS,
14528 "sound loops on/off"
14531 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
14532 SOUND_CTRL_ID_SIMPLE,
14533 "normal sounds on/off"
14538 void CreateGameButtons()
14542 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14544 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14545 struct GadgetInfo *gi;
14548 unsigned long event_mask;
14550 int gd_xoffset, gd_yoffset;
14551 int gd_x1, gd_x2, gd_y1, gd_y2;
14554 x = DX + *gamebutton_info[i].x;
14555 y = DY + *gamebutton_info[i].y;
14556 gd_xoffset = gamebutton_info[i].gd_x;
14557 gd_yoffset = gamebutton_info[i].gd_y;
14558 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14559 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14561 if (id == GAME_CTRL_ID_STOP ||
14562 id == GAME_CTRL_ID_PAUSE ||
14563 id == GAME_CTRL_ID_PLAY)
14565 button_type = GD_TYPE_NORMAL_BUTTON;
14567 event_mask = GD_EVENT_RELEASED;
14568 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14569 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14573 button_type = GD_TYPE_CHECK_BUTTON;
14575 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14576 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14577 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14578 event_mask = GD_EVENT_PRESSED;
14579 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
14580 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14583 gi = CreateGadget(GDI_CUSTOM_ID, id,
14584 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14589 GDI_X, DX + gd_xoffset,
14590 GDI_Y, DY + gd_yoffset,
14592 GDI_WIDTH, GAME_BUTTON_XSIZE,
14593 GDI_HEIGHT, GAME_BUTTON_YSIZE,
14594 GDI_TYPE, button_type,
14595 GDI_STATE, GD_BUTTON_UNPRESSED,
14596 GDI_CHECKED, checked,
14597 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14598 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14599 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14600 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14601 GDI_EVENT_MASK, event_mask,
14602 GDI_CALLBACK_ACTION, HandleGameButtons,
14606 Error(ERR_EXIT, "cannot create gadget");
14608 game_gadget[id] = gi;
14612 void FreeGameButtons()
14616 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14617 FreeGadget(game_gadget[i]);
14620 static void MapGameButtons()
14624 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14625 MapGadget(game_gadget[i]);
14628 void UnmapGameButtons()
14632 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14633 UnmapGadget(game_gadget[i]);
14636 static void HandleGameButtons(struct GadgetInfo *gi)
14638 int id = gi->custom_id;
14640 if (game_status != GAME_MODE_PLAYING)
14645 case GAME_CTRL_ID_STOP:
14649 RequestQuitGame(TRUE);
14652 case GAME_CTRL_ID_PAUSE:
14653 if (options.network)
14655 #if defined(NETWORK_AVALIABLE)
14657 SendToServer_ContinuePlaying();
14659 SendToServer_PausePlaying();
14663 TapeTogglePause(TAPE_TOGGLE_MANUAL);
14666 case GAME_CTRL_ID_PLAY:
14669 #if defined(NETWORK_AVALIABLE)
14670 if (options.network)
14671 SendToServer_ContinuePlaying();
14675 tape.pausing = FALSE;
14676 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14681 case SOUND_CTRL_ID_MUSIC:
14682 if (setup.sound_music)
14684 setup.sound_music = FALSE;
14687 else if (audio.music_available)
14689 setup.sound = setup.sound_music = TRUE;
14691 SetAudioMode(setup.sound);
14697 case SOUND_CTRL_ID_LOOPS:
14698 if (setup.sound_loops)
14699 setup.sound_loops = FALSE;
14700 else if (audio.loops_available)
14702 setup.sound = setup.sound_loops = TRUE;
14703 SetAudioMode(setup.sound);
14707 case SOUND_CTRL_ID_SIMPLE:
14708 if (setup.sound_simple)
14709 setup.sound_simple = FALSE;
14710 else if (audio.sound_available)
14712 setup.sound = setup.sound_simple = TRUE;
14713 SetAudioMode(setup.sound);