1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
90 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME (PANEL_XPOS(game.panel.time))
111 #define YY_TIME (PANEL_YPOS(game.panel.time))
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1 (DX + XX_LEVEL1)
115 #define DX_LEVEL2 (DX + XX_LEVEL2)
116 #define DX_LEVEL (DX + XX_LEVEL)
117 #define DY_LEVEL (DY + YY_LEVEL)
118 #define DX_EMERALDS (DX + XX_EMERALDS)
119 #define DY_EMERALDS (DY + YY_EMERALDS)
120 #define DX_DYNAMITE (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE (DY + YY_DYNAMITE)
122 #define DX_KEYS (DX + XX_KEYS)
123 #define DY_KEYS (DY + YY_KEYS)
124 #define DX_SCORE (DX + XX_SCORE)
125 #define DY_SCORE (DY + YY_SCORE)
126 #define DX_TIME1 (DX + XX_TIME1)
127 #define DX_TIME2 (DX + XX_TIME2)
128 #define DX_TIME (DX + XX_TIME)
129 #define DY_TIME (DY + YY_TIME)
132 /* game panel display and control definitions */
134 #define GAME_CONTROL_LEVEL_NUMBER 0
135 #define GAME_CONTROL_GEMS 1
136 #define GAME_CONTROL_INVENTORY 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_SWITCH 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 #define NUM_GAME_CONTROLS 69
206 int game_control_value[NUM_GAME_CONTROLS];
207 int last_game_control_value[NUM_GAME_CONTROLS];
209 struct GameControlInfo
213 struct TextPosInfo *pos;
217 static struct GameControlInfo game_controls[] =
220 GAME_CONTROL_LEVEL_NUMBER,
221 &game.panel.level_number,
230 GAME_CONTROL_INVENTORY,
231 &game.panel.inventory,
275 GAME_CONTROL_KEY_WHITE,
276 &game.panel.key_white,
280 GAME_CONTROL_KEY_WHITE_COUNT,
281 &game.panel.key_white_count,
295 GAME_CONTROL_TIME_HH,
300 GAME_CONTROL_TIME_MM,
305 GAME_CONTROL_TIME_SS,
310 GAME_CONTROL_DROP_NEXT_1,
311 &game.panel.drop_next_1,
315 GAME_CONTROL_DROP_NEXT_2,
316 &game.panel.drop_next_2,
320 GAME_CONTROL_DROP_NEXT_3,
321 &game.panel.drop_next_3,
325 GAME_CONTROL_DROP_NEXT_4,
326 &game.panel.drop_next_4,
330 GAME_CONTROL_DROP_NEXT_5,
331 &game.panel.drop_next_5,
335 GAME_CONTROL_DROP_NEXT_6,
336 &game.panel.drop_next_6,
340 GAME_CONTROL_DROP_NEXT_7,
341 &game.panel.drop_next_7,
345 GAME_CONTROL_DROP_NEXT_8,
346 &game.panel.drop_next_8,
350 GAME_CONTROL_SHIELD_NORMAL,
351 &game.panel.shield_normal,
355 GAME_CONTROL_SHIELD_NORMAL_TIME,
356 &game.panel.shield_normal_time,
360 GAME_CONTROL_SHIELD_DEADLY,
361 &game.panel.shield_deadly,
365 GAME_CONTROL_SHIELD_DEADLY_TIME,
366 &game.panel.shield_deadly_time,
375 GAME_CONTROL_EM_EXIT,
380 GAME_CONTROL_SP_EXIT,
385 GAME_CONTROL_STEEL_EXIT,
386 &game.panel.steel_exit,
390 GAME_CONTROL_EM_STEEL_EXIT,
391 &game.panel.em_steel_exit,
395 GAME_CONTROL_EMC_MAGIC_BALL,
396 &game.panel.emc_magic_ball,
400 GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
401 &game.panel.emc_magic_ball_switch,
405 GAME_CONTROL_LIGHT_SWITCH,
406 &game.panel.light_switch,
410 GAME_CONTROL_LIGHT_SWITCH_TIME,
411 &game.panel.light_switch_time,
415 GAME_CONTROL_TIMEGATE_SWITCH,
416 &game.panel.timegate_switch,
420 GAME_CONTROL_TIMEGATE_SWITCH_TIME,
421 &game.panel.timegate_switch_time,
425 GAME_CONTROL_SWITCHGATE_SWITCH,
426 &game.panel.switchgate_switch,
430 GAME_CONTROL_EMC_LENSES,
431 &game.panel.emc_lenses,
435 GAME_CONTROL_EMC_LENSES_TIME,
436 &game.panel.emc_lenses_time,
440 GAME_CONTROL_EMC_MAGNIFIER,
441 &game.panel.emc_magnifier,
445 GAME_CONTROL_EMC_MAGNIFIER_TIME,
446 &game.panel.emc_magnifier_time,
450 GAME_CONTROL_BALLOON_SWITCH,
451 &game.panel.balloon_switch,
455 GAME_CONTROL_DYNABOMB_NUMBER,
456 &game.panel.dynabomb_number,
460 GAME_CONTROL_DYNABOMB_SIZE,
461 &game.panel.dynabomb_size,
465 GAME_CONTROL_DYNABOMB_POWER,
466 &game.panel.dynabomb_power,
470 GAME_CONTROL_PENGUINS,
471 &game.panel.penguins,
475 GAME_CONTROL_SOKOBAN_OBJECTS,
476 &game.panel.sokoban_objects,
480 GAME_CONTROL_SOKOBAN_FIELDS,
481 &game.panel.sokoban_fields,
485 GAME_CONTROL_ROBOT_WHEEL,
486 &game.panel.robot_wheel,
490 GAME_CONTROL_CONVEYOR_BELT_1,
491 &game.panel.conveyor_belt_1,
495 GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
496 &game.panel.conveyor_belt_1_switch,
500 GAME_CONTROL_CONVEYOR_BELT_2,
501 &game.panel.conveyor_belt_2,
505 GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
506 &game.panel.conveyor_belt_2_switch,
510 GAME_CONTROL_CONVEYOR_BELT_3,
511 &game.panel.conveyor_belt_3,
515 GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
516 &game.panel.conveyor_belt_3_switch,
520 GAME_CONTROL_CONVEYOR_BELT_4,
521 &game.panel.conveyor_belt_4,
525 GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
526 &game.panel.conveyor_belt_4_switch,
530 GAME_CONTROL_MAGIC_WALL,
531 &game.panel.magic_wall,
535 GAME_CONTROL_MAGIC_WALL_TIME,
536 &game.panel.magic_wall_time,
540 GAME_CONTROL_BD_MAGIC_WALL,
541 &game.panel.bd_magic_wall,
545 GAME_CONTROL_DC_MAGIC_WALL,
546 &game.panel.dc_magic_wall,
550 GAME_CONTROL_PLAYER_NAME,
551 &game.panel.player_name,
555 GAME_CONTROL_LEVEL_NAME,
556 &game.panel.level_name,
560 GAME_CONTROL_LEVEL_AUTHOR,
561 &game.panel.level_author,
574 /* values for delayed check of falling and moving elements and for collision */
575 #define CHECK_DELAY_MOVING 3
576 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
577 #define CHECK_DELAY_COLLISION 2
578 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
580 /* values for initial player move delay (initial delay counter value) */
581 #define INITIAL_MOVE_DELAY_OFF -1
582 #define INITIAL_MOVE_DELAY_ON 0
584 /* values for player movement speed (which is in fact a delay value) */
585 #define MOVE_DELAY_MIN_SPEED 32
586 #define MOVE_DELAY_NORMAL_SPEED 8
587 #define MOVE_DELAY_HIGH_SPEED 4
588 #define MOVE_DELAY_MAX_SPEED 1
590 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
591 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
593 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
594 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
596 /* values for other actions */
597 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
598 #define MOVE_STEPSIZE_MIN (1)
599 #define MOVE_STEPSIZE_MAX (TILEX)
601 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
602 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
604 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
606 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
607 RND(element_info[e].push_delay_random))
608 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
609 RND(element_info[e].drop_delay_random))
610 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
611 RND(element_info[e].move_delay_random))
612 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
613 (element_info[e].move_delay_random))
614 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
615 RND(element_info[e].ce_value_random_initial))
616 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
617 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
618 RND((c)->delay_random * (c)->delay_frames))
619 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
620 RND((c)->delay_random))
623 #define GET_VALID_RUNTIME_ELEMENT(e) \
624 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
626 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
627 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
628 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
629 (be) + (e) - EL_SELF)
631 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
632 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
633 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
634 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
635 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
636 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
637 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
638 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
639 RESOLVED_REFERENCE_ELEMENT(be, e) : \
642 #define CAN_GROW_INTO(e) \
643 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
645 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
646 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
649 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
650 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
651 (CAN_MOVE_INTO_ACID(e) && \
652 Feld[x][y] == EL_ACID) || \
655 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
656 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
657 (CAN_MOVE_INTO_ACID(e) && \
658 Feld[x][y] == EL_ACID) || \
661 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
662 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
664 (CAN_MOVE_INTO_ACID(e) && \
665 Feld[x][y] == EL_ACID) || \
666 (DONT_COLLIDE_WITH(e) && \
668 !PLAYER_ENEMY_PROTECTED(x, y))))
670 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
671 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
673 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
674 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
676 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
677 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
679 #define ANDROID_CAN_CLONE_FIELD(x, y) \
680 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
681 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
683 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
684 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
686 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
687 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
689 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
690 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
692 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
693 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
695 #define PIG_CAN_ENTER_FIELD(e, x, y) \
696 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
698 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
699 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
700 Feld[x][y] == EL_EM_EXIT_OPEN || \
701 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
702 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
703 IS_FOOD_PENGUIN(Feld[x][y])))
704 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
705 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
707 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
708 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
710 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
711 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
713 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
714 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
715 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
717 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
719 #define CE_ENTER_FIELD_COND(e, x, y) \
720 (!IS_PLAYER(x, y) && \
721 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
723 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
724 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
726 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
727 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
729 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
730 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
731 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
732 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
734 /* game button identifiers */
735 #define GAME_CTRL_ID_STOP 0
736 #define GAME_CTRL_ID_PAUSE 1
737 #define GAME_CTRL_ID_PLAY 2
738 #define SOUND_CTRL_ID_MUSIC 3
739 #define SOUND_CTRL_ID_LOOPS 4
740 #define SOUND_CTRL_ID_SIMPLE 5
742 #define NUM_GAME_BUTTONS 6
745 /* forward declaration for internal use */
747 static void CreateField(int, int, int);
749 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
750 static void AdvanceFrameAndPlayerCounters(int);
752 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
753 static boolean MovePlayer(struct PlayerInfo *, int, int);
754 static void ScrollPlayer(struct PlayerInfo *, int);
755 static void ScrollScreen(struct PlayerInfo *, int);
757 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
759 static void InitBeltMovement(void);
760 static void CloseAllOpenTimegates(void);
761 static void CheckGravityMovement(struct PlayerInfo *);
762 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
763 static void KillPlayerUnlessEnemyProtected(int, int);
764 static void KillPlayerUnlessExplosionProtected(int, int);
766 static void TestIfPlayerTouchesCustomElement(int, int);
767 static void TestIfElementTouchesCustomElement(int, int);
768 static void TestIfElementHitsCustomElement(int, int, int);
770 static void TestIfElementSmashesCustomElement(int, int, int);
773 static void HandleElementChange(int, int, int);
774 static void ExecuteCustomElementAction(int, int, int, int);
775 static boolean ChangeElement(int, int, int, int);
777 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
778 #define CheckTriggeredElementChange(x, y, e, ev) \
779 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
780 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
781 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
782 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
783 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
784 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
785 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
787 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
788 #define CheckElementChange(x, y, e, te, ev) \
789 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
790 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
791 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
792 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
793 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
795 static void PlayLevelSound(int, int, int);
796 static void PlayLevelSoundNearest(int, int, int);
797 static void PlayLevelSoundAction(int, int, int);
798 static void PlayLevelSoundElementAction(int, int, int, int);
799 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
800 static void PlayLevelSoundActionIfLoop(int, int, int);
801 static void StopLevelSoundActionIfLoop(int, int, int);
802 static void PlayLevelMusic();
804 static void MapGameButtons();
805 static void HandleGameButtons(struct GadgetInfo *);
807 int AmoebeNachbarNr(int, int);
808 void AmoebeUmwandeln(int, int);
809 void ContinueMoving(int, int);
811 void InitMovDir(int, int);
812 void InitAmoebaNr(int, int);
813 int NewHiScore(void);
815 void TestIfGoodThingHitsBadThing(int, int, int);
816 void TestIfBadThingHitsGoodThing(int, int, int);
817 void TestIfPlayerTouchesBadThing(int, int);
818 void TestIfPlayerRunsIntoBadThing(int, int, int);
819 void TestIfBadThingTouchesPlayer(int, int);
820 void TestIfBadThingRunsIntoPlayer(int, int, int);
821 void TestIfFriendTouchesBadThing(int, int);
822 void TestIfBadThingTouchesFriend(int, int);
823 void TestIfBadThingTouchesOtherBadThing(int, int);
825 void KillPlayer(struct PlayerInfo *);
826 void BuryPlayer(struct PlayerInfo *);
827 void RemovePlayer(struct PlayerInfo *);
829 boolean SnapField(struct PlayerInfo *, int, int);
830 boolean DropElement(struct PlayerInfo *);
832 static int getInvisibleActiveFromInvisibleElement(int);
833 static int getInvisibleFromInvisibleActiveElement(int);
835 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
837 /* for detection of endless loops, caused by custom element programming */
838 /* (using maximal playfield width x 10 is just a rough approximation) */
839 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
841 #define RECURSION_LOOP_DETECTION_START(e, rc) \
843 if (recursion_loop_detected) \
846 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
848 recursion_loop_detected = TRUE; \
849 recursion_loop_element = (e); \
852 recursion_loop_depth++; \
855 #define RECURSION_LOOP_DETECTION_END() \
857 recursion_loop_depth--; \
860 static int recursion_loop_depth;
861 static boolean recursion_loop_detected;
862 static boolean recursion_loop_element;
865 /* ------------------------------------------------------------------------- */
866 /* definition of elements that automatically change to other elements after */
867 /* a specified time, eventually calling a function when changing */
868 /* ------------------------------------------------------------------------- */
870 /* forward declaration for changer functions */
871 static void InitBuggyBase(int, int);
872 static void WarnBuggyBase(int, int);
874 static void InitTrap(int, int);
875 static void ActivateTrap(int, int);
876 static void ChangeActiveTrap(int, int);
878 static void InitRobotWheel(int, int);
879 static void RunRobotWheel(int, int);
880 static void StopRobotWheel(int, int);
882 static void InitTimegateWheel(int, int);
883 static void RunTimegateWheel(int, int);
885 static void InitMagicBallDelay(int, int);
886 static void ActivateMagicBall(int, int);
888 struct ChangingElementInfo
893 void (*pre_change_function)(int x, int y);
894 void (*change_function)(int x, int y);
895 void (*post_change_function)(int x, int y);
898 static struct ChangingElementInfo change_delay_list[] =
933 EL_STEEL_EXIT_OPENING,
941 EL_STEEL_EXIT_CLOSING,
942 EL_STEEL_EXIT_CLOSED,
969 EL_EM_STEEL_EXIT_OPENING,
970 EL_EM_STEEL_EXIT_OPEN,
977 EL_EM_STEEL_EXIT_CLOSING,
981 EL_EM_STEEL_EXIT_CLOSED,
1005 EL_SWITCHGATE_OPENING,
1013 EL_SWITCHGATE_CLOSING,
1014 EL_SWITCHGATE_CLOSED,
1021 EL_TIMEGATE_OPENING,
1029 EL_TIMEGATE_CLOSING,
1038 EL_ACID_SPLASH_LEFT,
1046 EL_ACID_SPLASH_RIGHT,
1055 EL_SP_BUGGY_BASE_ACTIVATING,
1062 EL_SP_BUGGY_BASE_ACTIVATING,
1063 EL_SP_BUGGY_BASE_ACTIVE,
1070 EL_SP_BUGGY_BASE_ACTIVE,
1094 EL_ROBOT_WHEEL_ACTIVE,
1102 EL_TIMEGATE_SWITCH_ACTIVE,
1110 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1111 EL_DC_TIMEGATE_SWITCH,
1118 EL_EMC_MAGIC_BALL_ACTIVE,
1119 EL_EMC_MAGIC_BALL_ACTIVE,
1126 EL_EMC_SPRING_BUMPER_ACTIVE,
1127 EL_EMC_SPRING_BUMPER,
1134 EL_DIAGONAL_SHRINKING,
1142 EL_DIAGONAL_GROWING,
1163 int push_delay_fixed, push_delay_random;
1167 { EL_SPRING, 0, 0 },
1168 { EL_BALLOON, 0, 0 },
1170 { EL_SOKOBAN_OBJECT, 2, 0 },
1171 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1172 { EL_SATELLITE, 2, 0 },
1173 { EL_SP_DISK_YELLOW, 2, 0 },
1175 { EL_UNDEFINED, 0, 0 },
1183 move_stepsize_list[] =
1185 { EL_AMOEBA_DROP, 2 },
1186 { EL_AMOEBA_DROPPING, 2 },
1187 { EL_QUICKSAND_FILLING, 1 },
1188 { EL_QUICKSAND_EMPTYING, 1 },
1189 { EL_QUICKSAND_FAST_FILLING, 2 },
1190 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1191 { EL_MAGIC_WALL_FILLING, 2 },
1192 { EL_MAGIC_WALL_EMPTYING, 2 },
1193 { EL_BD_MAGIC_WALL_FILLING, 2 },
1194 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1195 { EL_DC_MAGIC_WALL_FILLING, 2 },
1196 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1198 { EL_UNDEFINED, 0 },
1206 collect_count_list[] =
1209 { EL_BD_DIAMOND, 1 },
1210 { EL_EMERALD_YELLOW, 1 },
1211 { EL_EMERALD_RED, 1 },
1212 { EL_EMERALD_PURPLE, 1 },
1214 { EL_SP_INFOTRON, 1 },
1218 { EL_UNDEFINED, 0 },
1226 access_direction_list[] =
1228 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1229 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1230 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1231 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1232 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1233 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1234 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1235 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1236 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1237 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1238 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1240 { EL_SP_PORT_LEFT, MV_RIGHT },
1241 { EL_SP_PORT_RIGHT, MV_LEFT },
1242 { EL_SP_PORT_UP, MV_DOWN },
1243 { EL_SP_PORT_DOWN, MV_UP },
1244 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1245 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1246 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1247 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1248 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1249 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1250 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1251 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1252 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1253 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1254 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1255 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1256 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1257 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1258 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1260 { EL_UNDEFINED, MV_NONE }
1263 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1265 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1266 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1267 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1268 IS_JUST_CHANGING(x, y))
1270 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1272 /* static variables for playfield scan mode (scanning forward or backward) */
1273 static int playfield_scan_start_x = 0;
1274 static int playfield_scan_start_y = 0;
1275 static int playfield_scan_delta_x = 1;
1276 static int playfield_scan_delta_y = 1;
1278 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1279 (y) >= 0 && (y) <= lev_fieldy - 1; \
1280 (y) += playfield_scan_delta_y) \
1281 for ((x) = playfield_scan_start_x; \
1282 (x) >= 0 && (x) <= lev_fieldx - 1; \
1283 (x) += playfield_scan_delta_x)
1286 void DEBUG_SetMaximumDynamite()
1290 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1291 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1292 local_player->inventory_element[local_player->inventory_size++] =
1297 static void InitPlayfieldScanModeVars()
1299 if (game.use_reverse_scan_direction)
1301 playfield_scan_start_x = lev_fieldx - 1;
1302 playfield_scan_start_y = lev_fieldy - 1;
1304 playfield_scan_delta_x = -1;
1305 playfield_scan_delta_y = -1;
1309 playfield_scan_start_x = 0;
1310 playfield_scan_start_y = 0;
1312 playfield_scan_delta_x = 1;
1313 playfield_scan_delta_y = 1;
1317 static void InitPlayfieldScanMode(int mode)
1319 game.use_reverse_scan_direction =
1320 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1322 InitPlayfieldScanModeVars();
1325 static int get_move_delay_from_stepsize(int move_stepsize)
1328 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1330 /* make sure that stepsize value is always a power of 2 */
1331 move_stepsize = (1 << log_2(move_stepsize));
1333 return TILEX / move_stepsize;
1336 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1339 int player_nr = player->index_nr;
1340 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1341 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1343 /* do no immediately change move delay -- the player might just be moving */
1344 player->move_delay_value_next = move_delay;
1346 /* information if player can move must be set separately */
1347 player->cannot_move = cannot_move;
1351 player->move_delay = game.initial_move_delay[player_nr];
1352 player->move_delay_value = game.initial_move_delay_value[player_nr];
1354 player->move_delay_value_next = -1;
1356 player->move_delay_reset_counter = 0;
1360 void GetPlayerConfig()
1362 GameFrameDelay = setup.game_frame_delay;
1364 if (!audio.sound_available)
1365 setup.sound_simple = FALSE;
1367 if (!audio.loops_available)
1368 setup.sound_loops = FALSE;
1370 if (!audio.music_available)
1371 setup.sound_music = FALSE;
1373 if (!video.fullscreen_available)
1374 setup.fullscreen = FALSE;
1376 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1378 SetAudioMode(setup.sound);
1382 int GetElementFromGroupElement(int element)
1384 if (IS_GROUP_ELEMENT(element))
1386 struct ElementGroupInfo *group = element_info[element].group;
1387 int last_anim_random_frame = gfx.anim_random_frame;
1390 if (group->choice_mode == ANIM_RANDOM)
1391 gfx.anim_random_frame = RND(group->num_elements_resolved);
1393 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1394 group->choice_mode, 0,
1397 if (group->choice_mode == ANIM_RANDOM)
1398 gfx.anim_random_frame = last_anim_random_frame;
1400 group->choice_pos++;
1402 element = group->element_resolved[element_pos];
1408 static void InitPlayerField(int x, int y, int element, boolean init_game)
1410 if (element == EL_SP_MURPHY)
1414 if (stored_player[0].present)
1416 Feld[x][y] = EL_SP_MURPHY_CLONE;
1422 stored_player[0].use_murphy = TRUE;
1424 if (!level.use_artwork_element[0])
1425 stored_player[0].artwork_element = EL_SP_MURPHY;
1428 Feld[x][y] = EL_PLAYER_1;
1434 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1435 int jx = player->jx, jy = player->jy;
1437 player->present = TRUE;
1439 player->block_last_field = (element == EL_SP_MURPHY ?
1440 level.sp_block_last_field :
1441 level.block_last_field);
1443 /* ---------- initialize player's last field block delay --------------- */
1445 /* always start with reliable default value (no adjustment needed) */
1446 player->block_delay_adjustment = 0;
1448 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1449 if (player->block_last_field && element == EL_SP_MURPHY)
1450 player->block_delay_adjustment = 1;
1452 /* special case 2: in game engines before 3.1.1, blocking was different */
1453 if (game.use_block_last_field_bug)
1454 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1456 if (!options.network || player->connected)
1458 player->active = TRUE;
1460 /* remove potentially duplicate players */
1461 if (StorePlayer[jx][jy] == Feld[x][y])
1462 StorePlayer[jx][jy] = 0;
1464 StorePlayer[x][y] = Feld[x][y];
1468 printf("Player %d activated.\n", player->element_nr);
1469 printf("[Local player is %d and currently %s.]\n",
1470 local_player->element_nr,
1471 local_player->active ? "active" : "not active");
1475 Feld[x][y] = EL_EMPTY;
1477 player->jx = player->last_jx = x;
1478 player->jy = player->last_jy = y;
1482 static void InitField(int x, int y, boolean init_game)
1484 int element = Feld[x][y];
1493 InitPlayerField(x, y, element, init_game);
1496 case EL_SOKOBAN_FIELD_PLAYER:
1497 element = Feld[x][y] = EL_PLAYER_1;
1498 InitField(x, y, init_game);
1500 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1501 InitField(x, y, init_game);
1504 case EL_SOKOBAN_FIELD_EMPTY:
1505 local_player->sokobanfields_still_needed++;
1509 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1510 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1511 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1512 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1513 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1514 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1515 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1516 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1517 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1518 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1527 case EL_SPACESHIP_RIGHT:
1528 case EL_SPACESHIP_UP:
1529 case EL_SPACESHIP_LEFT:
1530 case EL_SPACESHIP_DOWN:
1531 case EL_BD_BUTTERFLY:
1532 case EL_BD_BUTTERFLY_RIGHT:
1533 case EL_BD_BUTTERFLY_UP:
1534 case EL_BD_BUTTERFLY_LEFT:
1535 case EL_BD_BUTTERFLY_DOWN:
1537 case EL_BD_FIREFLY_RIGHT:
1538 case EL_BD_FIREFLY_UP:
1539 case EL_BD_FIREFLY_LEFT:
1540 case EL_BD_FIREFLY_DOWN:
1541 case EL_PACMAN_RIGHT:
1543 case EL_PACMAN_LEFT:
1544 case EL_PACMAN_DOWN:
1546 case EL_YAMYAM_LEFT:
1547 case EL_YAMYAM_RIGHT:
1549 case EL_YAMYAM_DOWN:
1550 case EL_DARK_YAMYAM:
1553 case EL_SP_SNIKSNAK:
1554 case EL_SP_ELECTRON:
1563 case EL_AMOEBA_FULL:
1568 case EL_AMOEBA_DROP:
1569 if (y == lev_fieldy - 1)
1571 Feld[x][y] = EL_AMOEBA_GROWING;
1572 Store[x][y] = EL_AMOEBA_WET;
1576 case EL_DYNAMITE_ACTIVE:
1577 case EL_SP_DISK_RED_ACTIVE:
1578 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1579 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1580 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1581 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1582 MovDelay[x][y] = 96;
1585 case EL_EM_DYNAMITE_ACTIVE:
1586 MovDelay[x][y] = 32;
1590 local_player->lights_still_needed++;
1594 local_player->friends_still_needed++;
1599 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1602 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1603 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1604 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1605 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1606 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1607 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1608 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1609 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1610 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1611 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1612 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1613 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1616 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1617 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1618 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1620 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1622 game.belt_dir[belt_nr] = belt_dir;
1623 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1625 else /* more than one switch -- set it like the first switch */
1627 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1632 #if !USE_BOTH_SWITCHGATE_SWITCHES
1633 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1635 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1638 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1640 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1644 case EL_LIGHT_SWITCH_ACTIVE:
1646 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1649 case EL_INVISIBLE_STEELWALL:
1650 case EL_INVISIBLE_WALL:
1651 case EL_INVISIBLE_SAND:
1652 if (game.light_time_left > 0 ||
1653 game.lenses_time_left > 0)
1654 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1657 case EL_EMC_MAGIC_BALL:
1658 if (game.ball_state)
1659 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1662 case EL_EMC_MAGIC_BALL_SWITCH:
1663 if (game.ball_state)
1664 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1668 if (IS_CUSTOM_ELEMENT(element))
1670 if (CAN_MOVE(element))
1673 #if USE_NEW_CUSTOM_VALUE
1674 if (!element_info[element].use_last_ce_value || init_game)
1675 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1678 else if (IS_GROUP_ELEMENT(element))
1680 Feld[x][y] = GetElementFromGroupElement(element);
1682 InitField(x, y, init_game);
1689 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1692 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1694 InitField(x, y, init_game);
1696 /* not needed to call InitMovDir() -- already done by InitField()! */
1697 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1698 CAN_MOVE(Feld[x][y]))
1702 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1704 int old_element = Feld[x][y];
1706 InitField(x, y, init_game);
1708 /* not needed to call InitMovDir() -- already done by InitField()! */
1709 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1710 CAN_MOVE(old_element) &&
1711 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1714 /* this case is in fact a combination of not less than three bugs:
1715 first, it calls InitMovDir() for elements that can move, although this is
1716 already done by InitField(); then, it checks the element that was at this
1717 field _before_ the call to InitField() (which can change it); lastly, it
1718 was not called for "mole with direction" elements, which were treated as
1719 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1725 static int get_key_element_from_nr(int key_nr)
1727 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1728 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1729 EL_EM_KEY_1 : EL_KEY_1);
1731 return key_base_element + key_nr;
1734 void InitGameControlValues()
1738 for (i = 0; game_controls[i].nr != -1; i++)
1740 int nr = game_controls[i].nr;
1741 int type = game_controls[i].type;
1742 struct TextPosInfo *pos = game_controls[i].pos;
1744 /* force update of game controls after initialization */
1745 game_control_value[nr] = last_game_control_value[nr] = -1;
1747 /* determine panel value width for later calculation of alignment */
1748 if (type == TYPE_INTEGER || type == TYPE_STRING)
1749 pos->width = pos->chars * getFontWidth(pos->font);
1750 else if (type == TYPE_ELEMENT)
1751 pos->width = MINI_TILESIZE;
1755 void UpdateGameControlValues()
1759 game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1760 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1762 game_control_value[GAME_CONTROL_INVENTORY] = 0;
1763 for (i = 0; i < MAX_NUM_KEYS; i++)
1764 game_control_value[GAME_CONTROL_KEY_1 + i] = EL_EMPTY;
1765 game_control_value[GAME_CONTROL_KEY_WHITE] = EL_EMPTY;
1766 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1768 if (game.centered_player_nr == -1)
1770 for (i = 0; i < MAX_PLAYERS; i++)
1772 for (k = 0; k < MAX_NUM_KEYS; k++)
1773 if (stored_player[i].key[k])
1774 game_control_value[GAME_CONTROL_KEY_1 + k] =
1775 get_key_element_from_nr(k);
1777 game_control_value[GAME_CONTROL_INVENTORY] +=
1778 stored_player[i].inventory_size;
1780 if (stored_player[i].num_white_keys > 0)
1781 game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1783 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1784 stored_player[i].num_white_keys;
1789 int player_nr = game.centered_player_nr;
1791 for (k = 0; k < MAX_NUM_KEYS; k++)
1792 if (stored_player[player_nr].key[k])
1793 game_control_value[GAME_CONTROL_KEY_1 + k] =
1794 get_key_element_from_nr(k);
1796 game_control_value[GAME_CONTROL_INVENTORY] +=
1797 stored_player[player_nr].inventory_size;
1799 if (stored_player[player_nr].num_white_keys > 0)
1800 game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1802 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1803 stored_player[player_nr].num_white_keys;
1806 game_control_value[GAME_CONTROL_SCORE] = (local_player->LevelSolved ?
1807 local_player->score_final :
1808 local_player->score);
1810 game_control_value[GAME_CONTROL_TIME] = (level.time == 0 ?
1814 game_control_value[GAME_CONTROL_TIME_HH] = TapeTime / 3600;
1815 game_control_value[GAME_CONTROL_TIME_MM] = (TapeTime / 60) % 60;
1816 game_control_value[GAME_CONTROL_TIME_SS] = TapeTime % 60;
1819 for (i = 0; i < 8; i++)
1820 game_control_value[GAME_CONTROL_DROP_NEXT_1 + i] = EL_UNDEFINED;
1822 game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1823 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1825 game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1826 local_player->shield_normal_time_left;
1827 game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1828 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1830 game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1831 local_player->shield_deadly_time_left;
1833 if (local_player->gems_still_needed > 0 ||
1834 local_player->sokobanfields_still_needed > 0 ||
1835 local_player->lights_still_needed > 0)
1837 game_control_value[GAME_CONTROL_EXIT] = EL_EXIT_CLOSED;
1838 game_control_value[GAME_CONTROL_EM_EXIT] = EL_EM_EXIT_CLOSED;
1839 game_control_value[GAME_CONTROL_SP_EXIT] = EL_SP_EXIT_CLOSED;
1840 game_control_value[GAME_CONTROL_STEEL_EXIT] = EL_STEEL_EXIT_CLOSED;
1841 game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED;
1845 game_control_value[GAME_CONTROL_EXIT] = EL_EXIT_OPEN;
1846 game_control_value[GAME_CONTROL_EM_EXIT] = EL_EM_EXIT_OPEN;
1847 game_control_value[GAME_CONTROL_SP_EXIT] = EL_SP_EXIT_OPEN;
1848 game_control_value[GAME_CONTROL_STEEL_EXIT] = EL_STEEL_EXIT_OPEN;
1849 game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN;
1853 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED;
1854 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED;
1856 game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
1857 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1858 game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1860 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
1861 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1862 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1863 game.timegate_time_left;
1866 game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED;
1868 game_control_value[GAME_CONTROL_EMC_LENSES] =
1869 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1870 game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1872 game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
1873 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1874 game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1876 game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
1877 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
1878 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1879 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
1880 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
1881 EL_BALLOON_SWITCH_NONE);
1883 game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1884 local_player->dynabomb_count;
1885 game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1886 local_player->dynabomb_size;
1887 game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1888 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1890 game_control_value[GAME_CONTROL_PENGUINS] =
1891 local_player->friends_still_needed;
1893 game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1894 local_player->sokobanfields_still_needed;
1895 game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1896 local_player->sokobanfields_still_needed;
1899 game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
1902 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
1903 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
1904 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
1905 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
1906 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
1907 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
1908 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
1909 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
1912 game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED;
1913 game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
1914 game.magic_wall_time_left;
1915 game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED;
1916 game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED;
1918 game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
1919 game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
1920 game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
1923 void DisplayGameControlValues()
1927 for (i = 0; game_controls[i].nr != -1; i++)
1929 int nr = game_controls[i].nr;
1930 int type = game_controls[i].type;
1931 struct TextPosInfo *pos = game_controls[i].pos;
1932 int value = game_control_value[nr];
1933 int last_value = last_game_control_value[nr];
1934 int chars = pos->chars;
1935 int font = pos->font;
1937 if (value == last_value)
1940 last_game_control_value[nr] = value;
1943 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
1946 if (PANEL_DEACTIVATED(pos))
1949 if (type == TYPE_INTEGER)
1951 if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
1953 boolean use_dynamic_chars = (pos->chars == -1 ? TRUE : FALSE);
1955 if (use_dynamic_chars) /* use dynamic number of chars */
1957 int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
1958 int chars1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
1959 int chars2 = chars1 + 1;
1960 int font1 = pos->font;
1961 int font2 = pos->font_alt;
1963 chars = (value < value_change ? chars1 : chars2);
1964 font = (value < value_change ? font1 : font2);
1966 /* clear background if value just changed its size (dynamic chars) */
1967 if ((last_value < value_change) != (value < value_change))
1969 int width1 = chars1 * getFontWidth(font1);
1970 int width2 = chars2 * getFontWidth(font2);
1971 int max_width = MAX(width1, width2);
1972 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
1974 pos->width = max_width;
1976 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1977 max_width, max_height);
1981 pos->width = chars * getFontWidth(font);
1984 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font);
1986 else if (type == TYPE_ELEMENT)
1988 int dst_x = PANEL_XPOS(pos);
1989 int dst_y = PANEL_YPOS(pos);
1991 if (value == EL_UNDEFINED || value == EL_EMPTY)
1993 int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
1994 int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
1996 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1997 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2001 int graphic = el2edimg(value);
2003 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2006 else if (type == TYPE_STRING)
2008 char *s = (nr == GAME_CONTROL_PLAYER_NAME ? setup.player_name :
2009 nr == GAME_CONTROL_LEVEL_NAME ? level.name :
2010 nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2014 char *s_cut = getStringCopyN(s, pos->chars);
2016 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, pos->font);
2022 redraw_mask |= REDRAW_DOOR_1;
2026 void DrawGameValue_Emeralds(int value)
2028 struct TextPosInfo *pos = &game.panel.gems;
2030 int font_nr = pos->font;
2032 int font_nr = FONT_TEXT_2;
2034 int font_width = getFontWidth(font_nr);
2035 int chars = pos->chars;
2038 return; /* !!! USE NEW STUFF !!! */
2041 if (PANEL_DEACTIVATED(pos))
2044 pos->width = chars * font_width;
2046 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2049 void DrawGameValue_Dynamite(int value)
2051 struct TextPosInfo *pos = &game.panel.inventory;
2053 int font_nr = pos->font;
2055 int font_nr = FONT_TEXT_2;
2057 int font_width = getFontWidth(font_nr);
2058 int chars = pos->chars;
2061 return; /* !!! USE NEW STUFF !!! */
2064 if (PANEL_DEACTIVATED(pos))
2067 pos->width = chars * font_width;
2069 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2072 void DrawGameValue_Score(int value)
2074 struct TextPosInfo *pos = &game.panel.score;
2076 int font_nr = pos->font;
2078 int font_nr = FONT_TEXT_2;
2080 int font_width = getFontWidth(font_nr);
2081 int chars = pos->chars;
2084 return; /* !!! USE NEW STUFF !!! */
2087 if (PANEL_DEACTIVATED(pos))
2090 pos->width = chars * font_width;
2092 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2095 void DrawGameValue_Time(int value)
2097 struct TextPosInfo *pos = &game.panel.time;
2098 static int last_value = -1;
2101 int chars = pos->chars;
2103 int font1_nr = pos->font;
2104 int font2_nr = pos->font_alt;
2106 int font1_nr = FONT_TEXT_2;
2107 int font2_nr = FONT_TEXT_1;
2109 int font_nr = font1_nr;
2110 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2113 return; /* !!! USE NEW STUFF !!! */
2116 if (PANEL_DEACTIVATED(pos))
2119 if (use_dynamic_chars) /* use dynamic number of chars */
2121 chars = (value < 1000 ? chars1 : chars2);
2122 font_nr = (value < 1000 ? font1_nr : font2_nr);
2125 /* clear background if value just changed its size (dynamic chars only) */
2126 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2128 int width1 = chars1 * getFontWidth(font1_nr);
2129 int width2 = chars2 * getFontWidth(font2_nr);
2130 int max_width = MAX(width1, width2);
2131 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2133 pos->width = max_width;
2135 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2136 max_width, max_height);
2139 pos->width = chars * getFontWidth(font_nr);
2141 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2146 void DrawGameValue_Level(int value)
2148 struct TextPosInfo *pos = &game.panel.level_number;
2151 int chars = pos->chars;
2153 int font1_nr = pos->font;
2154 int font2_nr = pos->font_alt;
2156 int font1_nr = FONT_TEXT_2;
2157 int font2_nr = FONT_TEXT_1;
2159 int font_nr = font1_nr;
2160 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2163 return; /* !!! USE NEW STUFF !!! */
2166 if (PANEL_DEACTIVATED(pos))
2169 if (use_dynamic_chars) /* use dynamic number of chars */
2171 chars = (level_nr < 100 ? chars1 : chars2);
2172 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2175 pos->width = chars * getFontWidth(font_nr);
2177 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2180 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2183 struct TextPosInfo *pos = &game.panel.keys;
2186 int base_key_graphic = EL_KEY_1;
2191 return; /* !!! USE NEW STUFF !!! */
2195 if (PANEL_DEACTIVATED(pos))
2200 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2201 base_key_graphic = EL_EM_KEY_1;
2205 pos->width = 4 * MINI_TILEX;
2209 for (i = 0; i < MAX_NUM_KEYS; i++)
2211 /* currently only 4 of 8 possible keys are displayed */
2212 for (i = 0; i < STD_NUM_KEYS; i++)
2216 struct TextPosInfo *pos = &game.panel.key[i];
2218 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2219 int src_y = DOOR_GFX_PAGEY1 + 123;
2221 int dst_x = PANEL_XPOS(pos);
2222 int dst_y = PANEL_YPOS(pos);
2224 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2225 int dst_y = PANEL_YPOS(pos);
2229 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2230 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2232 int graphic = el2edimg(element);
2236 if (PANEL_DEACTIVATED(pos))
2241 /* masked blit with tiles from half-size scaled bitmap does not work yet
2242 (no mask bitmap created for these sizes after loading and scaling) --
2243 solution: load without creating mask, scale, then create final mask */
2245 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2246 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2251 int graphic = el2edimg(base_key_graphic + i);
2256 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2258 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2259 dst_x - src_x, dst_y - src_y);
2260 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2266 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2268 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2269 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2272 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2274 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2275 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2283 void DrawGameValue_Emeralds(int value)
2285 int font_nr = FONT_TEXT_2;
2286 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2288 if (PANEL_DEACTIVATED(game.panel.gems))
2291 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2294 void DrawGameValue_Dynamite(int value)
2296 int font_nr = FONT_TEXT_2;
2297 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2299 if (PANEL_DEACTIVATED(game.panel.inventory))
2302 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2305 void DrawGameValue_Score(int value)
2307 int font_nr = FONT_TEXT_2;
2308 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2310 if (PANEL_DEACTIVATED(game.panel.score))
2313 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2316 void DrawGameValue_Time(int value)
2318 int font1_nr = FONT_TEXT_2;
2320 int font2_nr = FONT_TEXT_1;
2322 int font2_nr = FONT_LEVEL_NUMBER;
2324 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2325 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2327 if (PANEL_DEACTIVATED(game.panel.time))
2330 /* clear background if value just changed its size */
2331 if (value == 999 || value == 1000)
2332 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2335 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2337 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2340 void DrawGameValue_Level(int value)
2342 int font1_nr = FONT_TEXT_2;
2344 int font2_nr = FONT_TEXT_1;
2346 int font2_nr = FONT_LEVEL_NUMBER;
2349 if (PANEL_DEACTIVATED(game.panel.level))
2353 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2355 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2358 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2360 int base_key_graphic = EL_KEY_1;
2363 if (PANEL_DEACTIVATED(game.panel.keys))
2366 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2367 base_key_graphic = EL_EM_KEY_1;
2369 /* currently only 4 of 8 possible keys are displayed */
2370 for (i = 0; i < STD_NUM_KEYS; i++)
2372 int x = XX_KEYS + i * MINI_TILEX;
2376 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2378 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2379 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2385 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2388 int key[MAX_NUM_KEYS];
2391 /* prevent EM engine from updating time/score values parallel to GameWon() */
2392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2393 local_player->LevelSolved)
2396 for (i = 0; i < MAX_NUM_KEYS; i++)
2397 key[i] = key_bits & (1 << i);
2399 DrawGameValue_Level(level_nr);
2401 DrawGameValue_Emeralds(emeralds);
2402 DrawGameValue_Dynamite(dynamite);
2403 DrawGameValue_Score(score);
2404 DrawGameValue_Time(time);
2406 DrawGameValue_Keys(key);
2409 void DrawGameDoorValues()
2411 UpdateGameControlValues();
2412 DisplayGameControlValues();
2415 void DrawGameDoorValues_OLD()
2417 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2418 int dynamite_value = 0;
2419 int score_value = (local_player->LevelSolved ? local_player->score_final :
2420 local_player->score);
2421 int gems_value = local_player->gems_still_needed;
2425 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2427 DrawGameDoorValues_EM();
2432 if (game.centered_player_nr == -1)
2434 for (i = 0; i < MAX_PLAYERS; i++)
2436 for (j = 0; j < MAX_NUM_KEYS; j++)
2437 if (stored_player[i].key[j])
2438 key_bits |= (1 << j);
2440 dynamite_value += stored_player[i].inventory_size;
2445 int player_nr = game.centered_player_nr;
2447 for (i = 0; i < MAX_NUM_KEYS; i++)
2448 if (stored_player[player_nr].key[i])
2449 key_bits |= (1 << i);
2451 dynamite_value = stored_player[player_nr].inventory_size;
2454 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2460 =============================================================================
2462 -----------------------------------------------------------------------------
2463 initialize game engine due to level / tape version number
2464 =============================================================================
2467 static void InitGameEngine()
2469 int i, j, k, l, x, y;
2471 /* set game engine from tape file when re-playing, else from level file */
2472 game.engine_version = (tape.playing ? tape.engine_version :
2473 level.game_version);
2475 /* ---------------------------------------------------------------------- */
2476 /* set flags for bugs and changes according to active game engine version */
2477 /* ---------------------------------------------------------------------- */
2480 Summary of bugfix/change:
2481 Fixed handling for custom elements that change when pushed by the player.
2483 Fixed/changed in version:
2487 Before 3.1.0, custom elements that "change when pushing" changed directly
2488 after the player started pushing them (until then handled in "DigField()").
2489 Since 3.1.0, these custom elements are not changed until the "pushing"
2490 move of the element is finished (now handled in "ContinueMoving()").
2492 Affected levels/tapes:
2493 The first condition is generally needed for all levels/tapes before version
2494 3.1.0, which might use the old behaviour before it was changed; known tapes
2495 that are affected are some tapes from the level set "Walpurgis Gardens" by
2497 The second condition is an exception from the above case and is needed for
2498 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2499 above (including some development versions of 3.1.0), but before it was
2500 known that this change would break tapes like the above and was fixed in
2501 3.1.1, so that the changed behaviour was active although the engine version
2502 while recording maybe was before 3.1.0. There is at least one tape that is
2503 affected by this exception, which is the tape for the one-level set "Bug
2504 Machine" by Juergen Bonhagen.
2507 game.use_change_when_pushing_bug =
2508 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2510 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2511 tape.game_version < VERSION_IDENT(3,1,1,0)));
2514 Summary of bugfix/change:
2515 Fixed handling for blocking the field the player leaves when moving.
2517 Fixed/changed in version:
2521 Before 3.1.1, when "block last field when moving" was enabled, the field
2522 the player is leaving when moving was blocked for the time of the move,
2523 and was directly unblocked afterwards. This resulted in the last field
2524 being blocked for exactly one less than the number of frames of one player
2525 move. Additionally, even when blocking was disabled, the last field was
2526 blocked for exactly one frame.
2527 Since 3.1.1, due to changes in player movement handling, the last field
2528 is not blocked at all when blocking is disabled. When blocking is enabled,
2529 the last field is blocked for exactly the number of frames of one player
2530 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2531 last field is blocked for exactly one more than the number of frames of
2534 Affected levels/tapes:
2535 (!!! yet to be determined -- probably many !!!)
2538 game.use_block_last_field_bug =
2539 (game.engine_version < VERSION_IDENT(3,1,1,0));
2542 Summary of bugfix/change:
2543 Changed behaviour of CE changes with multiple changes per single frame.
2545 Fixed/changed in version:
2549 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2550 This resulted in race conditions where CEs seem to behave strange in some
2551 situations (where triggered CE changes were just skipped because there was
2552 already a CE change on that tile in the playfield in that engine frame).
2553 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2554 (The number of changes per frame must be limited in any case, because else
2555 it is easily possible to define CE changes that would result in an infinite
2556 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2557 should be set large enough so that it would only be reached in cases where
2558 the corresponding CE change conditions run into a loop. Therefore, it seems
2559 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2560 maximal number of change pages for custom elements.)
2562 Affected levels/tapes:
2566 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2567 game.max_num_changes_per_frame = 1;
2569 game.max_num_changes_per_frame =
2570 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2573 /* ---------------------------------------------------------------------- */
2575 /* default scan direction: scan playfield from top/left to bottom/right */
2576 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2578 /* dynamically adjust element properties according to game engine version */
2579 InitElementPropertiesEngine(game.engine_version);
2582 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2583 printf(" tape version == %06d [%s] [file: %06d]\n",
2584 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2586 printf(" => game.engine_version == %06d\n", game.engine_version);
2589 /* ---------- initialize player's initial move delay --------------------- */
2591 /* dynamically adjust player properties according to level information */
2592 for (i = 0; i < MAX_PLAYERS; i++)
2593 game.initial_move_delay_value[i] =
2594 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2596 /* dynamically adjust player properties according to game engine version */
2597 for (i = 0; i < MAX_PLAYERS; i++)
2598 game.initial_move_delay[i] =
2599 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2600 game.initial_move_delay_value[i] : 0);
2602 /* ---------- initialize player's initial push delay --------------------- */
2604 /* dynamically adjust player properties according to game engine version */
2605 game.initial_push_delay_value =
2606 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2608 /* ---------- initialize changing elements ------------------------------- */
2610 /* initialize changing elements information */
2611 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2613 struct ElementInfo *ei = &element_info[i];
2615 /* this pointer might have been changed in the level editor */
2616 ei->change = &ei->change_page[0];
2618 if (!IS_CUSTOM_ELEMENT(i))
2620 ei->change->target_element = EL_EMPTY_SPACE;
2621 ei->change->delay_fixed = 0;
2622 ei->change->delay_random = 0;
2623 ei->change->delay_frames = 1;
2626 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2628 ei->has_change_event[j] = FALSE;
2630 ei->event_page_nr[j] = 0;
2631 ei->event_page[j] = &ei->change_page[0];
2635 /* add changing elements from pre-defined list */
2636 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2638 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2639 struct ElementInfo *ei = &element_info[ch_delay->element];
2641 ei->change->target_element = ch_delay->target_element;
2642 ei->change->delay_fixed = ch_delay->change_delay;
2644 ei->change->pre_change_function = ch_delay->pre_change_function;
2645 ei->change->change_function = ch_delay->change_function;
2646 ei->change->post_change_function = ch_delay->post_change_function;
2648 ei->change->can_change = TRUE;
2649 ei->change->can_change_or_has_action = TRUE;
2651 ei->has_change_event[CE_DELAY] = TRUE;
2653 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2654 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2657 /* ---------- initialize internal run-time variables ------------- */
2659 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2661 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2663 for (j = 0; j < ei->num_change_pages; j++)
2665 ei->change_page[j].can_change_or_has_action =
2666 (ei->change_page[j].can_change |
2667 ei->change_page[j].has_action);
2671 /* add change events from custom element configuration */
2672 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2674 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2676 for (j = 0; j < ei->num_change_pages; j++)
2678 if (!ei->change_page[j].can_change_or_has_action)
2681 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2683 /* only add event page for the first page found with this event */
2684 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2686 ei->has_change_event[k] = TRUE;
2688 ei->event_page_nr[k] = j;
2689 ei->event_page[k] = &ei->change_page[j];
2695 /* ---------- initialize run-time trigger player and element ------------- */
2697 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2699 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2701 for (j = 0; j < ei->num_change_pages; j++)
2703 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2704 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2705 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2706 ei->change_page[j].actual_trigger_ce_value = 0;
2707 ei->change_page[j].actual_trigger_ce_score = 0;
2711 /* ---------- initialize trigger events ---------------------------------- */
2713 /* initialize trigger events information */
2714 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2715 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2716 trigger_events[i][j] = FALSE;
2718 /* add trigger events from element change event properties */
2719 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2721 struct ElementInfo *ei = &element_info[i];
2723 for (j = 0; j < ei->num_change_pages; j++)
2725 if (!ei->change_page[j].can_change_or_has_action)
2728 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2730 int trigger_element = ei->change_page[j].trigger_element;
2732 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2734 if (ei->change_page[j].has_event[k])
2736 if (IS_GROUP_ELEMENT(trigger_element))
2738 struct ElementGroupInfo *group =
2739 element_info[trigger_element].group;
2741 for (l = 0; l < group->num_elements_resolved; l++)
2742 trigger_events[group->element_resolved[l]][k] = TRUE;
2744 else if (trigger_element == EL_ANY_ELEMENT)
2745 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2746 trigger_events[l][k] = TRUE;
2748 trigger_events[trigger_element][k] = TRUE;
2755 /* ---------- initialize push delay -------------------------------------- */
2757 /* initialize push delay values to default */
2758 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2760 if (!IS_CUSTOM_ELEMENT(i))
2762 /* set default push delay values (corrected since version 3.0.7-1) */
2763 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2765 element_info[i].push_delay_fixed = 2;
2766 element_info[i].push_delay_random = 8;
2770 element_info[i].push_delay_fixed = 8;
2771 element_info[i].push_delay_random = 8;
2776 /* set push delay value for certain elements from pre-defined list */
2777 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2779 int e = push_delay_list[i].element;
2781 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2782 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2785 /* set push delay value for Supaplex elements for newer engine versions */
2786 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2788 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2790 if (IS_SP_ELEMENT(i))
2792 /* set SP push delay to just enough to push under a falling zonk */
2793 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2795 element_info[i].push_delay_fixed = delay;
2796 element_info[i].push_delay_random = 0;
2801 /* ---------- initialize move stepsize ----------------------------------- */
2803 /* initialize move stepsize values to default */
2804 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2805 if (!IS_CUSTOM_ELEMENT(i))
2806 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2808 /* set move stepsize value for certain elements from pre-defined list */
2809 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2811 int e = move_stepsize_list[i].element;
2813 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2816 /* ---------- initialize collect score ----------------------------------- */
2818 /* initialize collect score values for custom elements from initial value */
2819 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2820 if (IS_CUSTOM_ELEMENT(i))
2821 element_info[i].collect_score = element_info[i].collect_score_initial;
2823 /* ---------- initialize collect count ----------------------------------- */
2825 /* initialize collect count values for non-custom elements */
2826 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2827 if (!IS_CUSTOM_ELEMENT(i))
2828 element_info[i].collect_count_initial = 0;
2830 /* add collect count values for all elements from pre-defined list */
2831 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2832 element_info[collect_count_list[i].element].collect_count_initial =
2833 collect_count_list[i].count;
2835 /* ---------- initialize access direction -------------------------------- */
2837 /* initialize access direction values to default (access from every side) */
2838 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2839 if (!IS_CUSTOM_ELEMENT(i))
2840 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2842 /* set access direction value for certain elements from pre-defined list */
2843 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2844 element_info[access_direction_list[i].element].access_direction =
2845 access_direction_list[i].direction;
2847 /* ---------- initialize explosion content ------------------------------- */
2848 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2850 if (IS_CUSTOM_ELEMENT(i))
2853 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2855 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2857 element_info[i].content.e[x][y] =
2858 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2859 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2860 i == EL_PLAYER_3 ? EL_EMERALD :
2861 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2862 i == EL_MOLE ? EL_EMERALD_RED :
2863 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2864 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2865 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2866 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2867 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2868 i == EL_WALL_EMERALD ? EL_EMERALD :
2869 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2870 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2871 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2872 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2873 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2874 i == EL_WALL_PEARL ? EL_PEARL :
2875 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2880 /* ---------- initialize recursion detection ------------------------------ */
2881 recursion_loop_depth = 0;
2882 recursion_loop_detected = FALSE;
2883 recursion_loop_element = EL_UNDEFINED;
2885 /* ---------- initialize graphics engine ---------------------------------- */
2886 game.scroll_delay_value =
2887 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
2888 setup.scroll_delay ? setup.scroll_delay_value : 0);
2889 game.scroll_delay_value =
2890 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
2893 int get_num_special_action(int element, int action_first, int action_last)
2895 int num_special_action = 0;
2898 for (i = action_first; i <= action_last; i++)
2900 boolean found = FALSE;
2902 for (j = 0; j < NUM_DIRECTIONS; j++)
2903 if (el_act_dir2img(element, i, j) !=
2904 el_act_dir2img(element, ACTION_DEFAULT, j))
2908 num_special_action++;
2913 return num_special_action;
2918 =============================================================================
2920 -----------------------------------------------------------------------------
2921 initialize and start new game
2922 =============================================================================
2927 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2928 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2929 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2931 boolean do_fading = (game_status == GAME_MODE_MAIN);
2935 game_status = GAME_MODE_PLAYING;
2938 InitGameControlValues();
2940 /* don't play tapes over network */
2941 network_playing = (options.network && !tape.playing);
2943 for (i = 0; i < MAX_PLAYERS; i++)
2945 struct PlayerInfo *player = &stored_player[i];
2947 player->index_nr = i;
2948 player->index_bit = (1 << i);
2949 player->element_nr = EL_PLAYER_1 + i;
2951 player->present = FALSE;
2952 player->active = FALSE;
2953 player->killed = FALSE;
2956 player->effective_action = 0;
2957 player->programmed_action = 0;
2960 player->score_final = 0;
2962 player->gems_still_needed = level.gems_needed;
2963 player->sokobanfields_still_needed = 0;
2964 player->lights_still_needed = 0;
2965 player->friends_still_needed = 0;
2967 for (j = 0; j < MAX_NUM_KEYS; j++)
2968 player->key[j] = FALSE;
2970 player->num_white_keys = 0;
2972 player->dynabomb_count = 0;
2973 player->dynabomb_size = 1;
2974 player->dynabombs_left = 0;
2975 player->dynabomb_xl = FALSE;
2977 player->MovDir = MV_NONE;
2980 player->GfxDir = MV_NONE;
2981 player->GfxAction = ACTION_DEFAULT;
2983 player->StepFrame = 0;
2985 player->use_murphy = FALSE;
2986 player->artwork_element =
2987 (level.use_artwork_element[i] ? level.artwork_element[i] :
2988 player->element_nr);
2990 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2991 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2993 player->gravity = level.initial_player_gravity[i];
2995 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2997 player->actual_frame_counter = 0;
2999 player->step_counter = 0;
3001 player->last_move_dir = MV_NONE;
3003 player->is_active = FALSE;
3005 player->is_waiting = FALSE;
3006 player->is_moving = FALSE;
3007 player->is_auto_moving = FALSE;
3008 player->is_digging = FALSE;
3009 player->is_snapping = FALSE;
3010 player->is_collecting = FALSE;
3011 player->is_pushing = FALSE;
3012 player->is_switching = FALSE;
3013 player->is_dropping = FALSE;
3014 player->is_dropping_pressed = FALSE;
3016 player->is_bored = FALSE;
3017 player->is_sleeping = FALSE;
3019 player->frame_counter_bored = -1;
3020 player->frame_counter_sleeping = -1;
3022 player->anim_delay_counter = 0;
3023 player->post_delay_counter = 0;
3025 player->dir_waiting = MV_NONE;
3026 player->action_waiting = ACTION_DEFAULT;
3027 player->last_action_waiting = ACTION_DEFAULT;
3028 player->special_action_bored = ACTION_DEFAULT;
3029 player->special_action_sleeping = ACTION_DEFAULT;
3031 player->switch_x = -1;
3032 player->switch_y = -1;
3034 player->drop_x = -1;
3035 player->drop_y = -1;
3037 player->show_envelope = 0;
3039 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3041 player->push_delay = -1; /* initialized when pushing starts */
3042 player->push_delay_value = game.initial_push_delay_value;
3044 player->drop_delay = 0;
3045 player->drop_pressed_delay = 0;
3047 player->last_jx = -1;
3048 player->last_jy = -1;
3052 player->shield_normal_time_left = 0;
3053 player->shield_deadly_time_left = 0;
3055 player->inventory_infinite_element = EL_UNDEFINED;
3056 player->inventory_size = 0;
3058 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3059 SnapField(player, 0, 0);
3061 player->LevelSolved = FALSE;
3062 player->GameOver = FALSE;
3064 player->LevelSolved_GameWon = FALSE;
3065 player->LevelSolved_GameEnd = FALSE;
3066 player->LevelSolved_PanelOff = FALSE;
3067 player->LevelSolved_SaveTape = FALSE;
3068 player->LevelSolved_SaveScore = FALSE;
3071 network_player_action_received = FALSE;
3073 #if defined(NETWORK_AVALIABLE)
3074 /* initial null action */
3075 if (network_playing)
3076 SendToServer_MovePlayer(MV_NONE);
3085 TimeLeft = level.time;
3088 ScreenMovDir = MV_NONE;
3092 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3094 AllPlayersGone = FALSE;
3096 game.yamyam_content_nr = 0;
3097 game.magic_wall_active = FALSE;
3098 game.magic_wall_time_left = 0;
3099 game.light_time_left = 0;
3100 game.timegate_time_left = 0;
3101 game.switchgate_pos = 0;
3102 game.wind_direction = level.wind_direction_initial;
3104 #if !USE_PLAYER_GRAVITY
3105 game.gravity = FALSE;
3106 game.explosions_delayed = TRUE;
3109 game.lenses_time_left = 0;
3110 game.magnify_time_left = 0;
3112 game.ball_state = level.ball_state_initial;
3113 game.ball_content_nr = 0;
3115 game.envelope_active = FALSE;
3117 /* set focus to local player for network games, else to all players */
3118 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3119 game.centered_player_nr_next = game.centered_player_nr;
3120 game.set_centered_player = FALSE;
3122 if (network_playing && tape.recording)
3124 /* store client dependent player focus when recording network games */
3125 tape.centered_player_nr_next = game.centered_player_nr_next;
3126 tape.set_centered_player = TRUE;
3129 for (i = 0; i < NUM_BELTS; i++)
3131 game.belt_dir[i] = MV_NONE;
3132 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3135 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3136 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3138 SCAN_PLAYFIELD(x, y)
3140 Feld[x][y] = level.field[x][y];
3141 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3142 ChangeDelay[x][y] = 0;
3143 ChangePage[x][y] = -1;
3144 #if USE_NEW_CUSTOM_VALUE
3145 CustomValue[x][y] = 0; /* initialized in InitField() */
3147 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3149 WasJustMoving[x][y] = 0;
3150 WasJustFalling[x][y] = 0;
3151 CheckCollision[x][y] = 0;
3152 CheckImpact[x][y] = 0;
3154 Pushed[x][y] = FALSE;
3156 ChangeCount[x][y] = 0;
3157 ChangeEvent[x][y] = -1;
3159 ExplodePhase[x][y] = 0;
3160 ExplodeDelay[x][y] = 0;
3161 ExplodeField[x][y] = EX_TYPE_NONE;
3163 RunnerVisit[x][y] = 0;
3164 PlayerVisit[x][y] = 0;
3167 GfxRandom[x][y] = INIT_GFX_RANDOM();
3168 GfxElement[x][y] = EL_UNDEFINED;
3169 GfxAction[x][y] = ACTION_DEFAULT;
3170 GfxDir[x][y] = MV_NONE;
3173 SCAN_PLAYFIELD(x, y)
3175 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3177 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3179 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3182 InitField(x, y, TRUE);
3187 for (i = 0; i < MAX_PLAYERS; i++)
3189 struct PlayerInfo *player = &stored_player[i];
3191 /* set number of special actions for bored and sleeping animation */
3192 player->num_special_action_bored =
3193 get_num_special_action(player->artwork_element,
3194 ACTION_BORING_1, ACTION_BORING_LAST);
3195 player->num_special_action_sleeping =
3196 get_num_special_action(player->artwork_element,
3197 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3200 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3201 emulate_sb ? EMU_SOKOBAN :
3202 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3204 #if USE_NEW_ALL_SLIPPERY
3205 /* initialize type of slippery elements */
3206 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3208 if (!IS_CUSTOM_ELEMENT(i))
3210 /* default: elements slip down either to the left or right randomly */
3211 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3213 /* SP style elements prefer to slip down on the left side */
3214 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3215 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3217 /* BD style elements prefer to slip down on the left side */
3218 if (game.emulation == EMU_BOULDERDASH)
3219 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3224 /* initialize explosion and ignition delay */
3225 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3227 if (!IS_CUSTOM_ELEMENT(i))
3230 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3231 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3232 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3233 int last_phase = (num_phase + 1) * delay;
3234 int half_phase = (num_phase / 2) * delay;
3236 element_info[i].explosion_delay = last_phase - 1;
3237 element_info[i].ignition_delay = half_phase;
3239 if (i == EL_BLACK_ORB)
3240 element_info[i].ignition_delay = 1;
3244 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3245 element_info[i].explosion_delay = 1;
3247 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3248 element_info[i].ignition_delay = 1;
3252 /* correct non-moving belts to start moving left */
3253 for (i = 0; i < NUM_BELTS; i++)
3254 if (game.belt_dir[i] == MV_NONE)
3255 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3257 /* check if any connected player was not found in playfield */
3258 for (i = 0; i < MAX_PLAYERS; i++)
3260 struct PlayerInfo *player = &stored_player[i];
3262 if (player->connected && !player->present)
3264 for (j = 0; j < MAX_PLAYERS; j++)
3266 struct PlayerInfo *some_player = &stored_player[j];
3267 int jx = some_player->jx, jy = some_player->jy;
3269 /* assign first free player found that is present in the playfield */
3270 if (some_player->present && !some_player->connected)
3272 player->present = TRUE;
3273 player->active = TRUE;
3275 some_player->present = FALSE;
3276 some_player->active = FALSE;
3278 player->artwork_element = some_player->artwork_element;
3280 player->block_last_field = some_player->block_last_field;
3281 player->block_delay_adjustment = some_player->block_delay_adjustment;
3283 StorePlayer[jx][jy] = player->element_nr;
3284 player->jx = player->last_jx = jx;
3285 player->jy = player->last_jy = jy;
3295 /* when playing a tape, eliminate all players who do not participate */
3297 for (i = 0; i < MAX_PLAYERS; i++)
3299 if (stored_player[i].active && !tape.player_participates[i])
3301 struct PlayerInfo *player = &stored_player[i];
3302 int jx = player->jx, jy = player->jy;
3304 player->active = FALSE;
3305 StorePlayer[jx][jy] = 0;
3306 Feld[jx][jy] = EL_EMPTY;
3310 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3312 /* when in single player mode, eliminate all but the first active player */
3314 for (i = 0; i < MAX_PLAYERS; i++)
3316 if (stored_player[i].active)
3318 for (j = i + 1; j < MAX_PLAYERS; j++)
3320 if (stored_player[j].active)
3322 struct PlayerInfo *player = &stored_player[j];
3323 int jx = player->jx, jy = player->jy;
3325 player->active = FALSE;
3326 player->present = FALSE;
3328 StorePlayer[jx][jy] = 0;
3329 Feld[jx][jy] = EL_EMPTY;
3336 /* when recording the game, store which players take part in the game */
3339 for (i = 0; i < MAX_PLAYERS; i++)
3340 if (stored_player[i].active)
3341 tape.player_participates[i] = TRUE;
3346 for (i = 0; i < MAX_PLAYERS; i++)
3348 struct PlayerInfo *player = &stored_player[i];
3350 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3355 if (local_player == player)
3356 printf("Player %d is local player.\n", i+1);
3360 if (BorderElement == EL_EMPTY)
3363 SBX_Right = lev_fieldx - SCR_FIELDX;
3365 SBY_Lower = lev_fieldy - SCR_FIELDY;
3370 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3372 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3375 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3376 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3378 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3379 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3381 /* if local player not found, look for custom element that might create
3382 the player (make some assumptions about the right custom element) */
3383 if (!local_player->present)
3385 int start_x = 0, start_y = 0;
3386 int found_rating = 0;
3387 int found_element = EL_UNDEFINED;
3388 int player_nr = local_player->index_nr;
3390 SCAN_PLAYFIELD(x, y)
3392 int element = Feld[x][y];
3397 if (level.use_start_element[player_nr] &&
3398 level.start_element[player_nr] == element &&
3405 found_element = element;
3408 if (!IS_CUSTOM_ELEMENT(element))
3411 if (CAN_CHANGE(element))
3413 for (i = 0; i < element_info[element].num_change_pages; i++)
3415 /* check for player created from custom element as single target */
3416 content = element_info[element].change_page[i].target_element;
3417 is_player = ELEM_IS_PLAYER(content);
3419 if (is_player && (found_rating < 3 ||
3420 (found_rating == 3 && element < found_element)))
3426 found_element = element;
3431 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3433 /* check for player created from custom element as explosion content */
3434 content = element_info[element].content.e[xx][yy];
3435 is_player = ELEM_IS_PLAYER(content);
3437 if (is_player && (found_rating < 2 ||
3438 (found_rating == 2 && element < found_element)))
3440 start_x = x + xx - 1;
3441 start_y = y + yy - 1;
3444 found_element = element;
3447 if (!CAN_CHANGE(element))
3450 for (i = 0; i < element_info[element].num_change_pages; i++)
3452 /* check for player created from custom element as extended target */
3454 element_info[element].change_page[i].target_content.e[xx][yy];
3456 is_player = ELEM_IS_PLAYER(content);
3458 if (is_player && (found_rating < 1 ||
3459 (found_rating == 1 && element < found_element)))
3461 start_x = x + xx - 1;
3462 start_y = y + yy - 1;
3465 found_element = element;
3471 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3472 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3475 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3476 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3481 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3482 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3483 local_player->jx - MIDPOSX);
3485 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3486 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3487 local_player->jy - MIDPOSY);
3492 if (!game.restart_level)
3493 CloseDoor(DOOR_CLOSE_1);
3496 if (level_editor_test_game)
3497 FadeSkipNextFadeIn();
3499 FadeSetEnterScreen();
3501 if (level_editor_test_game)
3502 fading = fading_none;
3504 fading = menu.destination;
3508 FadeOut(REDRAW_FIELD);
3511 FadeOut(REDRAW_FIELD);
3514 /* !!! FIX THIS (START) !!! */
3515 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3517 InitGameEngine_EM();
3519 /* blit playfield from scroll buffer to normal back buffer for fading in */
3520 BlitScreenToBitmap_EM(backbuffer);
3527 /* after drawing the level, correct some elements */
3528 if (game.timegate_time_left == 0)
3529 CloseAllOpenTimegates();
3531 /* blit playfield from scroll buffer to normal back buffer for fading in */
3532 if (setup.soft_scrolling)
3533 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3535 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3537 /* !!! FIX THIS (END) !!! */
3540 FadeIn(REDRAW_FIELD);
3543 FadeIn(REDRAW_FIELD);
3548 if (!game.restart_level)
3550 /* copy default game door content to main double buffer */
3551 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3552 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3555 SetPanelBackground();
3556 SetDrawBackgroundMask(REDRAW_DOOR_1);
3558 DrawGameDoorValues();
3560 if (!game.restart_level)
3564 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3565 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3566 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3570 /* copy actual game door content to door double buffer for OpenDoor() */
3571 BlitBitmap(drawto, bitmap_db_door,
3572 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3574 OpenDoor(DOOR_OPEN_ALL);
3576 PlaySound(SND_GAME_STARTING);
3578 if (setup.sound_music)
3581 KeyboardAutoRepeatOffUnlessAutoplay();
3585 for (i = 0; i < MAX_PLAYERS; i++)
3586 printf("Player %d %sactive.\n",
3587 i + 1, (stored_player[i].active ? "" : "not "));
3598 game.restart_level = FALSE;
3601 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3603 /* this is used for non-R'n'D game engines to update certain engine values */
3605 /* needed to determine if sounds are played within the visible screen area */
3606 scroll_x = actual_scroll_x;
3607 scroll_y = actual_scroll_y;
3610 void InitMovDir(int x, int y)
3612 int i, element = Feld[x][y];
3613 static int xy[4][2] =
3620 static int direction[3][4] =
3622 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3623 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3624 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3633 Feld[x][y] = EL_BUG;
3634 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3637 case EL_SPACESHIP_RIGHT:
3638 case EL_SPACESHIP_UP:
3639 case EL_SPACESHIP_LEFT:
3640 case EL_SPACESHIP_DOWN:
3641 Feld[x][y] = EL_SPACESHIP;
3642 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3645 case EL_BD_BUTTERFLY_RIGHT:
3646 case EL_BD_BUTTERFLY_UP:
3647 case EL_BD_BUTTERFLY_LEFT:
3648 case EL_BD_BUTTERFLY_DOWN:
3649 Feld[x][y] = EL_BD_BUTTERFLY;
3650 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3653 case EL_BD_FIREFLY_RIGHT:
3654 case EL_BD_FIREFLY_UP:
3655 case EL_BD_FIREFLY_LEFT:
3656 case EL_BD_FIREFLY_DOWN:
3657 Feld[x][y] = EL_BD_FIREFLY;
3658 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3661 case EL_PACMAN_RIGHT:
3663 case EL_PACMAN_LEFT:
3664 case EL_PACMAN_DOWN:
3665 Feld[x][y] = EL_PACMAN;
3666 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3669 case EL_YAMYAM_LEFT:
3670 case EL_YAMYAM_RIGHT:
3672 case EL_YAMYAM_DOWN:
3673 Feld[x][y] = EL_YAMYAM;
3674 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3677 case EL_SP_SNIKSNAK:
3678 MovDir[x][y] = MV_UP;
3681 case EL_SP_ELECTRON:
3682 MovDir[x][y] = MV_LEFT;
3689 Feld[x][y] = EL_MOLE;
3690 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3694 if (IS_CUSTOM_ELEMENT(element))
3696 struct ElementInfo *ei = &element_info[element];
3697 int move_direction_initial = ei->move_direction_initial;
3698 int move_pattern = ei->move_pattern;
3700 if (move_direction_initial == MV_START_PREVIOUS)
3702 if (MovDir[x][y] != MV_NONE)
3705 move_direction_initial = MV_START_AUTOMATIC;
3708 if (move_direction_initial == MV_START_RANDOM)
3709 MovDir[x][y] = 1 << RND(4);
3710 else if (move_direction_initial & MV_ANY_DIRECTION)
3711 MovDir[x][y] = move_direction_initial;
3712 else if (move_pattern == MV_ALL_DIRECTIONS ||
3713 move_pattern == MV_TURNING_LEFT ||
3714 move_pattern == MV_TURNING_RIGHT ||
3715 move_pattern == MV_TURNING_LEFT_RIGHT ||
3716 move_pattern == MV_TURNING_RIGHT_LEFT ||
3717 move_pattern == MV_TURNING_RANDOM)
3718 MovDir[x][y] = 1 << RND(4);
3719 else if (move_pattern == MV_HORIZONTAL)
3720 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3721 else if (move_pattern == MV_VERTICAL)
3722 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3723 else if (move_pattern & MV_ANY_DIRECTION)
3724 MovDir[x][y] = element_info[element].move_pattern;
3725 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3726 move_pattern == MV_ALONG_RIGHT_SIDE)
3728 /* use random direction as default start direction */
3729 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3730 MovDir[x][y] = 1 << RND(4);
3732 for (i = 0; i < NUM_DIRECTIONS; i++)
3734 int x1 = x + xy[i][0];
3735 int y1 = y + xy[i][1];
3737 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3739 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3740 MovDir[x][y] = direction[0][i];
3742 MovDir[x][y] = direction[1][i];
3751 MovDir[x][y] = 1 << RND(4);
3753 if (element != EL_BUG &&
3754 element != EL_SPACESHIP &&
3755 element != EL_BD_BUTTERFLY &&
3756 element != EL_BD_FIREFLY)
3759 for (i = 0; i < NUM_DIRECTIONS; i++)
3761 int x1 = x + xy[i][0];
3762 int y1 = y + xy[i][1];
3764 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3766 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3768 MovDir[x][y] = direction[0][i];
3771 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3772 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3774 MovDir[x][y] = direction[1][i];
3783 GfxDir[x][y] = MovDir[x][y];
3786 void InitAmoebaNr(int x, int y)
3789 int group_nr = AmoebeNachbarNr(x, y);
3793 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3795 if (AmoebaCnt[i] == 0)
3803 AmoebaNr[x][y] = group_nr;
3804 AmoebaCnt[group_nr]++;
3805 AmoebaCnt2[group_nr]++;
3808 static void PlayerWins(struct PlayerInfo *player)
3810 player->LevelSolved = TRUE;
3811 player->GameOver = TRUE;
3813 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3814 level.native_em_level->lev->score : player->score);
3819 static int time, time_final;
3820 static int score, score_final;
3821 static int game_over_delay_1 = 0;
3822 static int game_over_delay_2 = 0;
3823 int game_over_delay_value_1 = 50;
3824 int game_over_delay_value_2 = 50;
3826 if (!local_player->LevelSolved_GameWon)
3830 /* do not start end game actions before the player stops moving (to exit) */
3831 if (local_player->MovPos)
3834 local_player->LevelSolved_GameWon = TRUE;
3835 local_player->LevelSolved_SaveTape = tape.recording;
3836 local_player->LevelSolved_SaveScore = !tape.playing;
3838 if (tape.auto_play) /* tape might already be stopped here */
3839 tape.auto_play_level_solved = TRUE;
3845 game_over_delay_1 = game_over_delay_value_1;
3846 game_over_delay_2 = game_over_delay_value_2;
3848 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3849 score = score_final = local_player->score_final;
3854 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3856 else if (level.time == 0 && TimePlayed < 999)
3859 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3862 local_player->score_final = score_final;
3864 if (level_editor_test_game)
3867 score = score_final;
3870 game_control_value[GAME_CONTROL_TIME] = time;
3871 game_control_value[GAME_CONTROL_SCORE] = score;
3873 DisplayGameControlValues();
3875 DrawGameValue_Time(time);
3876 DrawGameValue_Score(score);
3880 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3882 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3884 /* close exit door after last player */
3885 if ((AllPlayersGone &&
3886 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3887 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3888 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3889 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3890 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3892 int element = Feld[ExitX][ExitY];
3895 if (element == EL_EM_EXIT_OPEN ||
3896 element == EL_EM_STEEL_EXIT_OPEN)
3903 Feld[ExitX][ExitY] =
3904 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3905 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3906 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3907 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3908 EL_EM_STEEL_EXIT_CLOSING);
3910 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3914 /* player disappears */
3915 DrawLevelField(ExitX, ExitY);
3918 for (i = 0; i < MAX_PLAYERS; i++)
3920 struct PlayerInfo *player = &stored_player[i];
3922 if (player->present)
3924 RemovePlayer(player);
3926 /* player disappears */
3927 DrawLevelField(player->jx, player->jy);
3932 PlaySound(SND_GAME_WINNING);
3935 if (game_over_delay_1 > 0)
3937 game_over_delay_1--;
3942 if (time != time_final)
3944 int time_to_go = ABS(time_final - time);
3945 int time_count_dir = (time < time_final ? +1 : -1);
3946 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3948 time += time_count_steps * time_count_dir;
3949 score += time_count_steps * level.score[SC_TIME_BONUS];
3952 game_control_value[GAME_CONTROL_TIME] = time;
3953 game_control_value[GAME_CONTROL_SCORE] = score;
3955 DisplayGameControlValues();
3957 DrawGameValue_Time(time);
3958 DrawGameValue_Score(score);
3961 if (time == time_final)
3962 StopSound(SND_GAME_LEVELTIME_BONUS);
3963 else if (setup.sound_loops)
3964 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3966 PlaySound(SND_GAME_LEVELTIME_BONUS);
3971 local_player->LevelSolved_PanelOff = TRUE;
3973 if (game_over_delay_2 > 0)
3975 game_over_delay_2--;
3988 boolean raise_level = FALSE;
3990 local_player->LevelSolved_GameEnd = TRUE;
3992 CloseDoor(DOOR_CLOSE_1);
3994 if (local_player->LevelSolved_SaveTape)
4001 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4003 SaveTape(tape.level_nr); /* ask to save tape */
4007 if (level_editor_test_game)
4009 game_status = GAME_MODE_MAIN;
4012 DrawAndFadeInMainMenu(REDRAW_FIELD);
4020 if (!local_player->LevelSolved_SaveScore)
4023 FadeOut(REDRAW_FIELD);
4026 game_status = GAME_MODE_MAIN;
4028 DrawAndFadeInMainMenu(REDRAW_FIELD);
4033 if (level_nr == leveldir_current->handicap_level)
4035 leveldir_current->handicap_level++;
4036 SaveLevelSetup_SeriesInfo();
4039 if (level_nr < leveldir_current->last_level)
4040 raise_level = TRUE; /* advance to next level */
4042 if ((hi_pos = NewHiScore()) >= 0)
4044 game_status = GAME_MODE_SCORES;
4046 DrawHallOfFame(hi_pos);
4057 FadeOut(REDRAW_FIELD);
4060 game_status = GAME_MODE_MAIN;
4068 DrawAndFadeInMainMenu(REDRAW_FIELD);
4077 LoadScore(level_nr);
4079 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4080 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4083 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4085 if (local_player->score_final > highscore[k].Score)
4087 /* player has made it to the hall of fame */
4089 if (k < MAX_SCORE_ENTRIES - 1)
4091 int m = MAX_SCORE_ENTRIES - 1;
4094 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4095 if (strEqual(setup.player_name, highscore[l].Name))
4097 if (m == k) /* player's new highscore overwrites his old one */
4101 for (l = m; l > k; l--)
4103 strcpy(highscore[l].Name, highscore[l - 1].Name);
4104 highscore[l].Score = highscore[l - 1].Score;
4111 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4112 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4113 highscore[k].Score = local_player->score_final;
4119 else if (!strncmp(setup.player_name, highscore[k].Name,
4120 MAX_PLAYER_NAME_LEN))
4121 break; /* player already there with a higher score */
4127 SaveScore(level_nr);
4132 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4134 int element = Feld[x][y];
4135 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4136 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4137 int horiz_move = (dx != 0);
4138 int sign = (horiz_move ? dx : dy);
4139 int step = sign * element_info[element].move_stepsize;
4141 /* special values for move stepsize for spring and things on conveyor belt */
4144 if (CAN_FALL(element) &&
4145 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4146 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4147 else if (element == EL_SPRING)
4148 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4154 inline static int getElementMoveStepsize(int x, int y)
4156 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4159 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4161 if (player->GfxAction != action || player->GfxDir != dir)
4164 printf("Player frame reset! (%d => %d, %d => %d)\n",
4165 player->GfxAction, action, player->GfxDir, dir);
4168 player->GfxAction = action;
4169 player->GfxDir = dir;
4171 player->StepFrame = 0;
4175 #if USE_GFX_RESET_GFX_ANIMATION
4176 static void ResetGfxFrame(int x, int y, boolean redraw)
4178 int element = Feld[x][y];
4179 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4180 int last_gfx_frame = GfxFrame[x][y];
4182 if (graphic_info[graphic].anim_global_sync)
4183 GfxFrame[x][y] = FrameCounter;
4184 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4185 GfxFrame[x][y] = CustomValue[x][y];
4186 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4187 GfxFrame[x][y] = element_info[element].collect_score;
4188 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4189 GfxFrame[x][y] = ChangeDelay[x][y];
4191 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4192 DrawLevelGraphicAnimation(x, y, graphic);
4196 static void ResetGfxAnimation(int x, int y)
4198 GfxAction[x][y] = ACTION_DEFAULT;
4199 GfxDir[x][y] = MovDir[x][y];
4202 #if USE_GFX_RESET_GFX_ANIMATION
4203 ResetGfxFrame(x, y, FALSE);
4207 static void ResetRandomAnimationValue(int x, int y)
4209 GfxRandom[x][y] = INIT_GFX_RANDOM();
4212 void InitMovingField(int x, int y, int direction)
4214 int element = Feld[x][y];
4215 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4216 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4219 boolean is_moving_before, is_moving_after;
4221 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4224 /* check if element was/is moving or being moved before/after mode change */
4227 is_moving_before = (WasJustMoving[x][y] != 0);
4229 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4230 is_moving_before = WasJustMoving[x][y];
4233 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4235 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4237 /* reset animation only for moving elements which change direction of moving
4238 or which just started or stopped moving
4239 (else CEs with property "can move" / "not moving" are reset each frame) */
4240 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4242 if (is_moving_before != is_moving_after ||
4243 direction != MovDir[x][y])
4244 ResetGfxAnimation(x, y);
4246 if ((is_moving_before || is_moving_after) && !continues_moving)
4247 ResetGfxAnimation(x, y);
4250 if (!continues_moving)
4251 ResetGfxAnimation(x, y);
4254 MovDir[x][y] = direction;
4255 GfxDir[x][y] = direction;
4257 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4258 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4259 direction == MV_DOWN && CAN_FALL(element) ?
4260 ACTION_FALLING : ACTION_MOVING);
4262 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4263 ACTION_FALLING : ACTION_MOVING);
4266 /* this is needed for CEs with property "can move" / "not moving" */
4268 if (is_moving_after)
4270 if (Feld[newx][newy] == EL_EMPTY)
4271 Feld[newx][newy] = EL_BLOCKED;
4273 MovDir[newx][newy] = MovDir[x][y];
4275 #if USE_NEW_CUSTOM_VALUE
4276 CustomValue[newx][newy] = CustomValue[x][y];
4279 GfxFrame[newx][newy] = GfxFrame[x][y];
4280 GfxRandom[newx][newy] = GfxRandom[x][y];
4281 GfxAction[newx][newy] = GfxAction[x][y];
4282 GfxDir[newx][newy] = GfxDir[x][y];
4286 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4288 int direction = MovDir[x][y];
4289 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4290 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4296 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4298 int oldx = x, oldy = y;
4299 int direction = MovDir[x][y];
4301 if (direction == MV_LEFT)
4303 else if (direction == MV_RIGHT)
4305 else if (direction == MV_UP)
4307 else if (direction == MV_DOWN)
4310 *comes_from_x = oldx;
4311 *comes_from_y = oldy;
4314 int MovingOrBlocked2Element(int x, int y)
4316 int element = Feld[x][y];
4318 if (element == EL_BLOCKED)
4322 Blocked2Moving(x, y, &oldx, &oldy);
4323 return Feld[oldx][oldy];
4329 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4331 /* like MovingOrBlocked2Element(), but if element is moving
4332 and (x,y) is the field the moving element is just leaving,
4333 return EL_BLOCKED instead of the element value */
4334 int element = Feld[x][y];
4336 if (IS_MOVING(x, y))
4338 if (element == EL_BLOCKED)
4342 Blocked2Moving(x, y, &oldx, &oldy);
4343 return Feld[oldx][oldy];
4352 static void RemoveField(int x, int y)
4354 Feld[x][y] = EL_EMPTY;
4360 #if USE_NEW_CUSTOM_VALUE
4361 CustomValue[x][y] = 0;
4365 ChangeDelay[x][y] = 0;
4366 ChangePage[x][y] = -1;
4367 Pushed[x][y] = FALSE;
4370 ExplodeField[x][y] = EX_TYPE_NONE;
4373 GfxElement[x][y] = EL_UNDEFINED;
4374 GfxAction[x][y] = ACTION_DEFAULT;
4375 GfxDir[x][y] = MV_NONE;
4378 void RemoveMovingField(int x, int y)
4380 int oldx = x, oldy = y, newx = x, newy = y;
4381 int element = Feld[x][y];
4382 int next_element = EL_UNDEFINED;
4384 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4387 if (IS_MOVING(x, y))
4389 Moving2Blocked(x, y, &newx, &newy);
4391 if (Feld[newx][newy] != EL_BLOCKED)
4393 /* element is moving, but target field is not free (blocked), but
4394 already occupied by something different (example: acid pool);
4395 in this case, only remove the moving field, but not the target */
4397 RemoveField(oldx, oldy);
4399 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4401 DrawLevelField(oldx, oldy);
4406 else if (element == EL_BLOCKED)
4408 Blocked2Moving(x, y, &oldx, &oldy);
4409 if (!IS_MOVING(oldx, oldy))
4413 if (element == EL_BLOCKED &&
4414 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4415 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4416 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4417 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4418 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4419 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4420 next_element = get_next_element(Feld[oldx][oldy]);
4422 RemoveField(oldx, oldy);
4423 RemoveField(newx, newy);
4425 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4427 if (next_element != EL_UNDEFINED)
4428 Feld[oldx][oldy] = next_element;
4430 DrawLevelField(oldx, oldy);
4431 DrawLevelField(newx, newy);
4434 void DrawDynamite(int x, int y)
4436 int sx = SCREENX(x), sy = SCREENY(y);
4437 int graphic = el2img(Feld[x][y]);
4440 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4443 if (IS_WALKABLE_INSIDE(Back[x][y]))
4447 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4448 else if (Store[x][y])
4449 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4451 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4453 if (Back[x][y] || Store[x][y])
4454 DrawGraphicThruMask(sx, sy, graphic, frame);
4456 DrawGraphic(sx, sy, graphic, frame);
4459 void CheckDynamite(int x, int y)
4461 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4465 if (MovDelay[x][y] != 0)
4468 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4474 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4479 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4481 boolean num_checked_players = 0;
4484 for (i = 0; i < MAX_PLAYERS; i++)
4486 if (stored_player[i].active)
4488 int sx = stored_player[i].jx;
4489 int sy = stored_player[i].jy;
4491 if (num_checked_players == 0)
4498 *sx1 = MIN(*sx1, sx);
4499 *sy1 = MIN(*sy1, sy);
4500 *sx2 = MAX(*sx2, sx);
4501 *sy2 = MAX(*sy2, sy);
4504 num_checked_players++;
4509 static boolean checkIfAllPlayersFitToScreen_RND()
4511 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4513 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4515 return (sx2 - sx1 < SCR_FIELDX &&
4516 sy2 - sy1 < SCR_FIELDY);
4519 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4521 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4523 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4525 *sx = (sx1 + sx2) / 2;
4526 *sy = (sy1 + sy2) / 2;
4529 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4530 boolean center_screen, boolean quick_relocation)
4532 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4533 boolean no_delay = (tape.warp_forward);
4534 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4535 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4537 if (quick_relocation)
4539 int offset = game.scroll_delay_value;
4541 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4543 if (!level.shifted_relocation || center_screen)
4545 /* quick relocation (without scrolling), with centering of screen */
4547 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4548 x > SBX_Right + MIDPOSX ? SBX_Right :
4551 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4552 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4557 /* quick relocation (without scrolling), but do not center screen */
4559 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4560 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4563 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4564 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4567 int offset_x = x + (scroll_x - center_scroll_x);
4568 int offset_y = y + (scroll_y - center_scroll_y);
4570 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4571 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4572 offset_x - MIDPOSX);
4574 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4575 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4576 offset_y - MIDPOSY);
4581 /* quick relocation (without scrolling), inside visible screen area */
4583 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4584 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4585 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4587 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4588 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4589 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4591 /* don't scroll over playfield boundaries */
4592 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4593 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4595 /* don't scroll over playfield boundaries */
4596 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4597 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4600 RedrawPlayfield(TRUE, 0,0,0,0);
4605 int scroll_xx, scroll_yy;
4607 if (!level.shifted_relocation || center_screen)
4609 /* visible relocation (with scrolling), with centering of screen */
4611 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4612 x > SBX_Right + MIDPOSX ? SBX_Right :
4615 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4616 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4621 /* visible relocation (with scrolling), but do not center screen */
4623 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4624 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4627 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4628 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4631 int offset_x = x + (scroll_x - center_scroll_x);
4632 int offset_y = y + (scroll_y - center_scroll_y);
4634 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4635 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4636 offset_x - MIDPOSX);
4638 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4639 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4640 offset_y - MIDPOSY);
4645 /* visible relocation (with scrolling), with centering of screen */
4647 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4648 x > SBX_Right + MIDPOSX ? SBX_Right :
4651 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4652 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4656 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4658 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4661 int fx = FX, fy = FY;
4663 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4664 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4666 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4672 fx += dx * TILEX / 2;
4673 fy += dy * TILEY / 2;
4675 ScrollLevel(dx, dy);
4678 /* scroll in two steps of half tile size to make things smoother */
4679 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4681 Delay(wait_delay_value);
4683 /* scroll second step to align at full tile size */
4685 Delay(wait_delay_value);
4690 Delay(wait_delay_value);
4694 void RelocatePlayer(int jx, int jy, int el_player_raw)
4696 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4697 int player_nr = GET_PLAYER_NR(el_player);
4698 struct PlayerInfo *player = &stored_player[player_nr];
4699 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4700 boolean no_delay = (tape.warp_forward);
4701 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4702 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4703 int old_jx = player->jx;
4704 int old_jy = player->jy;
4705 int old_element = Feld[old_jx][old_jy];
4706 int element = Feld[jx][jy];
4707 boolean player_relocated = (old_jx != jx || old_jy != jy);
4709 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4710 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4711 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4712 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4713 int leave_side_horiz = move_dir_horiz;
4714 int leave_side_vert = move_dir_vert;
4715 int enter_side = enter_side_horiz | enter_side_vert;
4716 int leave_side = leave_side_horiz | leave_side_vert;
4718 if (player->GameOver) /* do not reanimate dead player */
4721 if (!player_relocated) /* no need to relocate the player */
4724 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4726 RemoveField(jx, jy); /* temporarily remove newly placed player */
4727 DrawLevelField(jx, jy);
4730 if (player->present)
4732 while (player->MovPos)
4734 ScrollPlayer(player, SCROLL_GO_ON);
4735 ScrollScreen(NULL, SCROLL_GO_ON);
4737 AdvanceFrameAndPlayerCounters(player->index_nr);
4742 Delay(wait_delay_value);
4745 DrawPlayer(player); /* needed here only to cleanup last field */
4746 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4748 player->is_moving = FALSE;
4751 if (IS_CUSTOM_ELEMENT(old_element))
4752 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4754 player->index_bit, leave_side);
4756 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4758 player->index_bit, leave_side);
4760 Feld[jx][jy] = el_player;
4761 InitPlayerField(jx, jy, el_player, TRUE);
4763 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4765 Feld[jx][jy] = element;
4766 InitField(jx, jy, FALSE);
4769 /* only visually relocate centered player */
4770 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4771 FALSE, level.instant_relocation);
4773 TestIfPlayerTouchesBadThing(jx, jy);
4774 TestIfPlayerTouchesCustomElement(jx, jy);
4776 if (IS_CUSTOM_ELEMENT(element))
4777 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4778 player->index_bit, enter_side);
4780 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4781 player->index_bit, enter_side);
4784 void Explode(int ex, int ey, int phase, int mode)
4790 /* !!! eliminate this variable !!! */
4791 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4793 if (game.explosions_delayed)
4795 ExplodeField[ex][ey] = mode;
4799 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4801 int center_element = Feld[ex][ey];
4802 int artwork_element, explosion_element; /* set these values later */
4805 /* --- This is only really needed (and now handled) in "Impact()". --- */
4806 /* do not explode moving elements that left the explode field in time */
4807 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4808 center_element == EL_EMPTY &&
4809 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4814 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4815 if (mode == EX_TYPE_NORMAL ||
4816 mode == EX_TYPE_CENTER ||
4817 mode == EX_TYPE_CROSS)
4818 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4821 /* remove things displayed in background while burning dynamite */
4822 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4825 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4827 /* put moving element to center field (and let it explode there) */
4828 center_element = MovingOrBlocked2Element(ex, ey);
4829 RemoveMovingField(ex, ey);
4830 Feld[ex][ey] = center_element;
4833 /* now "center_element" is finally determined -- set related values now */
4834 artwork_element = center_element; /* for custom player artwork */
4835 explosion_element = center_element; /* for custom player artwork */
4837 if (IS_PLAYER(ex, ey))
4839 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4841 artwork_element = stored_player[player_nr].artwork_element;
4843 if (level.use_explosion_element[player_nr])
4845 explosion_element = level.explosion_element[player_nr];
4846 artwork_element = explosion_element;
4851 if (mode == EX_TYPE_NORMAL ||
4852 mode == EX_TYPE_CENTER ||
4853 mode == EX_TYPE_CROSS)
4854 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4857 last_phase = element_info[explosion_element].explosion_delay + 1;
4859 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4861 int xx = x - ex + 1;
4862 int yy = y - ey + 1;
4865 if (!IN_LEV_FIELD(x, y) ||
4866 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4867 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4870 element = Feld[x][y];
4872 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4874 element = MovingOrBlocked2Element(x, y);
4876 if (!IS_EXPLOSION_PROOF(element))
4877 RemoveMovingField(x, y);
4880 /* indestructible elements can only explode in center (but not flames) */
4881 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4882 mode == EX_TYPE_BORDER)) ||
4883 element == EL_FLAMES)
4886 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4887 behaviour, for example when touching a yamyam that explodes to rocks
4888 with active deadly shield, a rock is created under the player !!! */
4889 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4891 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4892 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4893 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4895 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4898 if (IS_ACTIVE_BOMB(element))
4900 /* re-activate things under the bomb like gate or penguin */
4901 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4908 /* save walkable background elements while explosion on same tile */
4909 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4910 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4911 Back[x][y] = element;
4913 /* ignite explodable elements reached by other explosion */
4914 if (element == EL_EXPLOSION)
4915 element = Store2[x][y];
4917 if (AmoebaNr[x][y] &&
4918 (element == EL_AMOEBA_FULL ||
4919 element == EL_BD_AMOEBA ||
4920 element == EL_AMOEBA_GROWING))
4922 AmoebaCnt[AmoebaNr[x][y]]--;
4923 AmoebaCnt2[AmoebaNr[x][y]]--;
4928 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4930 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4932 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4934 if (PLAYERINFO(ex, ey)->use_murphy)
4935 Store[x][y] = EL_EMPTY;
4938 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4939 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4940 else if (ELEM_IS_PLAYER(center_element))
4941 Store[x][y] = EL_EMPTY;
4942 else if (center_element == EL_YAMYAM)
4943 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4944 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4945 Store[x][y] = element_info[center_element].content.e[xx][yy];
4947 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4948 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4949 otherwise) -- FIX THIS !!! */
4950 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4951 Store[x][y] = element_info[element].content.e[1][1];
4953 else if (!CAN_EXPLODE(element))
4954 Store[x][y] = element_info[element].content.e[1][1];
4957 Store[x][y] = EL_EMPTY;
4959 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4960 center_element == EL_AMOEBA_TO_DIAMOND)
4961 Store2[x][y] = element;
4963 Feld[x][y] = EL_EXPLOSION;
4964 GfxElement[x][y] = artwork_element;
4966 ExplodePhase[x][y] = 1;
4967 ExplodeDelay[x][y] = last_phase;
4972 if (center_element == EL_YAMYAM)
4973 game.yamyam_content_nr =
4974 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4986 GfxFrame[x][y] = 0; /* restart explosion animation */
4988 last_phase = ExplodeDelay[x][y];
4990 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4994 /* activate this even in non-DEBUG version until cause for crash in
4995 getGraphicAnimationFrame() (see below) is found and eliminated */
5001 /* this can happen if the player leaves an explosion just in time */
5002 if (GfxElement[x][y] == EL_UNDEFINED)
5003 GfxElement[x][y] = EL_EMPTY;
5005 if (GfxElement[x][y] == EL_UNDEFINED)
5008 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5009 printf("Explode(): This should never happen!\n");
5012 GfxElement[x][y] = EL_EMPTY;
5018 border_element = Store2[x][y];
5019 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5020 border_element = StorePlayer[x][y];
5022 if (phase == element_info[border_element].ignition_delay ||
5023 phase == last_phase)
5025 boolean border_explosion = FALSE;
5027 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5028 !PLAYER_EXPLOSION_PROTECTED(x, y))
5030 KillPlayerUnlessExplosionProtected(x, y);
5031 border_explosion = TRUE;
5033 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5035 Feld[x][y] = Store2[x][y];
5038 border_explosion = TRUE;
5040 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5042 AmoebeUmwandeln(x, y);
5044 border_explosion = TRUE;
5047 /* if an element just explodes due to another explosion (chain-reaction),
5048 do not immediately end the new explosion when it was the last frame of
5049 the explosion (as it would be done in the following "if"-statement!) */
5050 if (border_explosion && phase == last_phase)
5054 if (phase == last_phase)
5058 element = Feld[x][y] = Store[x][y];
5059 Store[x][y] = Store2[x][y] = 0;
5060 GfxElement[x][y] = EL_UNDEFINED;
5062 /* player can escape from explosions and might therefore be still alive */
5063 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5064 element <= EL_PLAYER_IS_EXPLODING_4)
5066 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5067 int explosion_element = EL_PLAYER_1 + player_nr;
5068 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5069 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5071 if (level.use_explosion_element[player_nr])
5072 explosion_element = level.explosion_element[player_nr];
5074 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5075 element_info[explosion_element].content.e[xx][yy]);
5078 /* restore probably existing indestructible background element */
5079 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5080 element = Feld[x][y] = Back[x][y];
5083 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5084 GfxDir[x][y] = MV_NONE;
5085 ChangeDelay[x][y] = 0;
5086 ChangePage[x][y] = -1;
5088 #if USE_NEW_CUSTOM_VALUE
5089 CustomValue[x][y] = 0;
5092 InitField_WithBug2(x, y, FALSE);
5094 DrawLevelField(x, y);
5096 TestIfElementTouchesCustomElement(x, y);
5098 if (GFX_CRUMBLED(element))
5099 DrawLevelFieldCrumbledSandNeighbours(x, y);
5101 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5102 StorePlayer[x][y] = 0;
5104 if (ELEM_IS_PLAYER(element))
5105 RelocatePlayer(x, y, element);
5107 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5109 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5110 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5113 DrawLevelFieldCrumbledSand(x, y);
5115 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5117 DrawLevelElement(x, y, Back[x][y]);
5118 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5120 else if (IS_WALKABLE_UNDER(Back[x][y]))
5122 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5123 DrawLevelElementThruMask(x, y, Back[x][y]);
5125 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5126 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5130 void DynaExplode(int ex, int ey)
5133 int dynabomb_element = Feld[ex][ey];
5134 int dynabomb_size = 1;
5135 boolean dynabomb_xl = FALSE;
5136 struct PlayerInfo *player;
5137 static int xy[4][2] =
5145 if (IS_ACTIVE_BOMB(dynabomb_element))
5147 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5148 dynabomb_size = player->dynabomb_size;
5149 dynabomb_xl = player->dynabomb_xl;
5150 player->dynabombs_left++;
5153 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5155 for (i = 0; i < NUM_DIRECTIONS; i++)
5157 for (j = 1; j <= dynabomb_size; j++)
5159 int x = ex + j * xy[i][0];
5160 int y = ey + j * xy[i][1];
5163 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5166 element = Feld[x][y];
5168 /* do not restart explosions of fields with active bombs */
5169 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5172 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5174 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5175 !IS_DIGGABLE(element) && !dynabomb_xl)
5181 void Bang(int x, int y)
5183 int element = MovingOrBlocked2Element(x, y);
5184 int explosion_type = EX_TYPE_NORMAL;
5186 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5188 struct PlayerInfo *player = PLAYERINFO(x, y);
5190 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5191 player->element_nr);
5193 if (level.use_explosion_element[player->index_nr])
5195 int explosion_element = level.explosion_element[player->index_nr];
5197 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5198 explosion_type = EX_TYPE_CROSS;
5199 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5200 explosion_type = EX_TYPE_CENTER;
5208 case EL_BD_BUTTERFLY:
5211 case EL_DARK_YAMYAM:
5215 RaiseScoreElement(element);
5218 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5219 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5220 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5221 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5222 case EL_DYNABOMB_INCREASE_NUMBER:
5223 case EL_DYNABOMB_INCREASE_SIZE:
5224 case EL_DYNABOMB_INCREASE_POWER:
5225 explosion_type = EX_TYPE_DYNA;
5228 case EL_DC_LANDMINE:
5230 case EL_EM_EXIT_OPEN:
5231 case EL_EM_STEEL_EXIT_OPEN:
5233 explosion_type = EX_TYPE_CENTER;
5238 case EL_LAMP_ACTIVE:
5239 case EL_AMOEBA_TO_DIAMOND:
5240 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5241 explosion_type = EX_TYPE_CENTER;
5245 if (element_info[element].explosion_type == EXPLODES_CROSS)
5246 explosion_type = EX_TYPE_CROSS;
5247 else if (element_info[element].explosion_type == EXPLODES_1X1)
5248 explosion_type = EX_TYPE_CENTER;
5252 if (explosion_type == EX_TYPE_DYNA)
5255 Explode(x, y, EX_PHASE_START, explosion_type);
5257 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5260 void SplashAcid(int x, int y)
5262 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5263 (!IN_LEV_FIELD(x - 1, y - 2) ||
5264 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5265 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5267 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5268 (!IN_LEV_FIELD(x + 1, y - 2) ||
5269 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5270 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5272 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5275 static void InitBeltMovement()
5277 static int belt_base_element[4] =
5279 EL_CONVEYOR_BELT_1_LEFT,
5280 EL_CONVEYOR_BELT_2_LEFT,
5281 EL_CONVEYOR_BELT_3_LEFT,
5282 EL_CONVEYOR_BELT_4_LEFT
5284 static int belt_base_active_element[4] =
5286 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5287 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5288 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5289 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5294 /* set frame order for belt animation graphic according to belt direction */
5295 for (i = 0; i < NUM_BELTS; i++)
5299 for (j = 0; j < NUM_BELT_PARTS; j++)
5301 int element = belt_base_active_element[belt_nr] + j;
5302 int graphic = el2img(element);
5304 if (game.belt_dir[i] == MV_LEFT)
5305 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5307 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5311 SCAN_PLAYFIELD(x, y)
5313 int element = Feld[x][y];
5315 for (i = 0; i < NUM_BELTS; i++)
5317 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5319 int e_belt_nr = getBeltNrFromBeltElement(element);
5322 if (e_belt_nr == belt_nr)
5324 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5326 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5333 static void ToggleBeltSwitch(int x, int y)
5335 static int belt_base_element[4] =
5337 EL_CONVEYOR_BELT_1_LEFT,
5338 EL_CONVEYOR_BELT_2_LEFT,
5339 EL_CONVEYOR_BELT_3_LEFT,
5340 EL_CONVEYOR_BELT_4_LEFT
5342 static int belt_base_active_element[4] =
5344 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5345 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5346 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5347 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5349 static int belt_base_switch_element[4] =
5351 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5352 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5353 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5354 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5356 static int belt_move_dir[4] =
5364 int element = Feld[x][y];
5365 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5366 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5367 int belt_dir = belt_move_dir[belt_dir_nr];
5370 if (!IS_BELT_SWITCH(element))
5373 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5374 game.belt_dir[belt_nr] = belt_dir;
5376 if (belt_dir_nr == 3)
5379 /* set frame order for belt animation graphic according to belt direction */
5380 for (i = 0; i < NUM_BELT_PARTS; i++)
5382 int element = belt_base_active_element[belt_nr] + i;
5383 int graphic = el2img(element);
5385 if (belt_dir == MV_LEFT)
5386 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5388 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5391 SCAN_PLAYFIELD(xx, yy)
5393 int element = Feld[xx][yy];
5395 if (IS_BELT_SWITCH(element))
5397 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5399 if (e_belt_nr == belt_nr)
5401 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5402 DrawLevelField(xx, yy);
5405 else if (IS_BELT(element) && belt_dir != MV_NONE)
5407 int e_belt_nr = getBeltNrFromBeltElement(element);
5409 if (e_belt_nr == belt_nr)
5411 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5413 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5414 DrawLevelField(xx, yy);
5417 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5419 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5421 if (e_belt_nr == belt_nr)
5423 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5425 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5426 DrawLevelField(xx, yy);
5432 static void ToggleSwitchgateSwitch(int x, int y)
5436 game.switchgate_pos = !game.switchgate_pos;
5438 SCAN_PLAYFIELD(xx, yy)
5440 int element = Feld[xx][yy];
5442 #if !USE_BOTH_SWITCHGATE_SWITCHES
5443 if (element == EL_SWITCHGATE_SWITCH_UP ||
5444 element == EL_SWITCHGATE_SWITCH_DOWN)
5446 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5447 DrawLevelField(xx, yy);
5449 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5450 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5452 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5453 DrawLevelField(xx, yy);
5456 if (element == EL_SWITCHGATE_SWITCH_UP)
5458 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5459 DrawLevelField(xx, yy);
5461 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5463 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5464 DrawLevelField(xx, yy);
5466 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5468 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5469 DrawLevelField(xx, yy);
5471 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5473 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5474 DrawLevelField(xx, yy);
5477 else if (element == EL_SWITCHGATE_OPEN ||
5478 element == EL_SWITCHGATE_OPENING)
5480 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5482 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5484 else if (element == EL_SWITCHGATE_CLOSED ||
5485 element == EL_SWITCHGATE_CLOSING)
5487 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5489 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5494 static int getInvisibleActiveFromInvisibleElement(int element)
5496 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5497 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5498 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5502 static int getInvisibleFromInvisibleActiveElement(int element)
5504 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5505 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5506 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5510 static void RedrawAllLightSwitchesAndInvisibleElements()
5514 SCAN_PLAYFIELD(x, y)
5516 int element = Feld[x][y];
5518 if (element == EL_LIGHT_SWITCH &&
5519 game.light_time_left > 0)
5521 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5522 DrawLevelField(x, y);
5524 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5525 game.light_time_left == 0)
5527 Feld[x][y] = EL_LIGHT_SWITCH;
5528 DrawLevelField(x, y);
5530 else if (element == EL_EMC_DRIPPER &&
5531 game.light_time_left > 0)
5533 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5534 DrawLevelField(x, y);
5536 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5537 game.light_time_left == 0)
5539 Feld[x][y] = EL_EMC_DRIPPER;
5540 DrawLevelField(x, y);
5542 else if (element == EL_INVISIBLE_STEELWALL ||
5543 element == EL_INVISIBLE_WALL ||
5544 element == EL_INVISIBLE_SAND)
5546 if (game.light_time_left > 0)
5547 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5549 DrawLevelField(x, y);
5551 /* uncrumble neighbour fields, if needed */
5552 if (element == EL_INVISIBLE_SAND)
5553 DrawLevelFieldCrumbledSandNeighbours(x, y);
5555 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5556 element == EL_INVISIBLE_WALL_ACTIVE ||
5557 element == EL_INVISIBLE_SAND_ACTIVE)
5559 if (game.light_time_left == 0)
5560 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5562 DrawLevelField(x, y);
5564 /* re-crumble neighbour fields, if needed */
5565 if (element == EL_INVISIBLE_SAND)
5566 DrawLevelFieldCrumbledSandNeighbours(x, y);
5571 static void RedrawAllInvisibleElementsForLenses()
5575 SCAN_PLAYFIELD(x, y)
5577 int element = Feld[x][y];
5579 if (element == EL_EMC_DRIPPER &&
5580 game.lenses_time_left > 0)
5582 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5583 DrawLevelField(x, y);
5585 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5586 game.lenses_time_left == 0)
5588 Feld[x][y] = EL_EMC_DRIPPER;
5589 DrawLevelField(x, y);
5591 else if (element == EL_INVISIBLE_STEELWALL ||
5592 element == EL_INVISIBLE_WALL ||
5593 element == EL_INVISIBLE_SAND)
5595 if (game.lenses_time_left > 0)
5596 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5598 DrawLevelField(x, y);
5600 /* uncrumble neighbour fields, if needed */
5601 if (element == EL_INVISIBLE_SAND)
5602 DrawLevelFieldCrumbledSandNeighbours(x, y);
5604 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5605 element == EL_INVISIBLE_WALL_ACTIVE ||
5606 element == EL_INVISIBLE_SAND_ACTIVE)
5608 if (game.lenses_time_left == 0)
5609 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5611 DrawLevelField(x, y);
5613 /* re-crumble neighbour fields, if needed */
5614 if (element == EL_INVISIBLE_SAND)
5615 DrawLevelFieldCrumbledSandNeighbours(x, y);
5620 static void RedrawAllInvisibleElementsForMagnifier()
5624 SCAN_PLAYFIELD(x, y)
5626 int element = Feld[x][y];
5628 if (element == EL_EMC_FAKE_GRASS &&
5629 game.magnify_time_left > 0)
5631 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5632 DrawLevelField(x, y);
5634 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5635 game.magnify_time_left == 0)
5637 Feld[x][y] = EL_EMC_FAKE_GRASS;
5638 DrawLevelField(x, y);
5640 else if (IS_GATE_GRAY(element) &&
5641 game.magnify_time_left > 0)
5643 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5644 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5645 IS_EM_GATE_GRAY(element) ?
5646 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5647 IS_EMC_GATE_GRAY(element) ?
5648 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5650 DrawLevelField(x, y);
5652 else if (IS_GATE_GRAY_ACTIVE(element) &&
5653 game.magnify_time_left == 0)
5655 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5656 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5657 IS_EM_GATE_GRAY_ACTIVE(element) ?
5658 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5659 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5660 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5662 DrawLevelField(x, y);
5667 static void ToggleLightSwitch(int x, int y)
5669 int element = Feld[x][y];
5671 game.light_time_left =
5672 (element == EL_LIGHT_SWITCH ?
5673 level.time_light * FRAMES_PER_SECOND : 0);
5675 RedrawAllLightSwitchesAndInvisibleElements();
5678 static void ActivateTimegateSwitch(int x, int y)
5682 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5684 SCAN_PLAYFIELD(xx, yy)
5686 int element = Feld[xx][yy];
5688 if (element == EL_TIMEGATE_CLOSED ||
5689 element == EL_TIMEGATE_CLOSING)
5691 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5692 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5696 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5698 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5699 DrawLevelField(xx, yy);
5706 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5707 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5709 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5713 void Impact(int x, int y)
5715 boolean last_line = (y == lev_fieldy - 1);
5716 boolean object_hit = FALSE;
5717 boolean impact = (last_line || object_hit);
5718 int element = Feld[x][y];
5719 int smashed = EL_STEELWALL;
5721 if (!last_line) /* check if element below was hit */
5723 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5726 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5727 MovDir[x][y + 1] != MV_DOWN ||
5728 MovPos[x][y + 1] <= TILEY / 2));
5730 /* do not smash moving elements that left the smashed field in time */
5731 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5732 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5735 #if USE_QUICKSAND_IMPACT_BUGFIX
5736 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5738 RemoveMovingField(x, y + 1);
5739 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5740 Feld[x][y + 2] = EL_ROCK;
5741 DrawLevelField(x, y + 2);
5746 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5748 RemoveMovingField(x, y + 1);
5749 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5750 Feld[x][y + 2] = EL_ROCK;
5751 DrawLevelField(x, y + 2);
5758 smashed = MovingOrBlocked2Element(x, y + 1);
5760 impact = (last_line || object_hit);
5763 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5765 SplashAcid(x, y + 1);
5769 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5770 /* only reset graphic animation if graphic really changes after impact */
5772 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5774 ResetGfxAnimation(x, y);
5775 DrawLevelField(x, y);
5778 if (impact && CAN_EXPLODE_IMPACT(element))
5783 else if (impact && element == EL_PEARL &&
5784 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5786 ResetGfxAnimation(x, y);
5788 Feld[x][y] = EL_PEARL_BREAKING;
5789 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5792 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5794 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5799 if (impact && element == EL_AMOEBA_DROP)
5801 if (object_hit && IS_PLAYER(x, y + 1))
5802 KillPlayerUnlessEnemyProtected(x, y + 1);
5803 else if (object_hit && smashed == EL_PENGUIN)
5807 Feld[x][y] = EL_AMOEBA_GROWING;
5808 Store[x][y] = EL_AMOEBA_WET;
5810 ResetRandomAnimationValue(x, y);
5815 if (object_hit) /* check which object was hit */
5817 if ((CAN_PASS_MAGIC_WALL(element) &&
5818 (smashed == EL_MAGIC_WALL ||
5819 smashed == EL_BD_MAGIC_WALL)) ||
5820 (CAN_PASS_DC_MAGIC_WALL(element) &&
5821 smashed == EL_DC_MAGIC_WALL))
5824 int activated_magic_wall =
5825 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5826 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5827 EL_DC_MAGIC_WALL_ACTIVE);
5829 /* activate magic wall / mill */
5830 SCAN_PLAYFIELD(xx, yy)
5832 if (Feld[xx][yy] == smashed)
5833 Feld[xx][yy] = activated_magic_wall;
5836 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5837 game.magic_wall_active = TRUE;
5839 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5840 SND_MAGIC_WALL_ACTIVATING :
5841 smashed == EL_BD_MAGIC_WALL ?
5842 SND_BD_MAGIC_WALL_ACTIVATING :
5843 SND_DC_MAGIC_WALL_ACTIVATING));
5846 if (IS_PLAYER(x, y + 1))
5848 if (CAN_SMASH_PLAYER(element))
5850 KillPlayerUnlessEnemyProtected(x, y + 1);
5854 else if (smashed == EL_PENGUIN)
5856 if (CAN_SMASH_PLAYER(element))
5862 else if (element == EL_BD_DIAMOND)
5864 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5870 else if (((element == EL_SP_INFOTRON ||
5871 element == EL_SP_ZONK) &&
5872 (smashed == EL_SP_SNIKSNAK ||
5873 smashed == EL_SP_ELECTRON ||
5874 smashed == EL_SP_DISK_ORANGE)) ||
5875 (element == EL_SP_INFOTRON &&
5876 smashed == EL_SP_DISK_YELLOW))
5881 else if (CAN_SMASH_EVERYTHING(element))
5883 if (IS_CLASSIC_ENEMY(smashed) ||
5884 CAN_EXPLODE_SMASHED(smashed))
5889 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5891 if (smashed == EL_LAMP ||
5892 smashed == EL_LAMP_ACTIVE)
5897 else if (smashed == EL_NUT)
5899 Feld[x][y + 1] = EL_NUT_BREAKING;
5900 PlayLevelSound(x, y, SND_NUT_BREAKING);
5901 RaiseScoreElement(EL_NUT);
5904 else if (smashed == EL_PEARL)
5906 ResetGfxAnimation(x, y);
5908 Feld[x][y + 1] = EL_PEARL_BREAKING;
5909 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5912 else if (smashed == EL_DIAMOND)
5914 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5915 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5918 else if (IS_BELT_SWITCH(smashed))
5920 ToggleBeltSwitch(x, y + 1);
5922 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5923 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5924 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5925 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5927 ToggleSwitchgateSwitch(x, y + 1);
5929 else if (smashed == EL_LIGHT_SWITCH ||
5930 smashed == EL_LIGHT_SWITCH_ACTIVE)
5932 ToggleLightSwitch(x, y + 1);
5937 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5940 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5942 CheckElementChangeBySide(x, y + 1, smashed, element,
5943 CE_SWITCHED, CH_SIDE_TOP);
5944 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5950 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5955 /* play sound of magic wall / mill */
5957 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5958 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5959 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5961 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5962 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5963 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5964 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5965 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5966 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5971 /* play sound of object that hits the ground */
5972 if (last_line || object_hit)
5973 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5976 inline static void TurnRoundExt(int x, int y)
5988 { 0, 0 }, { 0, 0 }, { 0, 0 },
5993 int left, right, back;
5997 { MV_DOWN, MV_UP, MV_RIGHT },
5998 { MV_UP, MV_DOWN, MV_LEFT },
6000 { MV_LEFT, MV_RIGHT, MV_DOWN },
6004 { MV_RIGHT, MV_LEFT, MV_UP }
6007 int element = Feld[x][y];
6008 int move_pattern = element_info[element].move_pattern;
6010 int old_move_dir = MovDir[x][y];
6011 int left_dir = turn[old_move_dir].left;
6012 int right_dir = turn[old_move_dir].right;
6013 int back_dir = turn[old_move_dir].back;
6015 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6016 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6017 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6018 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6020 int left_x = x + left_dx, left_y = y + left_dy;
6021 int right_x = x + right_dx, right_y = y + right_dy;
6022 int move_x = x + move_dx, move_y = y + move_dy;
6026 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6028 TestIfBadThingTouchesOtherBadThing(x, y);
6030 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6031 MovDir[x][y] = right_dir;
6032 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6033 MovDir[x][y] = left_dir;
6035 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6037 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6040 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6042 TestIfBadThingTouchesOtherBadThing(x, y);
6044 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6045 MovDir[x][y] = left_dir;
6046 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6047 MovDir[x][y] = right_dir;
6049 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6051 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6054 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6056 TestIfBadThingTouchesOtherBadThing(x, y);
6058 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6059 MovDir[x][y] = left_dir;
6060 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6061 MovDir[x][y] = right_dir;
6063 if (MovDir[x][y] != old_move_dir)
6066 else if (element == EL_YAMYAM)
6068 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6069 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6071 if (can_turn_left && can_turn_right)
6072 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6073 else if (can_turn_left)
6074 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6075 else if (can_turn_right)
6076 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6078 MovDir[x][y] = back_dir;
6080 MovDelay[x][y] = 16 + 16 * RND(3);
6082 else if (element == EL_DARK_YAMYAM)
6084 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6086 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6089 if (can_turn_left && can_turn_right)
6090 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6091 else if (can_turn_left)
6092 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6093 else if (can_turn_right)
6094 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6096 MovDir[x][y] = back_dir;
6098 MovDelay[x][y] = 16 + 16 * RND(3);
6100 else if (element == EL_PACMAN)
6102 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6103 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6105 if (can_turn_left && can_turn_right)
6106 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6107 else if (can_turn_left)
6108 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6109 else if (can_turn_right)
6110 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6112 MovDir[x][y] = back_dir;
6114 MovDelay[x][y] = 6 + RND(40);
6116 else if (element == EL_PIG)
6118 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6119 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6120 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6121 boolean should_turn_left, should_turn_right, should_move_on;
6123 int rnd = RND(rnd_value);
6125 should_turn_left = (can_turn_left &&
6127 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6128 y + back_dy + left_dy)));
6129 should_turn_right = (can_turn_right &&
6131 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6132 y + back_dy + right_dy)));
6133 should_move_on = (can_move_on &&
6136 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6137 y + move_dy + left_dy) ||
6138 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6139 y + move_dy + right_dy)));
6141 if (should_turn_left || should_turn_right || should_move_on)
6143 if (should_turn_left && should_turn_right && should_move_on)
6144 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6145 rnd < 2 * rnd_value / 3 ? right_dir :
6147 else if (should_turn_left && should_turn_right)
6148 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6149 else if (should_turn_left && should_move_on)
6150 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6151 else if (should_turn_right && should_move_on)
6152 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6153 else if (should_turn_left)
6154 MovDir[x][y] = left_dir;
6155 else if (should_turn_right)
6156 MovDir[x][y] = right_dir;
6157 else if (should_move_on)
6158 MovDir[x][y] = old_move_dir;
6160 else if (can_move_on && rnd > rnd_value / 8)
6161 MovDir[x][y] = old_move_dir;
6162 else if (can_turn_left && can_turn_right)
6163 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6164 else if (can_turn_left && rnd > rnd_value / 8)
6165 MovDir[x][y] = left_dir;
6166 else if (can_turn_right && rnd > rnd_value/8)
6167 MovDir[x][y] = right_dir;
6169 MovDir[x][y] = back_dir;
6171 xx = x + move_xy[MovDir[x][y]].dx;
6172 yy = y + move_xy[MovDir[x][y]].dy;
6174 if (!IN_LEV_FIELD(xx, yy) ||
6175 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6176 MovDir[x][y] = old_move_dir;
6180 else if (element == EL_DRAGON)
6182 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6183 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6184 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6186 int rnd = RND(rnd_value);
6188 if (can_move_on && rnd > rnd_value / 8)
6189 MovDir[x][y] = old_move_dir;
6190 else if (can_turn_left && can_turn_right)
6191 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6192 else if (can_turn_left && rnd > rnd_value / 8)
6193 MovDir[x][y] = left_dir;
6194 else if (can_turn_right && rnd > rnd_value / 8)
6195 MovDir[x][y] = right_dir;
6197 MovDir[x][y] = back_dir;
6199 xx = x + move_xy[MovDir[x][y]].dx;
6200 yy = y + move_xy[MovDir[x][y]].dy;
6202 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6203 MovDir[x][y] = old_move_dir;
6207 else if (element == EL_MOLE)
6209 boolean can_move_on =
6210 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6211 IS_AMOEBOID(Feld[move_x][move_y]) ||
6212 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6215 boolean can_turn_left =
6216 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6217 IS_AMOEBOID(Feld[left_x][left_y])));
6219 boolean can_turn_right =
6220 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6221 IS_AMOEBOID(Feld[right_x][right_y])));
6223 if (can_turn_left && can_turn_right)
6224 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6225 else if (can_turn_left)
6226 MovDir[x][y] = left_dir;
6228 MovDir[x][y] = right_dir;
6231 if (MovDir[x][y] != old_move_dir)
6234 else if (element == EL_BALLOON)
6236 MovDir[x][y] = game.wind_direction;
6239 else if (element == EL_SPRING)
6241 #if USE_NEW_SPRING_BUMPER
6242 if (MovDir[x][y] & MV_HORIZONTAL)
6244 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6245 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6247 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6248 ResetGfxAnimation(move_x, move_y);
6249 DrawLevelField(move_x, move_y);
6251 MovDir[x][y] = back_dir;
6253 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6254 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6255 MovDir[x][y] = MV_NONE;
6258 if (MovDir[x][y] & MV_HORIZONTAL &&
6259 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6260 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6261 MovDir[x][y] = MV_NONE;
6266 else if (element == EL_ROBOT ||
6267 element == EL_SATELLITE ||
6268 element == EL_PENGUIN ||
6269 element == EL_EMC_ANDROID)
6271 int attr_x = -1, attr_y = -1;
6282 for (i = 0; i < MAX_PLAYERS; i++)
6284 struct PlayerInfo *player = &stored_player[i];
6285 int jx = player->jx, jy = player->jy;
6287 if (!player->active)
6291 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6299 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6300 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6301 game.engine_version < VERSION_IDENT(3,1,0,0)))
6307 if (element == EL_PENGUIN)
6310 static int xy[4][2] =
6318 for (i = 0; i < NUM_DIRECTIONS; i++)
6320 int ex = x + xy[i][0];
6321 int ey = y + xy[i][1];
6323 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6324 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6325 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6326 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6335 MovDir[x][y] = MV_NONE;
6337 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6338 else if (attr_x > x)
6339 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6341 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6342 else if (attr_y > y)
6343 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6345 if (element == EL_ROBOT)
6349 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6350 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6351 Moving2Blocked(x, y, &newx, &newy);
6353 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6354 MovDelay[x][y] = 8 + 8 * !RND(3);
6356 MovDelay[x][y] = 16;
6358 else if (element == EL_PENGUIN)
6364 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6366 boolean first_horiz = RND(2);
6367 int new_move_dir = MovDir[x][y];
6370 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6371 Moving2Blocked(x, y, &newx, &newy);
6373 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6377 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6378 Moving2Blocked(x, y, &newx, &newy);
6380 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6383 MovDir[x][y] = old_move_dir;
6387 else if (element == EL_SATELLITE)
6393 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6395 boolean first_horiz = RND(2);
6396 int new_move_dir = MovDir[x][y];
6399 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6400 Moving2Blocked(x, y, &newx, &newy);
6402 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6406 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6407 Moving2Blocked(x, y, &newx, &newy);
6409 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6412 MovDir[x][y] = old_move_dir;
6416 else if (element == EL_EMC_ANDROID)
6418 static int check_pos[16] =
6420 -1, /* 0 => (invalid) */
6421 7, /* 1 => MV_LEFT */
6422 3, /* 2 => MV_RIGHT */
6423 -1, /* 3 => (invalid) */
6425 0, /* 5 => MV_LEFT | MV_UP */
6426 2, /* 6 => MV_RIGHT | MV_UP */
6427 -1, /* 7 => (invalid) */
6428 5, /* 8 => MV_DOWN */
6429 6, /* 9 => MV_LEFT | MV_DOWN */
6430 4, /* 10 => MV_RIGHT | MV_DOWN */
6431 -1, /* 11 => (invalid) */
6432 -1, /* 12 => (invalid) */
6433 -1, /* 13 => (invalid) */
6434 -1, /* 14 => (invalid) */
6435 -1, /* 15 => (invalid) */
6443 { -1, -1, MV_LEFT | MV_UP },
6445 { +1, -1, MV_RIGHT | MV_UP },
6446 { +1, 0, MV_RIGHT },
6447 { +1, +1, MV_RIGHT | MV_DOWN },
6449 { -1, +1, MV_LEFT | MV_DOWN },
6452 int start_pos, check_order;
6453 boolean can_clone = FALSE;
6456 /* check if there is any free field around current position */
6457 for (i = 0; i < 8; i++)
6459 int newx = x + check_xy[i].dx;
6460 int newy = y + check_xy[i].dy;
6462 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6470 if (can_clone) /* randomly find an element to clone */
6474 start_pos = check_pos[RND(8)];
6475 check_order = (RND(2) ? -1 : +1);
6477 for (i = 0; i < 8; i++)
6479 int pos_raw = start_pos + i * check_order;
6480 int pos = (pos_raw + 8) % 8;
6481 int newx = x + check_xy[pos].dx;
6482 int newy = y + check_xy[pos].dy;
6484 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6486 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6487 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6489 Store[x][y] = Feld[newx][newy];
6498 if (can_clone) /* randomly find a direction to move */
6502 start_pos = check_pos[RND(8)];
6503 check_order = (RND(2) ? -1 : +1);
6505 for (i = 0; i < 8; i++)
6507 int pos_raw = start_pos + i * check_order;
6508 int pos = (pos_raw + 8) % 8;
6509 int newx = x + check_xy[pos].dx;
6510 int newy = y + check_xy[pos].dy;
6511 int new_move_dir = check_xy[pos].dir;
6513 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6515 MovDir[x][y] = new_move_dir;
6516 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6525 if (can_clone) /* cloning and moving successful */
6528 /* cannot clone -- try to move towards player */
6530 start_pos = check_pos[MovDir[x][y] & 0x0f];
6531 check_order = (RND(2) ? -1 : +1);
6533 for (i = 0; i < 3; i++)
6535 /* first check start_pos, then previous/next or (next/previous) pos */
6536 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6537 int pos = (pos_raw + 8) % 8;
6538 int newx = x + check_xy[pos].dx;
6539 int newy = y + check_xy[pos].dy;
6540 int new_move_dir = check_xy[pos].dir;
6542 if (IS_PLAYER(newx, newy))
6545 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6547 MovDir[x][y] = new_move_dir;
6548 MovDelay[x][y] = level.android_move_time * 8 + 1;
6555 else if (move_pattern == MV_TURNING_LEFT ||
6556 move_pattern == MV_TURNING_RIGHT ||
6557 move_pattern == MV_TURNING_LEFT_RIGHT ||
6558 move_pattern == MV_TURNING_RIGHT_LEFT ||
6559 move_pattern == MV_TURNING_RANDOM ||
6560 move_pattern == MV_ALL_DIRECTIONS)
6562 boolean can_turn_left =
6563 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6564 boolean can_turn_right =
6565 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6567 if (element_info[element].move_stepsize == 0) /* "not moving" */
6570 if (move_pattern == MV_TURNING_LEFT)
6571 MovDir[x][y] = left_dir;
6572 else if (move_pattern == MV_TURNING_RIGHT)
6573 MovDir[x][y] = right_dir;
6574 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6575 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6576 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6577 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6578 else if (move_pattern == MV_TURNING_RANDOM)
6579 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6580 can_turn_right && !can_turn_left ? right_dir :
6581 RND(2) ? left_dir : right_dir);
6582 else if (can_turn_left && can_turn_right)
6583 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6584 else if (can_turn_left)
6585 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6586 else if (can_turn_right)
6587 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6589 MovDir[x][y] = back_dir;
6591 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6593 else if (move_pattern == MV_HORIZONTAL ||
6594 move_pattern == MV_VERTICAL)
6596 if (move_pattern & old_move_dir)
6597 MovDir[x][y] = back_dir;
6598 else if (move_pattern == MV_HORIZONTAL)
6599 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6600 else if (move_pattern == MV_VERTICAL)
6601 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6603 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6605 else if (move_pattern & MV_ANY_DIRECTION)
6607 MovDir[x][y] = move_pattern;
6608 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6610 else if (move_pattern & MV_WIND_DIRECTION)
6612 MovDir[x][y] = game.wind_direction;
6613 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6615 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6617 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6618 MovDir[x][y] = left_dir;
6619 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6620 MovDir[x][y] = right_dir;
6622 if (MovDir[x][y] != old_move_dir)
6623 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6625 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6627 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6628 MovDir[x][y] = right_dir;
6629 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6630 MovDir[x][y] = left_dir;
6632 if (MovDir[x][y] != old_move_dir)
6633 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6635 else if (move_pattern == MV_TOWARDS_PLAYER ||
6636 move_pattern == MV_AWAY_FROM_PLAYER)
6638 int attr_x = -1, attr_y = -1;
6640 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6651 for (i = 0; i < MAX_PLAYERS; i++)
6653 struct PlayerInfo *player = &stored_player[i];
6654 int jx = player->jx, jy = player->jy;
6656 if (!player->active)
6660 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6668 MovDir[x][y] = MV_NONE;
6670 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6671 else if (attr_x > x)
6672 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6674 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6675 else if (attr_y > y)
6676 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6678 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6680 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6682 boolean first_horiz = RND(2);
6683 int new_move_dir = MovDir[x][y];
6685 if (element_info[element].move_stepsize == 0) /* "not moving" */
6687 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6688 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6694 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6695 Moving2Blocked(x, y, &newx, &newy);
6697 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6701 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6702 Moving2Blocked(x, y, &newx, &newy);
6704 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6707 MovDir[x][y] = old_move_dir;
6710 else if (move_pattern == MV_WHEN_PUSHED ||
6711 move_pattern == MV_WHEN_DROPPED)
6713 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6714 MovDir[x][y] = MV_NONE;
6718 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6720 static int test_xy[7][2] =
6730 static int test_dir[7] =
6740 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6741 int move_preference = -1000000; /* start with very low preference */
6742 int new_move_dir = MV_NONE;
6743 int start_test = RND(4);
6746 for (i = 0; i < NUM_DIRECTIONS; i++)
6748 int move_dir = test_dir[start_test + i];
6749 int move_dir_preference;
6751 xx = x + test_xy[start_test + i][0];
6752 yy = y + test_xy[start_test + i][1];
6754 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6755 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6757 new_move_dir = move_dir;
6762 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6765 move_dir_preference = -1 * RunnerVisit[xx][yy];
6766 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6767 move_dir_preference = PlayerVisit[xx][yy];
6769 if (move_dir_preference > move_preference)
6771 /* prefer field that has not been visited for the longest time */
6772 move_preference = move_dir_preference;
6773 new_move_dir = move_dir;
6775 else if (move_dir_preference == move_preference &&
6776 move_dir == old_move_dir)
6778 /* prefer last direction when all directions are preferred equally */
6779 move_preference = move_dir_preference;
6780 new_move_dir = move_dir;
6784 MovDir[x][y] = new_move_dir;
6785 if (old_move_dir != new_move_dir)
6786 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6790 static void TurnRound(int x, int y)
6792 int direction = MovDir[x][y];
6796 GfxDir[x][y] = MovDir[x][y];
6798 if (direction != MovDir[x][y])
6802 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6804 ResetGfxFrame(x, y, FALSE);
6807 static boolean JustBeingPushed(int x, int y)
6811 for (i = 0; i < MAX_PLAYERS; i++)
6813 struct PlayerInfo *player = &stored_player[i];
6815 if (player->active && player->is_pushing && player->MovPos)
6817 int next_jx = player->jx + (player->jx - player->last_jx);
6818 int next_jy = player->jy + (player->jy - player->last_jy);
6820 if (x == next_jx && y == next_jy)
6828 void StartMoving(int x, int y)
6830 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6831 int element = Feld[x][y];
6836 if (MovDelay[x][y] == 0)
6837 GfxAction[x][y] = ACTION_DEFAULT;
6839 if (CAN_FALL(element) && y < lev_fieldy - 1)
6841 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6842 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6843 if (JustBeingPushed(x, y))
6846 if (element == EL_QUICKSAND_FULL)
6848 if (IS_FREE(x, y + 1))
6850 InitMovingField(x, y, MV_DOWN);
6851 started_moving = TRUE;
6853 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6854 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6855 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6856 Store[x][y] = EL_ROCK;
6858 Store[x][y] = EL_ROCK;
6861 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6863 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6865 if (!MovDelay[x][y])
6866 MovDelay[x][y] = TILEY + 1;
6875 Feld[x][y] = EL_QUICKSAND_EMPTY;
6876 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6877 Store[x][y + 1] = Store[x][y];
6880 PlayLevelSoundAction(x, y, ACTION_FILLING);
6883 else if (element == EL_QUICKSAND_FAST_FULL)
6885 if (IS_FREE(x, y + 1))
6887 InitMovingField(x, y, MV_DOWN);
6888 started_moving = TRUE;
6890 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6891 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6892 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6893 Store[x][y] = EL_ROCK;
6895 Store[x][y] = EL_ROCK;
6898 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6900 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6902 if (!MovDelay[x][y])
6903 MovDelay[x][y] = TILEY + 1;
6912 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6913 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6914 Store[x][y + 1] = Store[x][y];
6917 PlayLevelSoundAction(x, y, ACTION_FILLING);
6920 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6921 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6923 InitMovingField(x, y, MV_DOWN);
6924 started_moving = TRUE;
6926 Feld[x][y] = EL_QUICKSAND_FILLING;
6927 Store[x][y] = element;
6929 PlayLevelSoundAction(x, y, ACTION_FILLING);
6931 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6932 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6934 InitMovingField(x, y, MV_DOWN);
6935 started_moving = TRUE;
6937 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6938 Store[x][y] = element;
6940 PlayLevelSoundAction(x, y, ACTION_FILLING);
6942 else if (element == EL_MAGIC_WALL_FULL)
6944 if (IS_FREE(x, y + 1))
6946 InitMovingField(x, y, MV_DOWN);
6947 started_moving = TRUE;
6949 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6950 Store[x][y] = EL_CHANGED(Store[x][y]);
6952 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6954 if (!MovDelay[x][y])
6955 MovDelay[x][y] = TILEY/4 + 1;
6964 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6965 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6966 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6970 else if (element == EL_BD_MAGIC_WALL_FULL)
6972 if (IS_FREE(x, y + 1))
6974 InitMovingField(x, y, MV_DOWN);
6975 started_moving = TRUE;
6977 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6978 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6980 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6982 if (!MovDelay[x][y])
6983 MovDelay[x][y] = TILEY/4 + 1;
6992 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6993 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6994 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6998 else if (element == EL_DC_MAGIC_WALL_FULL)
7000 if (IS_FREE(x, y + 1))
7002 InitMovingField(x, y, MV_DOWN);
7003 started_moving = TRUE;
7005 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7006 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7008 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7010 if (!MovDelay[x][y])
7011 MovDelay[x][y] = TILEY/4 + 1;
7020 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7021 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7022 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7026 else if ((CAN_PASS_MAGIC_WALL(element) &&
7027 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7028 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7029 (CAN_PASS_DC_MAGIC_WALL(element) &&
7030 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7033 InitMovingField(x, y, MV_DOWN);
7034 started_moving = TRUE;
7037 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7038 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7039 EL_DC_MAGIC_WALL_FILLING);
7040 Store[x][y] = element;
7042 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7044 SplashAcid(x, y + 1);
7046 InitMovingField(x, y, MV_DOWN);
7047 started_moving = TRUE;
7049 Store[x][y] = EL_ACID;
7052 #if USE_FIX_IMPACT_COLLISION
7053 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7054 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7056 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7057 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7059 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7060 CAN_FALL(element) && WasJustFalling[x][y] &&
7061 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7063 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7064 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7065 (Feld[x][y + 1] == EL_BLOCKED)))
7067 /* this is needed for a special case not covered by calling "Impact()"
7068 from "ContinueMoving()": if an element moves to a tile directly below
7069 another element which was just falling on that tile (which was empty
7070 in the previous frame), the falling element above would just stop
7071 instead of smashing the element below (in previous version, the above
7072 element was just checked for "moving" instead of "falling", resulting
7073 in incorrect smashes caused by horizontal movement of the above
7074 element; also, the case of the player being the element to smash was
7075 simply not covered here... :-/ ) */
7077 CheckCollision[x][y] = 0;
7078 CheckImpact[x][y] = 0;
7082 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7084 if (MovDir[x][y] == MV_NONE)
7086 InitMovingField(x, y, MV_DOWN);
7087 started_moving = TRUE;
7090 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7092 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7093 MovDir[x][y] = MV_DOWN;
7095 InitMovingField(x, y, MV_DOWN);
7096 started_moving = TRUE;
7098 else if (element == EL_AMOEBA_DROP)
7100 Feld[x][y] = EL_AMOEBA_GROWING;
7101 Store[x][y] = EL_AMOEBA_WET;
7103 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7104 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7105 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7106 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7108 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7109 (IS_FREE(x - 1, y + 1) ||
7110 Feld[x - 1][y + 1] == EL_ACID));
7111 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7112 (IS_FREE(x + 1, y + 1) ||
7113 Feld[x + 1][y + 1] == EL_ACID));
7114 boolean can_fall_any = (can_fall_left || can_fall_right);
7115 boolean can_fall_both = (can_fall_left && can_fall_right);
7116 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7118 #if USE_NEW_ALL_SLIPPERY
7119 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7121 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7122 can_fall_right = FALSE;
7123 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7124 can_fall_left = FALSE;
7125 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7126 can_fall_right = FALSE;
7127 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7128 can_fall_left = FALSE;
7130 can_fall_any = (can_fall_left || can_fall_right);
7131 can_fall_both = FALSE;
7134 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7136 if (slippery_type == SLIPPERY_ONLY_LEFT)
7137 can_fall_right = FALSE;
7138 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7139 can_fall_left = FALSE;
7140 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7141 can_fall_right = FALSE;
7142 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7143 can_fall_left = FALSE;
7145 can_fall_any = (can_fall_left || can_fall_right);
7146 can_fall_both = (can_fall_left && can_fall_right);
7150 #if USE_NEW_ALL_SLIPPERY
7152 #if USE_NEW_SP_SLIPPERY
7153 /* !!! better use the same properties as for custom elements here !!! */
7154 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7155 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7157 can_fall_right = FALSE; /* slip down on left side */
7158 can_fall_both = FALSE;
7163 #if USE_NEW_ALL_SLIPPERY
7166 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7167 can_fall_right = FALSE; /* slip down on left side */
7169 can_fall_left = !(can_fall_right = RND(2));
7171 can_fall_both = FALSE;
7176 if (game.emulation == EMU_BOULDERDASH ||
7177 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7178 can_fall_right = FALSE; /* slip down on left side */
7180 can_fall_left = !(can_fall_right = RND(2));
7182 can_fall_both = FALSE;
7188 /* if not determined otherwise, prefer left side for slipping down */
7189 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7190 started_moving = TRUE;
7194 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7196 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7199 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7200 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7201 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7202 int belt_dir = game.belt_dir[belt_nr];
7204 if ((belt_dir == MV_LEFT && left_is_free) ||
7205 (belt_dir == MV_RIGHT && right_is_free))
7207 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7209 InitMovingField(x, y, belt_dir);
7210 started_moving = TRUE;
7212 Pushed[x][y] = TRUE;
7213 Pushed[nextx][y] = TRUE;
7215 GfxAction[x][y] = ACTION_DEFAULT;
7219 MovDir[x][y] = 0; /* if element was moving, stop it */
7224 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7226 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7228 if (CAN_MOVE(element) && !started_moving)
7231 int move_pattern = element_info[element].move_pattern;
7236 if (MovDir[x][y] == MV_NONE)
7238 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7239 x, y, element, element_info[element].token_name);
7240 printf("StartMoving(): This should never happen!\n");
7245 Moving2Blocked(x, y, &newx, &newy);
7247 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7250 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7251 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7253 WasJustMoving[x][y] = 0;
7254 CheckCollision[x][y] = 0;
7256 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7258 if (Feld[x][y] != element) /* element has changed */
7262 if (!MovDelay[x][y]) /* start new movement phase */
7264 /* all objects that can change their move direction after each step
7265 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7267 if (element != EL_YAMYAM &&
7268 element != EL_DARK_YAMYAM &&
7269 element != EL_PACMAN &&
7270 !(move_pattern & MV_ANY_DIRECTION) &&
7271 move_pattern != MV_TURNING_LEFT &&
7272 move_pattern != MV_TURNING_RIGHT &&
7273 move_pattern != MV_TURNING_LEFT_RIGHT &&
7274 move_pattern != MV_TURNING_RIGHT_LEFT &&
7275 move_pattern != MV_TURNING_RANDOM)
7279 if (MovDelay[x][y] && (element == EL_BUG ||
7280 element == EL_SPACESHIP ||
7281 element == EL_SP_SNIKSNAK ||
7282 element == EL_SP_ELECTRON ||
7283 element == EL_MOLE))
7284 DrawLevelField(x, y);
7288 if (MovDelay[x][y]) /* wait some time before next movement */
7292 if (element == EL_ROBOT ||
7293 element == EL_YAMYAM ||
7294 element == EL_DARK_YAMYAM)
7296 DrawLevelElementAnimationIfNeeded(x, y, element);
7297 PlayLevelSoundAction(x, y, ACTION_WAITING);
7299 else if (element == EL_SP_ELECTRON)
7300 DrawLevelElementAnimationIfNeeded(x, y, element);
7301 else if (element == EL_DRAGON)
7304 int dir = MovDir[x][y];
7305 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7306 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7307 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7308 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7309 dir == MV_UP ? IMG_FLAMES_1_UP :
7310 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7311 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7313 GfxAction[x][y] = ACTION_ATTACKING;
7315 if (IS_PLAYER(x, y))
7316 DrawPlayerField(x, y);
7318 DrawLevelField(x, y);
7320 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7322 for (i = 1; i <= 3; i++)
7324 int xx = x + i * dx;
7325 int yy = y + i * dy;
7326 int sx = SCREENX(xx);
7327 int sy = SCREENY(yy);
7328 int flame_graphic = graphic + (i - 1);
7330 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7335 int flamed = MovingOrBlocked2Element(xx, yy);
7339 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7341 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7342 RemoveMovingField(xx, yy);
7344 RemoveField(xx, yy);
7346 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7349 RemoveMovingField(xx, yy);
7352 ChangeDelay[xx][yy] = 0;
7354 Feld[xx][yy] = EL_FLAMES;
7356 if (IN_SCR_FIELD(sx, sy))
7358 DrawLevelFieldCrumbledSand(xx, yy);
7359 DrawGraphic(sx, sy, flame_graphic, frame);
7364 if (Feld[xx][yy] == EL_FLAMES)
7365 Feld[xx][yy] = EL_EMPTY;
7366 DrawLevelField(xx, yy);
7371 if (MovDelay[x][y]) /* element still has to wait some time */
7373 PlayLevelSoundAction(x, y, ACTION_WAITING);
7379 /* now make next step */
7381 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7383 if (DONT_COLLIDE_WITH(element) &&
7384 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7385 !PLAYER_ENEMY_PROTECTED(newx, newy))
7387 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7392 else if (CAN_MOVE_INTO_ACID(element) &&
7393 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7394 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7395 (MovDir[x][y] == MV_DOWN ||
7396 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7398 SplashAcid(newx, newy);
7399 Store[x][y] = EL_ACID;
7401 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7403 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7404 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7405 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7406 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7409 DrawLevelField(x, y);
7411 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7412 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7413 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7415 local_player->friends_still_needed--;
7416 if (!local_player->friends_still_needed &&
7417 !local_player->GameOver && AllPlayersGone)
7418 PlayerWins(local_player);
7422 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7424 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7425 DrawLevelField(newx, newy);
7427 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7429 else if (!IS_FREE(newx, newy))
7431 GfxAction[x][y] = ACTION_WAITING;
7433 if (IS_PLAYER(x, y))
7434 DrawPlayerField(x, y);
7436 DrawLevelField(x, y);
7441 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7443 if (IS_FOOD_PIG(Feld[newx][newy]))
7445 if (IS_MOVING(newx, newy))
7446 RemoveMovingField(newx, newy);
7449 Feld[newx][newy] = EL_EMPTY;
7450 DrawLevelField(newx, newy);
7453 PlayLevelSound(x, y, SND_PIG_DIGGING);
7455 else if (!IS_FREE(newx, newy))
7457 if (IS_PLAYER(x, y))
7458 DrawPlayerField(x, y);
7460 DrawLevelField(x, y);
7465 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7467 if (Store[x][y] != EL_EMPTY)
7469 boolean can_clone = FALSE;
7472 /* check if element to clone is still there */
7473 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7475 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7483 /* cannot clone or target field not free anymore -- do not clone */
7484 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7485 Store[x][y] = EL_EMPTY;
7488 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7490 if (IS_MV_DIAGONAL(MovDir[x][y]))
7492 int diagonal_move_dir = MovDir[x][y];
7493 int stored = Store[x][y];
7494 int change_delay = 8;
7497 /* android is moving diagonally */
7499 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7501 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7502 GfxElement[x][y] = EL_EMC_ANDROID;
7503 GfxAction[x][y] = ACTION_SHRINKING;
7504 GfxDir[x][y] = diagonal_move_dir;
7505 ChangeDelay[x][y] = change_delay;
7507 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7510 DrawLevelGraphicAnimation(x, y, graphic);
7511 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7513 if (Feld[newx][newy] == EL_ACID)
7515 SplashAcid(newx, newy);
7520 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7522 Store[newx][newy] = EL_EMC_ANDROID;
7523 GfxElement[newx][newy] = EL_EMC_ANDROID;
7524 GfxAction[newx][newy] = ACTION_GROWING;
7525 GfxDir[newx][newy] = diagonal_move_dir;
7526 ChangeDelay[newx][newy] = change_delay;
7528 graphic = el_act_dir2img(GfxElement[newx][newy],
7529 GfxAction[newx][newy], GfxDir[newx][newy]);
7531 DrawLevelGraphicAnimation(newx, newy, graphic);
7532 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7538 Feld[newx][newy] = EL_EMPTY;
7539 DrawLevelField(newx, newy);
7541 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7544 else if (!IS_FREE(newx, newy))
7547 if (IS_PLAYER(x, y))
7548 DrawPlayerField(x, y);
7550 DrawLevelField(x, y);
7556 else if (IS_CUSTOM_ELEMENT(element) &&
7557 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7559 int new_element = Feld[newx][newy];
7561 if (!IS_FREE(newx, newy))
7563 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7564 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7567 /* no element can dig solid indestructible elements */
7568 if (IS_INDESTRUCTIBLE(new_element) &&
7569 !IS_DIGGABLE(new_element) &&
7570 !IS_COLLECTIBLE(new_element))
7573 if (AmoebaNr[newx][newy] &&
7574 (new_element == EL_AMOEBA_FULL ||
7575 new_element == EL_BD_AMOEBA ||
7576 new_element == EL_AMOEBA_GROWING))
7578 AmoebaCnt[AmoebaNr[newx][newy]]--;
7579 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7582 if (IS_MOVING(newx, newy))
7583 RemoveMovingField(newx, newy);
7586 RemoveField(newx, newy);
7587 DrawLevelField(newx, newy);
7590 /* if digged element was about to explode, prevent the explosion */
7591 ExplodeField[newx][newy] = EX_TYPE_NONE;
7593 PlayLevelSoundAction(x, y, action);
7596 Store[newx][newy] = EL_EMPTY;
7598 /* this makes it possible to leave the removed element again */
7599 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7600 Store[newx][newy] = new_element;
7602 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7604 int move_leave_element = element_info[element].move_leave_element;
7606 /* this makes it possible to leave the removed element again */
7607 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7608 new_element : move_leave_element);
7612 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7614 RunnerVisit[x][y] = FrameCounter;
7615 PlayerVisit[x][y] /= 8; /* expire player visit path */
7618 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7620 if (!IS_FREE(newx, newy))
7622 if (IS_PLAYER(x, y))
7623 DrawPlayerField(x, y);
7625 DrawLevelField(x, y);
7631 boolean wanna_flame = !RND(10);
7632 int dx = newx - x, dy = newy - y;
7633 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7634 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7635 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7636 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7637 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7638 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7641 IS_CLASSIC_ENEMY(element1) ||
7642 IS_CLASSIC_ENEMY(element2)) &&
7643 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7644 element1 != EL_FLAMES && element2 != EL_FLAMES)
7646 ResetGfxAnimation(x, y);
7647 GfxAction[x][y] = ACTION_ATTACKING;
7649 if (IS_PLAYER(x, y))
7650 DrawPlayerField(x, y);
7652 DrawLevelField(x, y);
7654 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7656 MovDelay[x][y] = 50;
7660 RemoveField(newx, newy);
7662 Feld[newx][newy] = EL_FLAMES;
7663 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7666 RemoveField(newx1, newy1);
7668 Feld[newx1][newy1] = EL_FLAMES;
7670 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7673 RemoveField(newx2, newy2);
7675 Feld[newx2][newy2] = EL_FLAMES;
7682 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7683 Feld[newx][newy] == EL_DIAMOND)
7685 if (IS_MOVING(newx, newy))
7686 RemoveMovingField(newx, newy);
7689 Feld[newx][newy] = EL_EMPTY;
7690 DrawLevelField(newx, newy);
7693 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7695 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7696 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7698 if (AmoebaNr[newx][newy])
7700 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7701 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7702 Feld[newx][newy] == EL_BD_AMOEBA)
7703 AmoebaCnt[AmoebaNr[newx][newy]]--;
7708 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7710 RemoveMovingField(newx, newy);
7713 if (IS_MOVING(newx, newy))
7715 RemoveMovingField(newx, newy);
7720 Feld[newx][newy] = EL_EMPTY;
7721 DrawLevelField(newx, newy);
7724 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7726 else if ((element == EL_PACMAN || element == EL_MOLE)
7727 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7729 if (AmoebaNr[newx][newy])
7731 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7732 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7733 Feld[newx][newy] == EL_BD_AMOEBA)
7734 AmoebaCnt[AmoebaNr[newx][newy]]--;
7737 if (element == EL_MOLE)
7739 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7740 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7742 ResetGfxAnimation(x, y);
7743 GfxAction[x][y] = ACTION_DIGGING;
7744 DrawLevelField(x, y);
7746 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7748 return; /* wait for shrinking amoeba */
7750 else /* element == EL_PACMAN */
7752 Feld[newx][newy] = EL_EMPTY;
7753 DrawLevelField(newx, newy);
7754 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7757 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7758 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7759 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7761 /* wait for shrinking amoeba to completely disappear */
7764 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7766 /* object was running against a wall */
7771 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7772 if (move_pattern & MV_ANY_DIRECTION &&
7773 move_pattern == MovDir[x][y])
7775 int blocking_element =
7776 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7778 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7781 element = Feld[x][y]; /* element might have changed */
7785 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7786 DrawLevelElementAnimation(x, y, element);
7788 if (DONT_TOUCH(element))
7789 TestIfBadThingTouchesPlayer(x, y);
7794 InitMovingField(x, y, MovDir[x][y]);
7796 PlayLevelSoundAction(x, y, ACTION_MOVING);
7800 ContinueMoving(x, y);
7803 void ContinueMoving(int x, int y)
7805 int element = Feld[x][y];
7806 struct ElementInfo *ei = &element_info[element];
7807 int direction = MovDir[x][y];
7808 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7809 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7810 int newx = x + dx, newy = y + dy;
7811 int stored = Store[x][y];
7812 int stored_new = Store[newx][newy];
7813 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7814 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7815 boolean last_line = (newy == lev_fieldy - 1);
7817 MovPos[x][y] += getElementMoveStepsize(x, y);
7819 if (pushed_by_player) /* special case: moving object pushed by player */
7820 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7822 if (ABS(MovPos[x][y]) < TILEX)
7825 int ee = Feld[x][y];
7826 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7827 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7829 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7830 x, y, ABS(MovPos[x][y]),
7832 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7835 DrawLevelField(x, y);
7837 return; /* element is still moving */
7840 /* element reached destination field */
7842 Feld[x][y] = EL_EMPTY;
7843 Feld[newx][newy] = element;
7844 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7846 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7848 element = Feld[newx][newy] = EL_ACID;
7850 else if (element == EL_MOLE)
7852 Feld[x][y] = EL_SAND;
7854 DrawLevelFieldCrumbledSandNeighbours(x, y);
7856 else if (element == EL_QUICKSAND_FILLING)
7858 element = Feld[newx][newy] = get_next_element(element);
7859 Store[newx][newy] = Store[x][y];
7861 else if (element == EL_QUICKSAND_EMPTYING)
7863 Feld[x][y] = get_next_element(element);
7864 element = Feld[newx][newy] = Store[x][y];
7866 else if (element == EL_QUICKSAND_FAST_FILLING)
7868 element = Feld[newx][newy] = get_next_element(element);
7869 Store[newx][newy] = Store[x][y];
7871 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7873 Feld[x][y] = get_next_element(element);
7874 element = Feld[newx][newy] = Store[x][y];
7876 else if (element == EL_MAGIC_WALL_FILLING)
7878 element = Feld[newx][newy] = get_next_element(element);
7879 if (!game.magic_wall_active)
7880 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7881 Store[newx][newy] = Store[x][y];
7883 else if (element == EL_MAGIC_WALL_EMPTYING)
7885 Feld[x][y] = get_next_element(element);
7886 if (!game.magic_wall_active)
7887 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7888 element = Feld[newx][newy] = Store[x][y];
7890 #if USE_NEW_CUSTOM_VALUE
7891 InitField(newx, newy, FALSE);
7894 else if (element == EL_BD_MAGIC_WALL_FILLING)
7896 element = Feld[newx][newy] = get_next_element(element);
7897 if (!game.magic_wall_active)
7898 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7899 Store[newx][newy] = Store[x][y];
7901 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7903 Feld[x][y] = get_next_element(element);
7904 if (!game.magic_wall_active)
7905 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7906 element = Feld[newx][newy] = Store[x][y];
7908 #if USE_NEW_CUSTOM_VALUE
7909 InitField(newx, newy, FALSE);
7912 else if (element == EL_DC_MAGIC_WALL_FILLING)
7914 element = Feld[newx][newy] = get_next_element(element);
7915 if (!game.magic_wall_active)
7916 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7917 Store[newx][newy] = Store[x][y];
7919 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7921 Feld[x][y] = get_next_element(element);
7922 if (!game.magic_wall_active)
7923 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7924 element = Feld[newx][newy] = Store[x][y];
7926 #if USE_NEW_CUSTOM_VALUE
7927 InitField(newx, newy, FALSE);
7930 else if (element == EL_AMOEBA_DROPPING)
7932 Feld[x][y] = get_next_element(element);
7933 element = Feld[newx][newy] = Store[x][y];
7935 else if (element == EL_SOKOBAN_OBJECT)
7938 Feld[x][y] = Back[x][y];
7940 if (Back[newx][newy])
7941 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7943 Back[x][y] = Back[newx][newy] = 0;
7946 Store[x][y] = EL_EMPTY;
7951 MovDelay[newx][newy] = 0;
7953 if (CAN_CHANGE_OR_HAS_ACTION(element))
7955 /* copy element change control values to new field */
7956 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7957 ChangePage[newx][newy] = ChangePage[x][y];
7958 ChangeCount[newx][newy] = ChangeCount[x][y];
7959 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7962 #if USE_NEW_CUSTOM_VALUE
7963 CustomValue[newx][newy] = CustomValue[x][y];
7966 ChangeDelay[x][y] = 0;
7967 ChangePage[x][y] = -1;
7968 ChangeCount[x][y] = 0;
7969 ChangeEvent[x][y] = -1;
7971 #if USE_NEW_CUSTOM_VALUE
7972 CustomValue[x][y] = 0;
7975 /* copy animation control values to new field */
7976 GfxFrame[newx][newy] = GfxFrame[x][y];
7977 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7978 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7979 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7981 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7983 /* some elements can leave other elements behind after moving */
7985 if (ei->move_leave_element != EL_EMPTY &&
7986 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7987 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7989 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7990 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7991 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7994 int move_leave_element = ei->move_leave_element;
7998 /* this makes it possible to leave the removed element again */
7999 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8000 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8002 /* this makes it possible to leave the removed element again */
8003 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8004 move_leave_element = stored;
8007 /* this makes it possible to leave the removed element again */
8008 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8009 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8010 move_leave_element = stored;
8013 Feld[x][y] = move_leave_element;
8015 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8016 MovDir[x][y] = direction;
8018 InitField(x, y, FALSE);
8020 if (GFX_CRUMBLED(Feld[x][y]))
8021 DrawLevelFieldCrumbledSandNeighbours(x, y);
8023 if (ELEM_IS_PLAYER(move_leave_element))
8024 RelocatePlayer(x, y, move_leave_element);
8027 /* do this after checking for left-behind element */
8028 ResetGfxAnimation(x, y); /* reset animation values for old field */
8030 if (!CAN_MOVE(element) ||
8031 (CAN_FALL(element) && direction == MV_DOWN &&
8032 (element == EL_SPRING ||
8033 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8034 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8035 GfxDir[x][y] = MovDir[newx][newy] = 0;
8037 DrawLevelField(x, y);
8038 DrawLevelField(newx, newy);
8040 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8042 /* prevent pushed element from moving on in pushed direction */
8043 if (pushed_by_player && CAN_MOVE(element) &&
8044 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8045 !(element_info[element].move_pattern & direction))
8046 TurnRound(newx, newy);
8048 /* prevent elements on conveyor belt from moving on in last direction */
8049 if (pushed_by_conveyor && CAN_FALL(element) &&
8050 direction & MV_HORIZONTAL)
8051 MovDir[newx][newy] = 0;
8053 if (!pushed_by_player)
8055 int nextx = newx + dx, nexty = newy + dy;
8056 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8058 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8060 if (CAN_FALL(element) && direction == MV_DOWN)
8061 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8063 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8064 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8066 #if USE_FIX_IMPACT_COLLISION
8067 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8068 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8072 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8074 TestIfBadThingTouchesPlayer(newx, newy);
8075 TestIfBadThingTouchesFriend(newx, newy);
8077 if (!IS_CUSTOM_ELEMENT(element))
8078 TestIfBadThingTouchesOtherBadThing(newx, newy);
8080 else if (element == EL_PENGUIN)
8081 TestIfFriendTouchesBadThing(newx, newy);
8083 /* give the player one last chance (one more frame) to move away */
8084 if (CAN_FALL(element) && direction == MV_DOWN &&
8085 (last_line || (!IS_FREE(x, newy + 1) &&
8086 (!IS_PLAYER(x, newy + 1) ||
8087 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8090 if (pushed_by_player && !game.use_change_when_pushing_bug)
8092 int push_side = MV_DIR_OPPOSITE(direction);
8093 struct PlayerInfo *player = PLAYERINFO(x, y);
8095 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8096 player->index_bit, push_side);
8097 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8098 player->index_bit, push_side);
8101 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8102 MovDelay[newx][newy] = 1;
8104 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8106 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8109 if (ChangePage[newx][newy] != -1) /* delayed change */
8111 int page = ChangePage[newx][newy];
8112 struct ElementChangeInfo *change = &ei->change_page[page];
8114 ChangePage[newx][newy] = -1;
8116 if (change->can_change)
8118 if (ChangeElement(newx, newy, element, page))
8120 if (change->post_change_function)
8121 change->post_change_function(newx, newy);
8125 if (change->has_action)
8126 ExecuteCustomElementAction(newx, newy, element, page);
8130 TestIfElementHitsCustomElement(newx, newy, direction);
8131 TestIfPlayerTouchesCustomElement(newx, newy);
8132 TestIfElementTouchesCustomElement(newx, newy);
8134 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8135 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8136 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8137 MV_DIR_OPPOSITE(direction));
8140 int AmoebeNachbarNr(int ax, int ay)
8143 int element = Feld[ax][ay];
8145 static int xy[4][2] =
8153 for (i = 0; i < NUM_DIRECTIONS; i++)
8155 int x = ax + xy[i][0];
8156 int y = ay + xy[i][1];
8158 if (!IN_LEV_FIELD(x, y))
8161 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8162 group_nr = AmoebaNr[x][y];
8168 void AmoebenVereinigen(int ax, int ay)
8170 int i, x, y, xx, yy;
8171 int new_group_nr = AmoebaNr[ax][ay];
8172 static int xy[4][2] =
8180 if (new_group_nr == 0)
8183 for (i = 0; i < NUM_DIRECTIONS; i++)
8188 if (!IN_LEV_FIELD(x, y))
8191 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8192 Feld[x][y] == EL_BD_AMOEBA ||
8193 Feld[x][y] == EL_AMOEBA_DEAD) &&
8194 AmoebaNr[x][y] != new_group_nr)
8196 int old_group_nr = AmoebaNr[x][y];
8198 if (old_group_nr == 0)
8201 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8202 AmoebaCnt[old_group_nr] = 0;
8203 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8204 AmoebaCnt2[old_group_nr] = 0;
8206 SCAN_PLAYFIELD(xx, yy)
8208 if (AmoebaNr[xx][yy] == old_group_nr)
8209 AmoebaNr[xx][yy] = new_group_nr;
8215 void AmoebeUmwandeln(int ax, int ay)
8219 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8221 int group_nr = AmoebaNr[ax][ay];
8226 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8227 printf("AmoebeUmwandeln(): This should never happen!\n");
8232 SCAN_PLAYFIELD(x, y)
8234 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8237 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8241 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8242 SND_AMOEBA_TURNING_TO_GEM :
8243 SND_AMOEBA_TURNING_TO_ROCK));
8248 static int xy[4][2] =
8256 for (i = 0; i < NUM_DIRECTIONS; i++)
8261 if (!IN_LEV_FIELD(x, y))
8264 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8266 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8267 SND_AMOEBA_TURNING_TO_GEM :
8268 SND_AMOEBA_TURNING_TO_ROCK));
8275 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8278 int group_nr = AmoebaNr[ax][ay];
8279 boolean done = FALSE;
8284 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8285 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8290 SCAN_PLAYFIELD(x, y)
8292 if (AmoebaNr[x][y] == group_nr &&
8293 (Feld[x][y] == EL_AMOEBA_DEAD ||
8294 Feld[x][y] == EL_BD_AMOEBA ||
8295 Feld[x][y] == EL_AMOEBA_GROWING))
8298 Feld[x][y] = new_element;
8299 InitField(x, y, FALSE);
8300 DrawLevelField(x, y);
8306 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8307 SND_BD_AMOEBA_TURNING_TO_ROCK :
8308 SND_BD_AMOEBA_TURNING_TO_GEM));
8311 void AmoebeWaechst(int x, int y)
8313 static unsigned long sound_delay = 0;
8314 static unsigned long sound_delay_value = 0;
8316 if (!MovDelay[x][y]) /* start new growing cycle */
8320 if (DelayReached(&sound_delay, sound_delay_value))
8322 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8323 sound_delay_value = 30;
8327 if (MovDelay[x][y]) /* wait some time before growing bigger */
8330 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8332 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8333 6 - MovDelay[x][y]);
8335 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8338 if (!MovDelay[x][y])
8340 Feld[x][y] = Store[x][y];
8342 DrawLevelField(x, y);
8347 void AmoebaDisappearing(int x, int y)
8349 static unsigned long sound_delay = 0;
8350 static unsigned long sound_delay_value = 0;
8352 if (!MovDelay[x][y]) /* start new shrinking cycle */
8356 if (DelayReached(&sound_delay, sound_delay_value))
8357 sound_delay_value = 30;
8360 if (MovDelay[x][y]) /* wait some time before shrinking */
8363 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8365 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8366 6 - MovDelay[x][y]);
8368 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8371 if (!MovDelay[x][y])
8373 Feld[x][y] = EL_EMPTY;
8374 DrawLevelField(x, y);
8376 /* don't let mole enter this field in this cycle;
8377 (give priority to objects falling to this field from above) */
8383 void AmoebeAbleger(int ax, int ay)
8386 int element = Feld[ax][ay];
8387 int graphic = el2img(element);
8388 int newax = ax, neway = ay;
8389 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8390 static int xy[4][2] =
8398 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8400 Feld[ax][ay] = EL_AMOEBA_DEAD;
8401 DrawLevelField(ax, ay);
8405 if (IS_ANIMATED(graphic))
8406 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8408 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8409 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8411 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8414 if (MovDelay[ax][ay])
8418 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8421 int x = ax + xy[start][0];
8422 int y = ay + xy[start][1];
8424 if (!IN_LEV_FIELD(x, y))
8427 if (IS_FREE(x, y) ||
8428 CAN_GROW_INTO(Feld[x][y]) ||
8429 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8430 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8436 if (newax == ax && neway == ay)
8439 else /* normal or "filled" (BD style) amoeba */
8442 boolean waiting_for_player = FALSE;
8444 for (i = 0; i < NUM_DIRECTIONS; i++)
8446 int j = (start + i) % 4;
8447 int x = ax + xy[j][0];
8448 int y = ay + xy[j][1];
8450 if (!IN_LEV_FIELD(x, y))
8453 if (IS_FREE(x, y) ||
8454 CAN_GROW_INTO(Feld[x][y]) ||
8455 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8456 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8462 else if (IS_PLAYER(x, y))
8463 waiting_for_player = TRUE;
8466 if (newax == ax && neway == ay) /* amoeba cannot grow */
8468 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8470 Feld[ax][ay] = EL_AMOEBA_DEAD;
8471 DrawLevelField(ax, ay);
8472 AmoebaCnt[AmoebaNr[ax][ay]]--;
8474 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8476 if (element == EL_AMOEBA_FULL)
8477 AmoebeUmwandeln(ax, ay);
8478 else if (element == EL_BD_AMOEBA)
8479 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8484 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8486 /* amoeba gets larger by growing in some direction */
8488 int new_group_nr = AmoebaNr[ax][ay];
8491 if (new_group_nr == 0)
8493 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8494 printf("AmoebeAbleger(): This should never happen!\n");
8499 AmoebaNr[newax][neway] = new_group_nr;
8500 AmoebaCnt[new_group_nr]++;
8501 AmoebaCnt2[new_group_nr]++;
8503 /* if amoeba touches other amoeba(s) after growing, unify them */
8504 AmoebenVereinigen(newax, neway);
8506 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8508 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8514 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8515 (neway == lev_fieldy - 1 && newax != ax))
8517 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8518 Store[newax][neway] = element;
8520 else if (neway == ay || element == EL_EMC_DRIPPER)
8522 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8524 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8528 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8529 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8530 Store[ax][ay] = EL_AMOEBA_DROP;
8531 ContinueMoving(ax, ay);
8535 DrawLevelField(newax, neway);
8538 void Life(int ax, int ay)
8542 int element = Feld[ax][ay];
8543 int graphic = el2img(element);
8544 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8546 boolean changed = FALSE;
8548 if (IS_ANIMATED(graphic))
8549 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8554 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8555 MovDelay[ax][ay] = life_time;
8557 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8560 if (MovDelay[ax][ay])
8564 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8566 int xx = ax+x1, yy = ay+y1;
8569 if (!IN_LEV_FIELD(xx, yy))
8572 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8574 int x = xx+x2, y = yy+y2;
8576 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8579 if (((Feld[x][y] == element ||
8580 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8582 (IS_FREE(x, y) && Stop[x][y]))
8586 if (xx == ax && yy == ay) /* field in the middle */
8588 if (nachbarn < life_parameter[0] ||
8589 nachbarn > life_parameter[1])
8591 Feld[xx][yy] = EL_EMPTY;
8593 DrawLevelField(xx, yy);
8594 Stop[xx][yy] = TRUE;
8598 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8599 { /* free border field */
8600 if (nachbarn >= life_parameter[2] &&
8601 nachbarn <= life_parameter[3])
8603 Feld[xx][yy] = element;
8604 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8606 DrawLevelField(xx, yy);
8607 Stop[xx][yy] = TRUE;
8614 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8615 SND_GAME_OF_LIFE_GROWING);
8618 static void InitRobotWheel(int x, int y)
8620 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8623 static void RunRobotWheel(int x, int y)
8625 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8628 static void StopRobotWheel(int x, int y)
8630 if (ZX == x && ZY == y)
8634 static void InitTimegateWheel(int x, int y)
8636 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8639 static void RunTimegateWheel(int x, int y)
8641 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8644 static void InitMagicBallDelay(int x, int y)
8647 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8649 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8653 static void ActivateMagicBall(int bx, int by)
8657 if (level.ball_random)
8659 int pos_border = RND(8); /* select one of the eight border elements */
8660 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8661 int xx = pos_content % 3;
8662 int yy = pos_content / 3;
8667 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8668 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8672 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8674 int xx = x - bx + 1;
8675 int yy = y - by + 1;
8677 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8678 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8682 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8685 void CheckExit(int x, int y)
8687 if (local_player->gems_still_needed > 0 ||
8688 local_player->sokobanfields_still_needed > 0 ||
8689 local_player->lights_still_needed > 0)
8691 int element = Feld[x][y];
8692 int graphic = el2img(element);
8694 if (IS_ANIMATED(graphic))
8695 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8700 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8703 Feld[x][y] = EL_EXIT_OPENING;
8705 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8708 void CheckExitEM(int x, int y)
8710 if (local_player->gems_still_needed > 0 ||
8711 local_player->sokobanfields_still_needed > 0 ||
8712 local_player->lights_still_needed > 0)
8714 int element = Feld[x][y];
8715 int graphic = el2img(element);
8717 if (IS_ANIMATED(graphic))
8718 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8723 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8726 Feld[x][y] = EL_EM_EXIT_OPENING;
8728 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8731 void CheckExitSteel(int x, int y)
8733 if (local_player->gems_still_needed > 0 ||
8734 local_player->sokobanfields_still_needed > 0 ||
8735 local_player->lights_still_needed > 0)
8737 int element = Feld[x][y];
8738 int graphic = el2img(element);
8740 if (IS_ANIMATED(graphic))
8741 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8746 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8749 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8751 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8754 void CheckExitSteelEM(int x, int y)
8756 if (local_player->gems_still_needed > 0 ||
8757 local_player->sokobanfields_still_needed > 0 ||
8758 local_player->lights_still_needed > 0)
8760 int element = Feld[x][y];
8761 int graphic = el2img(element);
8763 if (IS_ANIMATED(graphic))
8764 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8769 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8772 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8774 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8777 void CheckExitSP(int x, int y)
8779 if (local_player->gems_still_needed > 0)
8781 int element = Feld[x][y];
8782 int graphic = el2img(element);
8784 if (IS_ANIMATED(graphic))
8785 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8790 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8793 Feld[x][y] = EL_SP_EXIT_OPENING;
8795 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8798 static void CloseAllOpenTimegates()
8802 SCAN_PLAYFIELD(x, y)
8804 int element = Feld[x][y];
8806 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8808 Feld[x][y] = EL_TIMEGATE_CLOSING;
8810 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8815 void DrawTwinkleOnField(int x, int y)
8817 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8820 if (Feld[x][y] == EL_BD_DIAMOND)
8823 if (MovDelay[x][y] == 0) /* next animation frame */
8824 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8826 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8830 if (setup.direct_draw && MovDelay[x][y])
8831 SetDrawtoField(DRAW_BUFFERED);
8833 DrawLevelElementAnimation(x, y, Feld[x][y]);
8835 if (MovDelay[x][y] != 0)
8837 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8838 10 - MovDelay[x][y]);
8840 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8842 if (setup.direct_draw)
8846 dest_x = FX + SCREENX(x) * TILEX;
8847 dest_y = FY + SCREENY(y) * TILEY;
8849 BlitBitmap(drawto_field, window,
8850 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8851 SetDrawtoField(DRAW_DIRECT);
8857 void MauerWaechst(int x, int y)
8861 if (!MovDelay[x][y]) /* next animation frame */
8862 MovDelay[x][y] = 3 * delay;
8864 if (MovDelay[x][y]) /* wait some time before next frame */
8868 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8870 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8871 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8873 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8876 if (!MovDelay[x][y])
8878 if (MovDir[x][y] == MV_LEFT)
8880 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8881 DrawLevelField(x - 1, y);
8883 else if (MovDir[x][y] == MV_RIGHT)
8885 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8886 DrawLevelField(x + 1, y);
8888 else if (MovDir[x][y] == MV_UP)
8890 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8891 DrawLevelField(x, y - 1);
8895 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8896 DrawLevelField(x, y + 1);
8899 Feld[x][y] = Store[x][y];
8901 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8902 DrawLevelField(x, y);
8907 void MauerAbleger(int ax, int ay)
8909 int element = Feld[ax][ay];
8910 int graphic = el2img(element);
8911 boolean oben_frei = FALSE, unten_frei = FALSE;
8912 boolean links_frei = FALSE, rechts_frei = FALSE;
8913 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8914 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8915 boolean new_wall = FALSE;
8917 if (IS_ANIMATED(graphic))
8918 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8920 if (!MovDelay[ax][ay]) /* start building new wall */
8921 MovDelay[ax][ay] = 6;
8923 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8926 if (MovDelay[ax][ay])
8930 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8932 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8934 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8936 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8939 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8940 element == EL_EXPANDABLE_WALL_ANY)
8944 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8945 Store[ax][ay-1] = element;
8946 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8947 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8948 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8949 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8954 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8955 Store[ax][ay+1] = element;
8956 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8957 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8958 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8959 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8964 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8965 element == EL_EXPANDABLE_WALL_ANY ||
8966 element == EL_EXPANDABLE_WALL ||
8967 element == EL_BD_EXPANDABLE_WALL)
8971 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8972 Store[ax-1][ay] = element;
8973 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8974 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8975 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8976 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8982 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8983 Store[ax+1][ay] = element;
8984 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8985 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8986 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8987 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8992 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8993 DrawLevelField(ax, ay);
8995 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8997 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8998 unten_massiv = TRUE;
8999 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9000 links_massiv = TRUE;
9001 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9002 rechts_massiv = TRUE;
9004 if (((oben_massiv && unten_massiv) ||
9005 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9006 element == EL_EXPANDABLE_WALL) &&
9007 ((links_massiv && rechts_massiv) ||
9008 element == EL_EXPANDABLE_WALL_VERTICAL))
9009 Feld[ax][ay] = EL_WALL;
9012 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9015 void MauerAblegerStahl(int ax, int ay)
9017 int element = Feld[ax][ay];
9018 int graphic = el2img(element);
9019 boolean oben_frei = FALSE, unten_frei = FALSE;
9020 boolean links_frei = FALSE, rechts_frei = FALSE;
9021 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9022 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9023 boolean new_wall = FALSE;
9025 if (IS_ANIMATED(graphic))
9026 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9028 if (!MovDelay[ax][ay]) /* start building new wall */
9029 MovDelay[ax][ay] = 6;
9031 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9034 if (MovDelay[ax][ay])
9038 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9040 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9042 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9044 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9047 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9048 element == EL_EXPANDABLE_STEELWALL_ANY)
9052 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9053 Store[ax][ay-1] = element;
9054 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9055 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9056 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9057 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9062 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9063 Store[ax][ay+1] = element;
9064 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9065 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9066 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9067 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9072 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9073 element == EL_EXPANDABLE_STEELWALL_ANY)
9077 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9078 Store[ax-1][ay] = element;
9079 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9080 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9081 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9082 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9088 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9089 Store[ax+1][ay] = element;
9090 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9091 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9092 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9093 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9098 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9100 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9101 unten_massiv = TRUE;
9102 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9103 links_massiv = TRUE;
9104 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9105 rechts_massiv = TRUE;
9107 if (((oben_massiv && unten_massiv) ||
9108 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9109 ((links_massiv && rechts_massiv) ||
9110 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9111 Feld[ax][ay] = EL_WALL;
9114 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9117 void CheckForDragon(int x, int y)
9120 boolean dragon_found = FALSE;
9121 static int xy[4][2] =
9129 for (i = 0; i < NUM_DIRECTIONS; i++)
9131 for (j = 0; j < 4; j++)
9133 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9135 if (IN_LEV_FIELD(xx, yy) &&
9136 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9138 if (Feld[xx][yy] == EL_DRAGON)
9139 dragon_found = TRUE;
9148 for (i = 0; i < NUM_DIRECTIONS; i++)
9150 for (j = 0; j < 3; j++)
9152 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9154 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9156 Feld[xx][yy] = EL_EMPTY;
9157 DrawLevelField(xx, yy);
9166 static void InitBuggyBase(int x, int y)
9168 int element = Feld[x][y];
9169 int activating_delay = FRAMES_PER_SECOND / 4;
9172 (element == EL_SP_BUGGY_BASE ?
9173 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9174 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9176 element == EL_SP_BUGGY_BASE_ACTIVE ?
9177 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9180 static void WarnBuggyBase(int x, int y)
9183 static int xy[4][2] =
9191 for (i = 0; i < NUM_DIRECTIONS; i++)
9193 int xx = x + xy[i][0];
9194 int yy = y + xy[i][1];
9196 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9198 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9205 static void InitTrap(int x, int y)
9207 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9210 static void ActivateTrap(int x, int y)
9212 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9215 static void ChangeActiveTrap(int x, int y)
9217 int graphic = IMG_TRAP_ACTIVE;
9219 /* if new animation frame was drawn, correct crumbled sand border */
9220 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9221 DrawLevelFieldCrumbledSand(x, y);
9224 static int getSpecialActionElement(int element, int number, int base_element)
9226 return (element != EL_EMPTY ? element :
9227 number != -1 ? base_element + number - 1 :
9231 static int getModifiedActionNumber(int value_old, int operator, int operand,
9232 int value_min, int value_max)
9234 int value_new = (operator == CA_MODE_SET ? operand :
9235 operator == CA_MODE_ADD ? value_old + operand :
9236 operator == CA_MODE_SUBTRACT ? value_old - operand :
9237 operator == CA_MODE_MULTIPLY ? value_old * operand :
9238 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9239 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9242 return (value_new < value_min ? value_min :
9243 value_new > value_max ? value_max :
9247 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9249 struct ElementInfo *ei = &element_info[element];
9250 struct ElementChangeInfo *change = &ei->change_page[page];
9251 int target_element = change->target_element;
9252 int action_type = change->action_type;
9253 int action_mode = change->action_mode;
9254 int action_arg = change->action_arg;
9257 if (!change->has_action)
9260 /* ---------- determine action paramater values -------------------------- */
9262 int level_time_value =
9263 (level.time > 0 ? TimeLeft :
9266 int action_arg_element =
9267 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9268 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9269 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9272 int action_arg_direction =
9273 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9274 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9275 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9276 change->actual_trigger_side :
9277 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9278 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9281 int action_arg_number_min =
9282 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9285 int action_arg_number_max =
9286 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9287 action_type == CA_SET_LEVEL_GEMS ? 999 :
9288 action_type == CA_SET_LEVEL_TIME ? 9999 :
9289 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9290 action_type == CA_SET_CE_VALUE ? 9999 :
9291 action_type == CA_SET_CE_SCORE ? 9999 :
9294 int action_arg_number_reset =
9295 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9296 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9297 action_type == CA_SET_LEVEL_TIME ? level.time :
9298 action_type == CA_SET_LEVEL_SCORE ? 0 :
9299 #if USE_NEW_CUSTOM_VALUE
9300 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9302 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9304 action_type == CA_SET_CE_SCORE ? 0 :
9307 int action_arg_number =
9308 (action_arg <= CA_ARG_MAX ? action_arg :
9309 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9310 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9311 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9312 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9313 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9314 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9315 #if USE_NEW_CUSTOM_VALUE
9316 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9318 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9320 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9321 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9322 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9323 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9324 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9325 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9326 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9327 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9328 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9329 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9330 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9333 int action_arg_number_old =
9334 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9335 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9336 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9337 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9338 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9341 int action_arg_number_new =
9342 getModifiedActionNumber(action_arg_number_old,
9343 action_mode, action_arg_number,
9344 action_arg_number_min, action_arg_number_max);
9346 int trigger_player_bits =
9347 (change->actual_trigger_player >= EL_PLAYER_1 &&
9348 change->actual_trigger_player <= EL_PLAYER_4 ?
9349 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9352 int action_arg_player_bits =
9353 (action_arg >= CA_ARG_PLAYER_1 &&
9354 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9355 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9358 /* ---------- execute action -------------------------------------------- */
9360 switch (action_type)
9367 /* ---------- level actions ------------------------------------------- */
9369 case CA_RESTART_LEVEL:
9371 game.restart_level = TRUE;
9376 case CA_SHOW_ENVELOPE:
9378 int element = getSpecialActionElement(action_arg_element,
9379 action_arg_number, EL_ENVELOPE_1);
9381 if (IS_ENVELOPE(element))
9382 local_player->show_envelope = element;
9387 case CA_SET_LEVEL_TIME:
9389 if (level.time > 0) /* only modify limited time value */
9391 TimeLeft = action_arg_number_new;
9394 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9396 DisplayGameControlValues();
9398 DrawGameValue_Time(TimeLeft);
9401 if (!TimeLeft && setup.time_limit)
9402 for (i = 0; i < MAX_PLAYERS; i++)
9403 KillPlayer(&stored_player[i]);
9409 case CA_SET_LEVEL_SCORE:
9411 local_player->score = action_arg_number_new;
9414 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9416 DisplayGameControlValues();
9418 DrawGameValue_Score(local_player->score);
9424 case CA_SET_LEVEL_GEMS:
9426 local_player->gems_still_needed = action_arg_number_new;
9429 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9431 DisplayGameControlValues();
9433 DrawGameValue_Emeralds(local_player->gems_still_needed);
9439 #if !USE_PLAYER_GRAVITY
9440 case CA_SET_LEVEL_GRAVITY:
9442 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9443 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9444 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9450 case CA_SET_LEVEL_WIND:
9452 game.wind_direction = action_arg_direction;
9457 /* ---------- player actions ------------------------------------------ */
9459 case CA_MOVE_PLAYER:
9461 /* automatically move to the next field in specified direction */
9462 for (i = 0; i < MAX_PLAYERS; i++)
9463 if (trigger_player_bits & (1 << i))
9464 stored_player[i].programmed_action = action_arg_direction;
9469 case CA_EXIT_PLAYER:
9471 for (i = 0; i < MAX_PLAYERS; i++)
9472 if (action_arg_player_bits & (1 << i))
9473 PlayerWins(&stored_player[i]);
9478 case CA_KILL_PLAYER:
9480 for (i = 0; i < MAX_PLAYERS; i++)
9481 if (action_arg_player_bits & (1 << i))
9482 KillPlayer(&stored_player[i]);
9487 case CA_SET_PLAYER_KEYS:
9489 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9490 int element = getSpecialActionElement(action_arg_element,
9491 action_arg_number, EL_KEY_1);
9493 if (IS_KEY(element))
9495 for (i = 0; i < MAX_PLAYERS; i++)
9497 if (trigger_player_bits & (1 << i))
9499 stored_player[i].key[KEY_NR(element)] = key_state;
9501 DrawGameDoorValues();
9509 case CA_SET_PLAYER_SPEED:
9511 for (i = 0; i < MAX_PLAYERS; i++)
9513 if (trigger_player_bits & (1 << i))
9515 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9517 if (action_arg == CA_ARG_SPEED_FASTER &&
9518 stored_player[i].cannot_move)
9520 action_arg_number = STEPSIZE_VERY_SLOW;
9522 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9523 action_arg == CA_ARG_SPEED_FASTER)
9525 action_arg_number = 2;
9526 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9529 else if (action_arg == CA_ARG_NUMBER_RESET)
9531 action_arg_number = level.initial_player_stepsize[i];
9535 getModifiedActionNumber(move_stepsize,
9538 action_arg_number_min,
9539 action_arg_number_max);
9541 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9548 case CA_SET_PLAYER_SHIELD:
9550 for (i = 0; i < MAX_PLAYERS; i++)
9552 if (trigger_player_bits & (1 << i))
9554 if (action_arg == CA_ARG_SHIELD_OFF)
9556 stored_player[i].shield_normal_time_left = 0;
9557 stored_player[i].shield_deadly_time_left = 0;
9559 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9561 stored_player[i].shield_normal_time_left = 999999;
9563 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9565 stored_player[i].shield_normal_time_left = 999999;
9566 stored_player[i].shield_deadly_time_left = 999999;
9574 #if USE_PLAYER_GRAVITY
9575 case CA_SET_PLAYER_GRAVITY:
9577 for (i = 0; i < MAX_PLAYERS; i++)
9579 if (trigger_player_bits & (1 << i))
9581 stored_player[i].gravity =
9582 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9583 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9584 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9585 stored_player[i].gravity);
9593 case CA_SET_PLAYER_ARTWORK:
9595 for (i = 0; i < MAX_PLAYERS; i++)
9597 if (trigger_player_bits & (1 << i))
9599 int artwork_element = action_arg_element;
9601 if (action_arg == CA_ARG_ELEMENT_RESET)
9603 (level.use_artwork_element[i] ? level.artwork_element[i] :
9604 stored_player[i].element_nr);
9606 #if USE_GFX_RESET_PLAYER_ARTWORK
9607 if (stored_player[i].artwork_element != artwork_element)
9608 stored_player[i].Frame = 0;
9611 stored_player[i].artwork_element = artwork_element;
9613 SetPlayerWaiting(&stored_player[i], FALSE);
9615 /* set number of special actions for bored and sleeping animation */
9616 stored_player[i].num_special_action_bored =
9617 get_num_special_action(artwork_element,
9618 ACTION_BORING_1, ACTION_BORING_LAST);
9619 stored_player[i].num_special_action_sleeping =
9620 get_num_special_action(artwork_element,
9621 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9628 /* ---------- CE actions ---------------------------------------------- */
9630 case CA_SET_CE_VALUE:
9632 #if USE_NEW_CUSTOM_VALUE
9633 int last_ce_value = CustomValue[x][y];
9635 CustomValue[x][y] = action_arg_number_new;
9637 if (CustomValue[x][y] != last_ce_value)
9639 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9640 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9642 if (CustomValue[x][y] == 0)
9644 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9645 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9653 case CA_SET_CE_SCORE:
9655 #if USE_NEW_CUSTOM_VALUE
9656 int last_ce_score = ei->collect_score;
9658 ei->collect_score = action_arg_number_new;
9660 if (ei->collect_score != last_ce_score)
9662 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9663 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9665 if (ei->collect_score == 0)
9669 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9670 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9673 This is a very special case that seems to be a mixture between
9674 CheckElementChange() and CheckTriggeredElementChange(): while
9675 the first one only affects single elements that are triggered
9676 directly, the second one affects multiple elements in the playfield
9677 that are triggered indirectly by another element. This is a third
9678 case: Changing the CE score always affects multiple identical CEs,
9679 so every affected CE must be checked, not only the single CE for
9680 which the CE score was changed in the first place (as every instance
9681 of that CE shares the same CE score, and therefore also can change)!
9683 SCAN_PLAYFIELD(xx, yy)
9685 if (Feld[xx][yy] == element)
9686 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9687 CE_SCORE_GETS_ZERO);
9696 /* ---------- engine actions ------------------------------------------ */
9698 case CA_SET_ENGINE_SCAN_MODE:
9700 InitPlayfieldScanMode(action_arg);
9710 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9712 int old_element = Feld[x][y];
9713 int new_element = GetElementFromGroupElement(element);
9714 int previous_move_direction = MovDir[x][y];
9715 #if USE_NEW_CUSTOM_VALUE
9716 int last_ce_value = CustomValue[x][y];
9718 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9719 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9720 boolean add_player_onto_element = (new_element_is_player &&
9721 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9722 /* this breaks SnakeBite when a snake is
9723 halfway through a door that closes */
9724 /* NOW FIXED AT LEVEL INIT IN files.c */
9725 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9727 IS_WALKABLE(old_element));
9730 /* check if element under the player changes from accessible to unaccessible
9731 (needed for special case of dropping element which then changes) */
9732 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9733 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9741 if (!add_player_onto_element)
9743 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9744 RemoveMovingField(x, y);
9748 Feld[x][y] = new_element;
9750 #if !USE_GFX_RESET_GFX_ANIMATION
9751 ResetGfxAnimation(x, y);
9752 ResetRandomAnimationValue(x, y);
9755 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9756 MovDir[x][y] = previous_move_direction;
9758 #if USE_NEW_CUSTOM_VALUE
9759 if (element_info[new_element].use_last_ce_value)
9760 CustomValue[x][y] = last_ce_value;
9763 InitField_WithBug1(x, y, FALSE);
9765 new_element = Feld[x][y]; /* element may have changed */
9767 #if USE_GFX_RESET_GFX_ANIMATION
9768 ResetGfxAnimation(x, y);
9769 ResetRandomAnimationValue(x, y);
9772 DrawLevelField(x, y);
9774 if (GFX_CRUMBLED(new_element))
9775 DrawLevelFieldCrumbledSandNeighbours(x, y);
9779 /* check if element under the player changes from accessible to unaccessible
9780 (needed for special case of dropping element which then changes) */
9781 /* (must be checked after creating new element for walkable group elements) */
9782 #if USE_FIX_KILLED_BY_NON_WALKABLE
9783 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9784 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9791 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9792 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9801 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9802 if (new_element_is_player)
9803 RelocatePlayer(x, y, new_element);
9806 ChangeCount[x][y]++; /* count number of changes in the same frame */
9808 TestIfBadThingTouchesPlayer(x, y);
9809 TestIfPlayerTouchesCustomElement(x, y);
9810 TestIfElementTouchesCustomElement(x, y);
9813 static void CreateField(int x, int y, int element)
9815 CreateFieldExt(x, y, element, FALSE);
9818 static void CreateElementFromChange(int x, int y, int element)
9820 element = GET_VALID_RUNTIME_ELEMENT(element);
9822 #if USE_STOP_CHANGED_ELEMENTS
9823 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9825 int old_element = Feld[x][y];
9827 /* prevent changed element from moving in same engine frame
9828 unless both old and new element can either fall or move */
9829 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9830 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9835 CreateFieldExt(x, y, element, TRUE);
9838 static boolean ChangeElement(int x, int y, int element, int page)
9840 struct ElementInfo *ei = &element_info[element];
9841 struct ElementChangeInfo *change = &ei->change_page[page];
9842 int ce_value = CustomValue[x][y];
9843 int ce_score = ei->collect_score;
9845 int old_element = Feld[x][y];
9847 /* always use default change event to prevent running into a loop */
9848 if (ChangeEvent[x][y] == -1)
9849 ChangeEvent[x][y] = CE_DELAY;
9851 if (ChangeEvent[x][y] == CE_DELAY)
9853 /* reset actual trigger element, trigger player and action element */
9854 change->actual_trigger_element = EL_EMPTY;
9855 change->actual_trigger_player = EL_PLAYER_1;
9856 change->actual_trigger_side = CH_SIDE_NONE;
9857 change->actual_trigger_ce_value = 0;
9858 change->actual_trigger_ce_score = 0;
9861 /* do not change elements more than a specified maximum number of changes */
9862 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9865 ChangeCount[x][y]++; /* count number of changes in the same frame */
9867 if (change->explode)
9874 if (change->use_target_content)
9876 boolean complete_replace = TRUE;
9877 boolean can_replace[3][3];
9880 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9883 boolean is_walkable;
9884 boolean is_diggable;
9885 boolean is_collectible;
9886 boolean is_removable;
9887 boolean is_destructible;
9888 int ex = x + xx - 1;
9889 int ey = y + yy - 1;
9890 int content_element = change->target_content.e[xx][yy];
9893 can_replace[xx][yy] = TRUE;
9895 if (ex == x && ey == y) /* do not check changing element itself */
9898 if (content_element == EL_EMPTY_SPACE)
9900 can_replace[xx][yy] = FALSE; /* do not replace border with space */
9905 if (!IN_LEV_FIELD(ex, ey))
9907 can_replace[xx][yy] = FALSE;
9908 complete_replace = FALSE;
9915 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9916 e = MovingOrBlocked2Element(ex, ey);
9918 is_empty = (IS_FREE(ex, ey) ||
9919 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9921 is_walkable = (is_empty || IS_WALKABLE(e));
9922 is_diggable = (is_empty || IS_DIGGABLE(e));
9923 is_collectible = (is_empty || IS_COLLECTIBLE(e));
9924 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9925 is_removable = (is_diggable || is_collectible);
9927 can_replace[xx][yy] =
9928 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9929 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9930 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9931 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9932 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9933 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9934 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9936 if (!can_replace[xx][yy])
9937 complete_replace = FALSE;
9940 if (!change->only_if_complete || complete_replace)
9942 boolean something_has_changed = FALSE;
9944 if (change->only_if_complete && change->use_random_replace &&
9945 RND(100) < change->random_percentage)
9948 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9950 int ex = x + xx - 1;
9951 int ey = y + yy - 1;
9952 int content_element;
9954 if (can_replace[xx][yy] && (!change->use_random_replace ||
9955 RND(100) < change->random_percentage))
9957 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9958 RemoveMovingField(ex, ey);
9960 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9962 content_element = change->target_content.e[xx][yy];
9963 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9964 ce_value, ce_score);
9966 CreateElementFromChange(ex, ey, target_element);
9968 something_has_changed = TRUE;
9970 /* for symmetry reasons, freeze newly created border elements */
9971 if (ex != x || ey != y)
9972 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9976 if (something_has_changed)
9978 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9979 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9985 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9986 ce_value, ce_score);
9988 if (element == EL_DIAGONAL_GROWING ||
9989 element == EL_DIAGONAL_SHRINKING)
9991 target_element = Store[x][y];
9993 Store[x][y] = EL_EMPTY;
9996 CreateElementFromChange(x, y, target_element);
9998 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9999 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10002 /* this uses direct change before indirect change */
10003 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10008 #if USE_NEW_DELAYED_ACTION
10010 static void HandleElementChange(int x, int y, int page)
10012 int element = MovingOrBlocked2Element(x, y);
10013 struct ElementInfo *ei = &element_info[element];
10014 struct ElementChangeInfo *change = &ei->change_page[page];
10017 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10018 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10021 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10022 x, y, element, element_info[element].token_name);
10023 printf("HandleElementChange(): This should never happen!\n");
10028 /* this can happen with classic bombs on walkable, changing elements */
10029 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10032 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10033 ChangeDelay[x][y] = 0;
10039 if (ChangeDelay[x][y] == 0) /* initialize element change */
10041 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10043 if (change->can_change)
10046 /* !!! not clear why graphic animation should be reset at all here !!! */
10047 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10048 #if USE_GFX_RESET_WHEN_NOT_MOVING
10049 /* when a custom element is about to change (for example by change delay),
10050 do not reset graphic animation when the custom element is moving */
10051 if (!IS_MOVING(x, y))
10054 ResetGfxAnimation(x, y);
10055 ResetRandomAnimationValue(x, y);
10059 if (change->pre_change_function)
10060 change->pre_change_function(x, y);
10064 ChangeDelay[x][y]--;
10066 if (ChangeDelay[x][y] != 0) /* continue element change */
10068 if (change->can_change)
10070 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10072 if (IS_ANIMATED(graphic))
10073 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10075 if (change->change_function)
10076 change->change_function(x, y);
10079 else /* finish element change */
10081 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10083 page = ChangePage[x][y];
10084 ChangePage[x][y] = -1;
10086 change = &ei->change_page[page];
10089 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10091 ChangeDelay[x][y] = 1; /* try change after next move step */
10092 ChangePage[x][y] = page; /* remember page to use for change */
10097 if (change->can_change)
10099 if (ChangeElement(x, y, element, page))
10101 if (change->post_change_function)
10102 change->post_change_function(x, y);
10106 if (change->has_action)
10107 ExecuteCustomElementAction(x, y, element, page);
10113 static void HandleElementChange(int x, int y, int page)
10115 int element = MovingOrBlocked2Element(x, y);
10116 struct ElementInfo *ei = &element_info[element];
10117 struct ElementChangeInfo *change = &ei->change_page[page];
10120 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10123 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10124 x, y, element, element_info[element].token_name);
10125 printf("HandleElementChange(): This should never happen!\n");
10130 /* this can happen with classic bombs on walkable, changing elements */
10131 if (!CAN_CHANGE(element))
10134 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10135 ChangeDelay[x][y] = 0;
10141 if (ChangeDelay[x][y] == 0) /* initialize element change */
10143 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10145 ResetGfxAnimation(x, y);
10146 ResetRandomAnimationValue(x, y);
10148 if (change->pre_change_function)
10149 change->pre_change_function(x, y);
10152 ChangeDelay[x][y]--;
10154 if (ChangeDelay[x][y] != 0) /* continue element change */
10156 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10158 if (IS_ANIMATED(graphic))
10159 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10161 if (change->change_function)
10162 change->change_function(x, y);
10164 else /* finish element change */
10166 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10168 page = ChangePage[x][y];
10169 ChangePage[x][y] = -1;
10171 change = &ei->change_page[page];
10174 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10176 ChangeDelay[x][y] = 1; /* try change after next move step */
10177 ChangePage[x][y] = page; /* remember page to use for change */
10182 if (ChangeElement(x, y, element, page))
10184 if (change->post_change_function)
10185 change->post_change_function(x, y);
10192 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10193 int trigger_element,
10195 int trigger_player,
10199 boolean change_done_any = FALSE;
10200 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10203 if (!(trigger_events[trigger_element][trigger_event]))
10207 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10208 trigger_event, recursion_loop_depth, recursion_loop_detected,
10209 recursion_loop_element, EL_NAME(recursion_loop_element));
10212 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10214 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10216 int element = EL_CUSTOM_START + i;
10217 boolean change_done = FALSE;
10220 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10221 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10224 for (p = 0; p < element_info[element].num_change_pages; p++)
10226 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10228 if (change->can_change_or_has_action &&
10229 change->has_event[trigger_event] &&
10230 change->trigger_side & trigger_side &&
10231 change->trigger_player & trigger_player &&
10232 change->trigger_page & trigger_page_bits &&
10233 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10235 change->actual_trigger_element = trigger_element;
10236 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10237 change->actual_trigger_side = trigger_side;
10238 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10239 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10241 if ((change->can_change && !change_done) || change->has_action)
10245 SCAN_PLAYFIELD(x, y)
10247 if (Feld[x][y] == element)
10249 if (change->can_change && !change_done)
10251 ChangeDelay[x][y] = 1;
10252 ChangeEvent[x][y] = trigger_event;
10254 HandleElementChange(x, y, p);
10256 #if USE_NEW_DELAYED_ACTION
10257 else if (change->has_action)
10259 ExecuteCustomElementAction(x, y, element, p);
10260 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10263 if (change->has_action)
10265 ExecuteCustomElementAction(x, y, element, p);
10266 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10272 if (change->can_change)
10274 change_done = TRUE;
10275 change_done_any = TRUE;
10282 RECURSION_LOOP_DETECTION_END();
10284 return change_done_any;
10287 static boolean CheckElementChangeExt(int x, int y,
10289 int trigger_element,
10291 int trigger_player,
10294 boolean change_done = FALSE;
10297 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10298 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10301 if (Feld[x][y] == EL_BLOCKED)
10303 Blocked2Moving(x, y, &x, &y);
10304 element = Feld[x][y];
10308 /* check if element has already changed */
10309 if (Feld[x][y] != element)
10312 /* check if element has already changed or is about to change after moving */
10313 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10314 Feld[x][y] != element) ||
10316 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10317 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10318 ChangePage[x][y] != -1)))
10323 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10324 trigger_event, recursion_loop_depth, recursion_loop_detected,
10325 recursion_loop_element, EL_NAME(recursion_loop_element));
10328 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10330 for (p = 0; p < element_info[element].num_change_pages; p++)
10332 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10334 /* check trigger element for all events where the element that is checked
10335 for changing interacts with a directly adjacent element -- this is
10336 different to element changes that affect other elements to change on the
10337 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10338 boolean check_trigger_element =
10339 (trigger_event == CE_TOUCHING_X ||
10340 trigger_event == CE_HITTING_X ||
10341 trigger_event == CE_HIT_BY_X ||
10343 /* this one was forgotten until 3.2.3 */
10344 trigger_event == CE_DIGGING_X);
10347 if (change->can_change_or_has_action &&
10348 change->has_event[trigger_event] &&
10349 change->trigger_side & trigger_side &&
10350 change->trigger_player & trigger_player &&
10351 (!check_trigger_element ||
10352 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10354 change->actual_trigger_element = trigger_element;
10355 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10356 change->actual_trigger_side = trigger_side;
10357 change->actual_trigger_ce_value = CustomValue[x][y];
10358 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10360 /* special case: trigger element not at (x,y) position for some events */
10361 if (check_trigger_element)
10373 { 0, 0 }, { 0, 0 }, { 0, 0 },
10377 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10378 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10380 change->actual_trigger_ce_value = CustomValue[xx][yy];
10381 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10384 if (change->can_change && !change_done)
10386 ChangeDelay[x][y] = 1;
10387 ChangeEvent[x][y] = trigger_event;
10389 HandleElementChange(x, y, p);
10391 change_done = TRUE;
10393 #if USE_NEW_DELAYED_ACTION
10394 else if (change->has_action)
10396 ExecuteCustomElementAction(x, y, element, p);
10397 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10400 if (change->has_action)
10402 ExecuteCustomElementAction(x, y, element, p);
10403 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10409 RECURSION_LOOP_DETECTION_END();
10411 return change_done;
10414 static void PlayPlayerSound(struct PlayerInfo *player)
10416 int jx = player->jx, jy = player->jy;
10417 int sound_element = player->artwork_element;
10418 int last_action = player->last_action_waiting;
10419 int action = player->action_waiting;
10421 if (player->is_waiting)
10423 if (action != last_action)
10424 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10426 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10430 if (action != last_action)
10431 StopSound(element_info[sound_element].sound[last_action]);
10433 if (last_action == ACTION_SLEEPING)
10434 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10438 static void PlayAllPlayersSound()
10442 for (i = 0; i < MAX_PLAYERS; i++)
10443 if (stored_player[i].active)
10444 PlayPlayerSound(&stored_player[i]);
10447 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10449 boolean last_waiting = player->is_waiting;
10450 int move_dir = player->MovDir;
10452 player->dir_waiting = move_dir;
10453 player->last_action_waiting = player->action_waiting;
10457 if (!last_waiting) /* not waiting -> waiting */
10459 player->is_waiting = TRUE;
10461 player->frame_counter_bored =
10463 game.player_boring_delay_fixed +
10464 GetSimpleRandom(game.player_boring_delay_random);
10465 player->frame_counter_sleeping =
10467 game.player_sleeping_delay_fixed +
10468 GetSimpleRandom(game.player_sleeping_delay_random);
10470 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10473 if (game.player_sleeping_delay_fixed +
10474 game.player_sleeping_delay_random > 0 &&
10475 player->anim_delay_counter == 0 &&
10476 player->post_delay_counter == 0 &&
10477 FrameCounter >= player->frame_counter_sleeping)
10478 player->is_sleeping = TRUE;
10479 else if (game.player_boring_delay_fixed +
10480 game.player_boring_delay_random > 0 &&
10481 FrameCounter >= player->frame_counter_bored)
10482 player->is_bored = TRUE;
10484 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10485 player->is_bored ? ACTION_BORING :
10488 if (player->is_sleeping && player->use_murphy)
10490 /* special case for sleeping Murphy when leaning against non-free tile */
10492 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10493 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10494 !IS_MOVING(player->jx - 1, player->jy)))
10495 move_dir = MV_LEFT;
10496 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10497 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10498 !IS_MOVING(player->jx + 1, player->jy)))
10499 move_dir = MV_RIGHT;
10501 player->is_sleeping = FALSE;
10503 player->dir_waiting = move_dir;
10506 if (player->is_sleeping)
10508 if (player->num_special_action_sleeping > 0)
10510 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10512 int last_special_action = player->special_action_sleeping;
10513 int num_special_action = player->num_special_action_sleeping;
10514 int special_action =
10515 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10516 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10517 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10518 last_special_action + 1 : ACTION_SLEEPING);
10519 int special_graphic =
10520 el_act_dir2img(player->artwork_element, special_action, move_dir);
10522 player->anim_delay_counter =
10523 graphic_info[special_graphic].anim_delay_fixed +
10524 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10525 player->post_delay_counter =
10526 graphic_info[special_graphic].post_delay_fixed +
10527 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10529 player->special_action_sleeping = special_action;
10532 if (player->anim_delay_counter > 0)
10534 player->action_waiting = player->special_action_sleeping;
10535 player->anim_delay_counter--;
10537 else if (player->post_delay_counter > 0)
10539 player->post_delay_counter--;
10543 else if (player->is_bored)
10545 if (player->num_special_action_bored > 0)
10547 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10549 int special_action =
10550 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10551 int special_graphic =
10552 el_act_dir2img(player->artwork_element, special_action, move_dir);
10554 player->anim_delay_counter =
10555 graphic_info[special_graphic].anim_delay_fixed +
10556 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10557 player->post_delay_counter =
10558 graphic_info[special_graphic].post_delay_fixed +
10559 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10561 player->special_action_bored = special_action;
10564 if (player->anim_delay_counter > 0)
10566 player->action_waiting = player->special_action_bored;
10567 player->anim_delay_counter--;
10569 else if (player->post_delay_counter > 0)
10571 player->post_delay_counter--;
10576 else if (last_waiting) /* waiting -> not waiting */
10578 player->is_waiting = FALSE;
10579 player->is_bored = FALSE;
10580 player->is_sleeping = FALSE;
10582 player->frame_counter_bored = -1;
10583 player->frame_counter_sleeping = -1;
10585 player->anim_delay_counter = 0;
10586 player->post_delay_counter = 0;
10588 player->dir_waiting = player->MovDir;
10589 player->action_waiting = ACTION_DEFAULT;
10591 player->special_action_bored = ACTION_DEFAULT;
10592 player->special_action_sleeping = ACTION_DEFAULT;
10596 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10598 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10599 int left = player_action & JOY_LEFT;
10600 int right = player_action & JOY_RIGHT;
10601 int up = player_action & JOY_UP;
10602 int down = player_action & JOY_DOWN;
10603 int button1 = player_action & JOY_BUTTON_1;
10604 int button2 = player_action & JOY_BUTTON_2;
10605 int dx = (left ? -1 : right ? 1 : 0);
10606 int dy = (up ? -1 : down ? 1 : 0);
10608 if (!player->active || tape.pausing)
10614 snapped = SnapField(player, dx, dy);
10618 dropped = DropElement(player);
10620 moved = MovePlayer(player, dx, dy);
10623 if (tape.single_step && tape.recording && !tape.pausing)
10625 if (button1 || (dropped && !moved))
10627 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10628 SnapField(player, 0, 0); /* stop snapping */
10632 SetPlayerWaiting(player, FALSE);
10634 return player_action;
10638 /* no actions for this player (no input at player's configured device) */
10640 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10641 SnapField(player, 0, 0);
10642 CheckGravityMovementWhenNotMoving(player);
10644 if (player->MovPos == 0)
10645 SetPlayerWaiting(player, TRUE);
10647 if (player->MovPos == 0) /* needed for tape.playing */
10648 player->is_moving = FALSE;
10650 player->is_dropping = FALSE;
10651 player->is_dropping_pressed = FALSE;
10652 player->drop_pressed_delay = 0;
10658 static void CheckLevelTime()
10662 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10664 if (level.native_em_level->lev->home == 0) /* all players at home */
10666 PlayerWins(local_player);
10668 AllPlayersGone = TRUE;
10670 level.native_em_level->lev->home = -1;
10673 if (level.native_em_level->ply[0]->alive == 0 &&
10674 level.native_em_level->ply[1]->alive == 0 &&
10675 level.native_em_level->ply[2]->alive == 0 &&
10676 level.native_em_level->ply[3]->alive == 0) /* all dead */
10677 AllPlayersGone = TRUE;
10680 if (TimeFrames >= FRAMES_PER_SECOND)
10685 for (i = 0; i < MAX_PLAYERS; i++)
10687 struct PlayerInfo *player = &stored_player[i];
10689 if (SHIELD_ON(player))
10691 player->shield_normal_time_left--;
10693 if (player->shield_deadly_time_left > 0)
10694 player->shield_deadly_time_left--;
10698 if (!local_player->LevelSolved && !level.use_step_counter)
10706 if (TimeLeft <= 10 && setup.time_limit)
10707 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10710 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10712 DisplayGameControlValues();
10714 DrawGameValue_Time(TimeLeft);
10717 if (!TimeLeft && setup.time_limit)
10719 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10720 level.native_em_level->lev->killed_out_of_time = TRUE;
10722 for (i = 0; i < MAX_PLAYERS; i++)
10723 KillPlayer(&stored_player[i]);
10727 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10729 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10731 DisplayGameControlValues();
10734 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10735 DrawGameValue_Time(TimePlayed);
10738 level.native_em_level->lev->time =
10739 (level.time == 0 ? TimePlayed : TimeLeft);
10742 if (tape.recording || tape.playing)
10743 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10746 DrawGameDoorValues();
10749 void AdvanceFrameAndPlayerCounters(int player_nr)
10753 /* advance frame counters (global frame counter and time frame counter) */
10757 /* advance player counters (counters for move delay, move animation etc.) */
10758 for (i = 0; i < MAX_PLAYERS; i++)
10760 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10761 int move_delay_value = stored_player[i].move_delay_value;
10762 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10764 if (!advance_player_counters) /* not all players may be affected */
10767 #if USE_NEW_PLAYER_ANIM
10768 if (move_frames == 0) /* less than one move per game frame */
10770 int stepsize = TILEX / move_delay_value;
10771 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10772 int count = (stored_player[i].is_moving ?
10773 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10775 if (count % delay == 0)
10780 stored_player[i].Frame += move_frames;
10782 if (stored_player[i].MovPos != 0)
10783 stored_player[i].StepFrame += move_frames;
10785 if (stored_player[i].move_delay > 0)
10786 stored_player[i].move_delay--;
10788 /* due to bugs in previous versions, counter must count up, not down */
10789 if (stored_player[i].push_delay != -1)
10790 stored_player[i].push_delay++;
10792 if (stored_player[i].drop_delay > 0)
10793 stored_player[i].drop_delay--;
10795 if (stored_player[i].is_dropping_pressed)
10796 stored_player[i].drop_pressed_delay++;
10800 void StartGameActions(boolean init_network_game, boolean record_tape,
10803 unsigned long new_random_seed = InitRND(random_seed);
10806 TapeStartRecording(new_random_seed);
10808 #if defined(NETWORK_AVALIABLE)
10809 if (init_network_game)
10811 SendToServer_StartPlaying();
10822 static unsigned long game_frame_delay = 0;
10823 unsigned long game_frame_delay_value;
10824 byte *recorded_player_action;
10825 byte summarized_player_action = 0;
10826 byte tape_action[MAX_PLAYERS];
10829 /* detect endless loops, caused by custom element programming */
10830 if (recursion_loop_detected && recursion_loop_depth == 0)
10832 char *message = getStringCat3("Internal Error ! Element ",
10833 EL_NAME(recursion_loop_element),
10834 " caused endless loop ! Quit the game ?");
10836 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10837 EL_NAME(recursion_loop_element));
10839 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10841 recursion_loop_detected = FALSE; /* if game should be continued */
10848 if (game.restart_level)
10849 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10851 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10853 if (level.native_em_level->lev->home == 0) /* all players at home */
10855 PlayerWins(local_player);
10857 AllPlayersGone = TRUE;
10859 level.native_em_level->lev->home = -1;
10862 if (level.native_em_level->ply[0]->alive == 0 &&
10863 level.native_em_level->ply[1]->alive == 0 &&
10864 level.native_em_level->ply[2]->alive == 0 &&
10865 level.native_em_level->ply[3]->alive == 0) /* all dead */
10866 AllPlayersGone = TRUE;
10869 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10872 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10875 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10878 game_frame_delay_value =
10879 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10881 if (tape.playing && tape.warp_forward && !tape.pausing)
10882 game_frame_delay_value = 0;
10884 /* ---------- main game synchronization point ---------- */
10886 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10888 if (network_playing && !network_player_action_received)
10890 /* try to get network player actions in time */
10892 #if defined(NETWORK_AVALIABLE)
10893 /* last chance to get network player actions without main loop delay */
10894 HandleNetworking();
10897 /* game was quit by network peer */
10898 if (game_status != GAME_MODE_PLAYING)
10901 if (!network_player_action_received)
10902 return; /* failed to get network player actions in time */
10904 /* do not yet reset "network_player_action_received" (for tape.pausing) */
10910 /* at this point we know that we really continue executing the game */
10912 network_player_action_received = FALSE;
10914 /* when playing tape, read previously recorded player input from tape data */
10915 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10918 /* TapePlayAction() may return NULL when toggling to "pause before death" */
10923 if (tape.set_centered_player)
10925 game.centered_player_nr_next = tape.centered_player_nr_next;
10926 game.set_centered_player = TRUE;
10929 for (i = 0; i < MAX_PLAYERS; i++)
10931 summarized_player_action |= stored_player[i].action;
10933 if (!network_playing)
10934 stored_player[i].effective_action = stored_player[i].action;
10937 #if defined(NETWORK_AVALIABLE)
10938 if (network_playing)
10939 SendToServer_MovePlayer(summarized_player_action);
10942 if (!options.network && !setup.team_mode)
10943 local_player->effective_action = summarized_player_action;
10945 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10947 for (i = 0; i < MAX_PLAYERS; i++)
10948 stored_player[i].effective_action =
10949 (i == game.centered_player_nr ? summarized_player_action : 0);
10952 if (recorded_player_action != NULL)
10953 for (i = 0; i < MAX_PLAYERS; i++)
10954 stored_player[i].effective_action = recorded_player_action[i];
10956 for (i = 0; i < MAX_PLAYERS; i++)
10958 tape_action[i] = stored_player[i].effective_action;
10960 /* (this can only happen in the R'n'D game engine) */
10961 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10962 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10965 /* only record actions from input devices, but not programmed actions */
10966 if (tape.recording)
10967 TapeRecordAction(tape_action);
10969 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10971 GameActions_EM_Main();
10979 void GameActions_EM_Main()
10981 byte effective_action[MAX_PLAYERS];
10982 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10985 for (i = 0; i < MAX_PLAYERS; i++)
10986 effective_action[i] = stored_player[i].effective_action;
10988 GameActions_EM(effective_action, warp_mode);
10992 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10995 void GameActions_RND()
10997 int magic_wall_x = 0, magic_wall_y = 0;
10998 int i, x, y, element, graphic;
11000 InitPlayfieldScanModeVars();
11002 #if USE_ONE_MORE_CHANGE_PER_FRAME
11003 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11005 SCAN_PLAYFIELD(x, y)
11007 ChangeCount[x][y] = 0;
11008 ChangeEvent[x][y] = -1;
11013 if (game.set_centered_player)
11015 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11017 /* switching to "all players" only possible if all players fit to screen */
11018 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11020 game.centered_player_nr_next = game.centered_player_nr;
11021 game.set_centered_player = FALSE;
11024 /* do not switch focus to non-existing (or non-active) player */
11025 if (game.centered_player_nr_next >= 0 &&
11026 !stored_player[game.centered_player_nr_next].active)
11028 game.centered_player_nr_next = game.centered_player_nr;
11029 game.set_centered_player = FALSE;
11033 if (game.set_centered_player &&
11034 ScreenMovPos == 0) /* screen currently aligned at tile position */
11038 if (game.centered_player_nr_next == -1)
11040 setScreenCenteredToAllPlayers(&sx, &sy);
11044 sx = stored_player[game.centered_player_nr_next].jx;
11045 sy = stored_player[game.centered_player_nr_next].jy;
11048 game.centered_player_nr = game.centered_player_nr_next;
11049 game.set_centered_player = FALSE;
11051 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11052 DrawGameDoorValues();
11055 for (i = 0; i < MAX_PLAYERS; i++)
11057 int actual_player_action = stored_player[i].effective_action;
11060 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11061 - rnd_equinox_tetrachloride 048
11062 - rnd_equinox_tetrachloride_ii 096
11063 - rnd_emanuel_schmieg 002
11064 - doctor_sloan_ww 001, 020
11066 if (stored_player[i].MovPos == 0)
11067 CheckGravityMovement(&stored_player[i]);
11070 /* overwrite programmed action with tape action */
11071 if (stored_player[i].programmed_action)
11072 actual_player_action = stored_player[i].programmed_action;
11074 PlayerActions(&stored_player[i], actual_player_action);
11076 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11079 ScrollScreen(NULL, SCROLL_GO_ON);
11081 /* for backwards compatibility, the following code emulates a fixed bug that
11082 occured when pushing elements (causing elements that just made their last
11083 pushing step to already (if possible) make their first falling step in the
11084 same game frame, which is bad); this code is also needed to use the famous
11085 "spring push bug" which is used in older levels and might be wanted to be
11086 used also in newer levels, but in this case the buggy pushing code is only
11087 affecting the "spring" element and no other elements */
11089 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11091 for (i = 0; i < MAX_PLAYERS; i++)
11093 struct PlayerInfo *player = &stored_player[i];
11094 int x = player->jx;
11095 int y = player->jy;
11097 if (player->active && player->is_pushing && player->is_moving &&
11099 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11100 Feld[x][y] == EL_SPRING))
11102 ContinueMoving(x, y);
11104 /* continue moving after pushing (this is actually a bug) */
11105 if (!IS_MOVING(x, y))
11106 Stop[x][y] = FALSE;
11112 debug_print_timestamp(0, "start main loop profiling");
11115 SCAN_PLAYFIELD(x, y)
11117 ChangeCount[x][y] = 0;
11118 ChangeEvent[x][y] = -1;
11120 /* this must be handled before main playfield loop */
11121 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11124 if (MovDelay[x][y] <= 0)
11128 #if USE_NEW_SNAP_DELAY
11129 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11132 if (MovDelay[x][y] <= 0)
11135 DrawLevelField(x, y);
11137 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11143 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11145 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11146 printf("GameActions(): This should never happen!\n");
11148 ChangePage[x][y] = -1;
11152 Stop[x][y] = FALSE;
11153 if (WasJustMoving[x][y] > 0)
11154 WasJustMoving[x][y]--;
11155 if (WasJustFalling[x][y] > 0)
11156 WasJustFalling[x][y]--;
11157 if (CheckCollision[x][y] > 0)
11158 CheckCollision[x][y]--;
11159 if (CheckImpact[x][y] > 0)
11160 CheckImpact[x][y]--;
11164 /* reset finished pushing action (not done in ContinueMoving() to allow
11165 continuous pushing animation for elements with zero push delay) */
11166 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11168 ResetGfxAnimation(x, y);
11169 DrawLevelField(x, y);
11173 if (IS_BLOCKED(x, y))
11177 Blocked2Moving(x, y, &oldx, &oldy);
11178 if (!IS_MOVING(oldx, oldy))
11180 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11181 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11182 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11183 printf("GameActions(): This should never happen!\n");
11190 debug_print_timestamp(0, "- time for pre-main loop:");
11193 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11194 SCAN_PLAYFIELD(x, y)
11196 element = Feld[x][y];
11197 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11202 int element2 = element;
11203 int graphic2 = graphic;
11205 int element2 = Feld[x][y];
11206 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11208 int last_gfx_frame = GfxFrame[x][y];
11210 if (graphic_info[graphic2].anim_global_sync)
11211 GfxFrame[x][y] = FrameCounter;
11212 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11213 GfxFrame[x][y] = CustomValue[x][y];
11214 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11215 GfxFrame[x][y] = element_info[element2].collect_score;
11216 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11217 GfxFrame[x][y] = ChangeDelay[x][y];
11219 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11220 DrawLevelGraphicAnimation(x, y, graphic2);
11223 ResetGfxFrame(x, y, TRUE);
11227 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11228 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11229 ResetRandomAnimationValue(x, y);
11233 SetRandomAnimationValue(x, y);
11237 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11240 #endif // -------------------- !!! TEST ONLY !!! --------------------
11243 debug_print_timestamp(0, "- time for TEST loop: -->");
11246 SCAN_PLAYFIELD(x, y)
11248 element = Feld[x][y];
11249 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11251 ResetGfxFrame(x, y, TRUE);
11253 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11254 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11255 ResetRandomAnimationValue(x, y);
11257 SetRandomAnimationValue(x, y);
11259 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11261 if (IS_INACTIVE(element))
11263 if (IS_ANIMATED(graphic))
11264 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11269 /* this may take place after moving, so 'element' may have changed */
11270 if (IS_CHANGING(x, y) &&
11271 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11273 int page = element_info[element].event_page_nr[CE_DELAY];
11276 HandleElementChange(x, y, page);
11278 if (CAN_CHANGE(element))
11279 HandleElementChange(x, y, page);
11281 if (HAS_ACTION(element))
11282 ExecuteCustomElementAction(x, y, element, page);
11285 element = Feld[x][y];
11286 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11289 #if 0 // ---------------------------------------------------------------------
11291 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11295 element = Feld[x][y];
11296 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11298 if (IS_ANIMATED(graphic) &&
11299 !IS_MOVING(x, y) &&
11301 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11303 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11304 DrawTwinkleOnField(x, y);
11306 else if (IS_MOVING(x, y))
11307 ContinueMoving(x, y);
11314 case EL_EM_EXIT_OPEN:
11315 case EL_SP_EXIT_OPEN:
11316 case EL_STEEL_EXIT_OPEN:
11317 case EL_EM_STEEL_EXIT_OPEN:
11318 case EL_SP_TERMINAL:
11319 case EL_SP_TERMINAL_ACTIVE:
11320 case EL_EXTRA_TIME:
11321 case EL_SHIELD_NORMAL:
11322 case EL_SHIELD_DEADLY:
11323 if (IS_ANIMATED(graphic))
11324 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11327 case EL_DYNAMITE_ACTIVE:
11328 case EL_EM_DYNAMITE_ACTIVE:
11329 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11330 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11331 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11332 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11333 case EL_SP_DISK_RED_ACTIVE:
11334 CheckDynamite(x, y);
11337 case EL_AMOEBA_GROWING:
11338 AmoebeWaechst(x, y);
11341 case EL_AMOEBA_SHRINKING:
11342 AmoebaDisappearing(x, y);
11345 #if !USE_NEW_AMOEBA_CODE
11346 case EL_AMOEBA_WET:
11347 case EL_AMOEBA_DRY:
11348 case EL_AMOEBA_FULL:
11350 case EL_EMC_DRIPPER:
11351 AmoebeAbleger(x, y);
11355 case EL_GAME_OF_LIFE:
11360 case EL_EXIT_CLOSED:
11364 case EL_EM_EXIT_CLOSED:
11368 case EL_STEEL_EXIT_CLOSED:
11369 CheckExitSteel(x, y);
11372 case EL_EM_STEEL_EXIT_CLOSED:
11373 CheckExitSteelEM(x, y);
11376 case EL_SP_EXIT_CLOSED:
11380 case EL_EXPANDABLE_WALL_GROWING:
11381 case EL_EXPANDABLE_STEELWALL_GROWING:
11382 MauerWaechst(x, y);
11385 case EL_EXPANDABLE_WALL:
11386 case EL_EXPANDABLE_WALL_HORIZONTAL:
11387 case EL_EXPANDABLE_WALL_VERTICAL:
11388 case EL_EXPANDABLE_WALL_ANY:
11389 case EL_BD_EXPANDABLE_WALL:
11390 MauerAbleger(x, y);
11393 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11394 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11395 case EL_EXPANDABLE_STEELWALL_ANY:
11396 MauerAblegerStahl(x, y);
11400 CheckForDragon(x, y);
11406 case EL_ELEMENT_SNAPPING:
11407 case EL_DIAGONAL_SHRINKING:
11408 case EL_DIAGONAL_GROWING:
11411 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11413 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11418 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11419 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11424 #else // ---------------------------------------------------------------------
11426 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11430 element = Feld[x][y];
11431 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11433 if (IS_ANIMATED(graphic) &&
11434 !IS_MOVING(x, y) &&
11436 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11438 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11439 DrawTwinkleOnField(x, y);
11441 else if ((element == EL_ACID ||
11442 element == EL_EXIT_OPEN ||
11443 element == EL_EM_EXIT_OPEN ||
11444 element == EL_SP_EXIT_OPEN ||
11445 element == EL_STEEL_EXIT_OPEN ||
11446 element == EL_EM_STEEL_EXIT_OPEN ||
11447 element == EL_SP_TERMINAL ||
11448 element == EL_SP_TERMINAL_ACTIVE ||
11449 element == EL_EXTRA_TIME ||
11450 element == EL_SHIELD_NORMAL ||
11451 element == EL_SHIELD_DEADLY) &&
11452 IS_ANIMATED(graphic))
11453 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11454 else if (IS_MOVING(x, y))
11455 ContinueMoving(x, y);
11456 else if (IS_ACTIVE_BOMB(element))
11457 CheckDynamite(x, y);
11458 else if (element == EL_AMOEBA_GROWING)
11459 AmoebeWaechst(x, y);
11460 else if (element == EL_AMOEBA_SHRINKING)
11461 AmoebaDisappearing(x, y);
11463 #if !USE_NEW_AMOEBA_CODE
11464 else if (IS_AMOEBALIVE(element))
11465 AmoebeAbleger(x, y);
11468 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11470 else if (element == EL_EXIT_CLOSED)
11472 else if (element == EL_EM_EXIT_CLOSED)
11474 else if (element == EL_STEEL_EXIT_CLOSED)
11475 CheckExitSteel(x, y);
11476 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11477 CheckExitSteelEM(x, y);
11478 else if (element == EL_SP_EXIT_CLOSED)
11480 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11481 element == EL_EXPANDABLE_STEELWALL_GROWING)
11482 MauerWaechst(x, y);
11483 else if (element == EL_EXPANDABLE_WALL ||
11484 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11485 element == EL_EXPANDABLE_WALL_VERTICAL ||
11486 element == EL_EXPANDABLE_WALL_ANY ||
11487 element == EL_BD_EXPANDABLE_WALL)
11488 MauerAbleger(x, y);
11489 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11490 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11491 element == EL_EXPANDABLE_STEELWALL_ANY)
11492 MauerAblegerStahl(x, y);
11493 else if (element == EL_FLAMES)
11494 CheckForDragon(x, y);
11495 else if (element == EL_EXPLOSION)
11496 ; /* drawing of correct explosion animation is handled separately */
11497 else if (element == EL_ELEMENT_SNAPPING ||
11498 element == EL_DIAGONAL_SHRINKING ||
11499 element == EL_DIAGONAL_GROWING)
11501 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11503 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11505 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11506 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11508 #endif // ---------------------------------------------------------------------
11510 if (IS_BELT_ACTIVE(element))
11511 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11513 if (game.magic_wall_active)
11515 int jx = local_player->jx, jy = local_player->jy;
11517 /* play the element sound at the position nearest to the player */
11518 if ((element == EL_MAGIC_WALL_FULL ||
11519 element == EL_MAGIC_WALL_ACTIVE ||
11520 element == EL_MAGIC_WALL_EMPTYING ||
11521 element == EL_BD_MAGIC_WALL_FULL ||
11522 element == EL_BD_MAGIC_WALL_ACTIVE ||
11523 element == EL_BD_MAGIC_WALL_EMPTYING ||
11524 element == EL_DC_MAGIC_WALL_FULL ||
11525 element == EL_DC_MAGIC_WALL_ACTIVE ||
11526 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11527 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11536 debug_print_timestamp(0, "- time for MAIN loop: -->");
11539 #if USE_NEW_AMOEBA_CODE
11540 /* new experimental amoeba growth stuff */
11541 if (!(FrameCounter % 8))
11543 static unsigned long random = 1684108901;
11545 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11547 x = RND(lev_fieldx);
11548 y = RND(lev_fieldy);
11549 element = Feld[x][y];
11551 if (!IS_PLAYER(x,y) &&
11552 (element == EL_EMPTY ||
11553 CAN_GROW_INTO(element) ||
11554 element == EL_QUICKSAND_EMPTY ||
11555 element == EL_QUICKSAND_FAST_EMPTY ||
11556 element == EL_ACID_SPLASH_LEFT ||
11557 element == EL_ACID_SPLASH_RIGHT))
11559 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11560 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11561 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11562 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11563 Feld[x][y] = EL_AMOEBA_DROP;
11566 random = random * 129 + 1;
11572 if (game.explosions_delayed)
11575 game.explosions_delayed = FALSE;
11577 SCAN_PLAYFIELD(x, y)
11579 element = Feld[x][y];
11581 if (ExplodeField[x][y])
11582 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11583 else if (element == EL_EXPLOSION)
11584 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11586 ExplodeField[x][y] = EX_TYPE_NONE;
11589 game.explosions_delayed = TRUE;
11592 if (game.magic_wall_active)
11594 if (!(game.magic_wall_time_left % 4))
11596 int element = Feld[magic_wall_x][magic_wall_y];
11598 if (element == EL_BD_MAGIC_WALL_FULL ||
11599 element == EL_BD_MAGIC_WALL_ACTIVE ||
11600 element == EL_BD_MAGIC_WALL_EMPTYING)
11601 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11602 else if (element == EL_DC_MAGIC_WALL_FULL ||
11603 element == EL_DC_MAGIC_WALL_ACTIVE ||
11604 element == EL_DC_MAGIC_WALL_EMPTYING)
11605 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11607 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11610 if (game.magic_wall_time_left > 0)
11612 game.magic_wall_time_left--;
11613 if (!game.magic_wall_time_left)
11615 SCAN_PLAYFIELD(x, y)
11617 element = Feld[x][y];
11619 if (element == EL_MAGIC_WALL_ACTIVE ||
11620 element == EL_MAGIC_WALL_FULL)
11622 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11623 DrawLevelField(x, y);
11625 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11626 element == EL_BD_MAGIC_WALL_FULL)
11628 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11629 DrawLevelField(x, y);
11631 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11632 element == EL_DC_MAGIC_WALL_FULL)
11634 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11635 DrawLevelField(x, y);
11639 game.magic_wall_active = FALSE;
11644 if (game.light_time_left > 0)
11646 game.light_time_left--;
11648 if (game.light_time_left == 0)
11649 RedrawAllLightSwitchesAndInvisibleElements();
11652 if (game.timegate_time_left > 0)
11654 game.timegate_time_left--;
11656 if (game.timegate_time_left == 0)
11657 CloseAllOpenTimegates();
11660 if (game.lenses_time_left > 0)
11662 game.lenses_time_left--;
11664 if (game.lenses_time_left == 0)
11665 RedrawAllInvisibleElementsForLenses();
11668 if (game.magnify_time_left > 0)
11670 game.magnify_time_left--;
11672 if (game.magnify_time_left == 0)
11673 RedrawAllInvisibleElementsForMagnifier();
11676 for (i = 0; i < MAX_PLAYERS; i++)
11678 struct PlayerInfo *player = &stored_player[i];
11680 if (SHIELD_ON(player))
11682 if (player->shield_deadly_time_left)
11683 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11684 else if (player->shield_normal_time_left)
11685 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11692 PlayAllPlayersSound();
11694 if (options.debug) /* calculate frames per second */
11696 static unsigned long fps_counter = 0;
11697 static int fps_frames = 0;
11698 unsigned long fps_delay_ms = Counter() - fps_counter;
11702 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11704 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11707 fps_counter = Counter();
11710 redraw_mask |= REDRAW_FPS;
11713 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11715 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11717 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11719 local_player->show_envelope = 0;
11723 debug_print_timestamp(0, "stop main loop profiling ");
11724 printf("----------------------------------------------------------\n");
11727 /* use random number generator in every frame to make it less predictable */
11728 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11732 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11734 int min_x = x, min_y = y, max_x = x, max_y = y;
11737 for (i = 0; i < MAX_PLAYERS; i++)
11739 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11741 if (!stored_player[i].active || &stored_player[i] == player)
11744 min_x = MIN(min_x, jx);
11745 min_y = MIN(min_y, jy);
11746 max_x = MAX(max_x, jx);
11747 max_y = MAX(max_y, jy);
11750 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11753 static boolean AllPlayersInVisibleScreen()
11757 for (i = 0; i < MAX_PLAYERS; i++)
11759 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11761 if (!stored_player[i].active)
11764 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11771 void ScrollLevel(int dx, int dy)
11774 static Bitmap *bitmap_db_field2 = NULL;
11775 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11782 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11783 /* only horizontal XOR vertical scroll direction allowed */
11784 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11789 if (bitmap_db_field2 == NULL)
11790 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11792 /* needed when blitting directly to same bitmap -- should not be needed with
11793 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11794 BlitBitmap(drawto_field, bitmap_db_field2,
11795 FX + TILEX * (dx == -1) - softscroll_offset,
11796 FY + TILEY * (dy == -1) - softscroll_offset,
11797 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11798 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11799 FX + TILEX * (dx == 1) - softscroll_offset,
11800 FY + TILEY * (dy == 1) - softscroll_offset);
11801 BlitBitmap(bitmap_db_field2, drawto_field,
11802 FX + TILEX * (dx == 1) - softscroll_offset,
11803 FY + TILEY * (dy == 1) - softscroll_offset,
11804 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11805 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11806 FX + TILEX * (dx == 1) - softscroll_offset,
11807 FY + TILEY * (dy == 1) - softscroll_offset);
11812 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11813 int xsize = (BX2 - BX1 + 1);
11814 int ysize = (BY2 - BY1 + 1);
11815 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11816 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11817 int step = (start < end ? +1 : -1);
11819 for (i = start; i != end; i += step)
11821 BlitBitmap(drawto_field, drawto_field,
11822 FX + TILEX * (dx != 0 ? i + step : 0),
11823 FY + TILEY * (dy != 0 ? i + step : 0),
11824 TILEX * (dx != 0 ? 1 : xsize),
11825 TILEY * (dy != 0 ? 1 : ysize),
11826 FX + TILEX * (dx != 0 ? i : 0),
11827 FY + TILEY * (dy != 0 ? i : 0));
11832 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11834 BlitBitmap(drawto_field, drawto_field,
11835 FX + TILEX * (dx == -1) - softscroll_offset,
11836 FY + TILEY * (dy == -1) - softscroll_offset,
11837 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11838 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11839 FX + TILEX * (dx == 1) - softscroll_offset,
11840 FY + TILEY * (dy == 1) - softscroll_offset);
11846 x = (dx == 1 ? BX1 : BX2);
11847 for (y = BY1; y <= BY2; y++)
11848 DrawScreenField(x, y);
11853 y = (dy == 1 ? BY1 : BY2);
11854 for (x = BX1; x <= BX2; x++)
11855 DrawScreenField(x, y);
11858 redraw_mask |= REDRAW_FIELD;
11861 static boolean canFallDown(struct PlayerInfo *player)
11863 int jx = player->jx, jy = player->jy;
11865 return (IN_LEV_FIELD(jx, jy + 1) &&
11866 (IS_FREE(jx, jy + 1) ||
11867 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11868 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11869 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11872 static boolean canPassField(int x, int y, int move_dir)
11874 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11875 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11876 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11877 int nextx = x + dx;
11878 int nexty = y + dy;
11879 int element = Feld[x][y];
11881 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11882 !CAN_MOVE(element) &&
11883 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11884 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11885 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11888 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11890 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11891 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11892 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11896 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11897 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11898 (IS_DIGGABLE(Feld[newx][newy]) ||
11899 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11900 canPassField(newx, newy, move_dir)));
11903 static void CheckGravityMovement(struct PlayerInfo *player)
11905 #if USE_PLAYER_GRAVITY
11906 if (player->gravity && !player->programmed_action)
11908 if (game.gravity && !player->programmed_action)
11911 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11912 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11913 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11914 int jx = player->jx, jy = player->jy;
11915 boolean player_is_moving_to_valid_field =
11916 (!player_is_snapping &&
11917 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11918 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11919 boolean player_can_fall_down = canFallDown(player);
11921 if (player_can_fall_down &&
11922 !player_is_moving_to_valid_field)
11923 player->programmed_action = MV_DOWN;
11927 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11929 return CheckGravityMovement(player);
11931 #if USE_PLAYER_GRAVITY
11932 if (player->gravity && !player->programmed_action)
11934 if (game.gravity && !player->programmed_action)
11937 int jx = player->jx, jy = player->jy;
11938 boolean field_under_player_is_free =
11939 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11940 boolean player_is_standing_on_valid_field =
11941 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11942 (IS_WALKABLE(Feld[jx][jy]) &&
11943 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11945 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11946 player->programmed_action = MV_DOWN;
11951 MovePlayerOneStep()
11952 -----------------------------------------------------------------------------
11953 dx, dy: direction (non-diagonal) to try to move the player to
11954 real_dx, real_dy: direction as read from input device (can be diagonal)
11957 boolean MovePlayerOneStep(struct PlayerInfo *player,
11958 int dx, int dy, int real_dx, int real_dy)
11960 int jx = player->jx, jy = player->jy;
11961 int new_jx = jx + dx, new_jy = jy + dy;
11962 #if !USE_FIXED_DONT_RUN_INTO
11966 boolean player_can_move = !player->cannot_move;
11968 if (!player->active || (!dx && !dy))
11969 return MP_NO_ACTION;
11971 player->MovDir = (dx < 0 ? MV_LEFT :
11972 dx > 0 ? MV_RIGHT :
11974 dy > 0 ? MV_DOWN : MV_NONE);
11976 if (!IN_LEV_FIELD(new_jx, new_jy))
11977 return MP_NO_ACTION;
11979 if (!player_can_move)
11981 if (player->MovPos == 0)
11983 player->is_moving = FALSE;
11984 player->is_digging = FALSE;
11985 player->is_collecting = FALSE;
11986 player->is_snapping = FALSE;
11987 player->is_pushing = FALSE;
11992 if (!options.network && game.centered_player_nr == -1 &&
11993 !AllPlayersInSight(player, new_jx, new_jy))
11994 return MP_NO_ACTION;
11996 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11997 return MP_NO_ACTION;
12000 #if !USE_FIXED_DONT_RUN_INTO
12001 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12003 /* (moved to DigField()) */
12004 if (player_can_move && DONT_RUN_INTO(element))
12006 if (element == EL_ACID && dx == 0 && dy == 1)
12008 SplashAcid(new_jx, new_jy);
12009 Feld[jx][jy] = EL_PLAYER_1;
12010 InitMovingField(jx, jy, MV_DOWN);
12011 Store[jx][jy] = EL_ACID;
12012 ContinueMoving(jx, jy);
12013 BuryPlayer(player);
12016 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12022 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12023 if (can_move != MP_MOVING)
12026 /* check if DigField() has caused relocation of the player */
12027 if (player->jx != jx || player->jy != jy)
12028 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12030 StorePlayer[jx][jy] = 0;
12031 player->last_jx = jx;
12032 player->last_jy = jy;
12033 player->jx = new_jx;
12034 player->jy = new_jy;
12035 StorePlayer[new_jx][new_jy] = player->element_nr;
12037 if (player->move_delay_value_next != -1)
12039 player->move_delay_value = player->move_delay_value_next;
12040 player->move_delay_value_next = -1;
12044 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12046 player->step_counter++;
12048 PlayerVisit[jx][jy] = FrameCounter;
12050 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12051 player->is_moving = TRUE;
12055 /* should better be called in MovePlayer(), but this breaks some tapes */
12056 ScrollPlayer(player, SCROLL_INIT);
12062 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12064 int jx = player->jx, jy = player->jy;
12065 int old_jx = jx, old_jy = jy;
12066 int moved = MP_NO_ACTION;
12068 if (!player->active)
12073 if (player->MovPos == 0)
12075 player->is_moving = FALSE;
12076 player->is_digging = FALSE;
12077 player->is_collecting = FALSE;
12078 player->is_snapping = FALSE;
12079 player->is_pushing = FALSE;
12085 if (player->move_delay > 0)
12088 player->move_delay = -1; /* set to "uninitialized" value */
12090 /* store if player is automatically moved to next field */
12091 player->is_auto_moving = (player->programmed_action != MV_NONE);
12093 /* remove the last programmed player action */
12094 player->programmed_action = 0;
12096 if (player->MovPos)
12098 /* should only happen if pre-1.2 tape recordings are played */
12099 /* this is only for backward compatibility */
12101 int original_move_delay_value = player->move_delay_value;
12104 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12108 /* scroll remaining steps with finest movement resolution */
12109 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12111 while (player->MovPos)
12113 ScrollPlayer(player, SCROLL_GO_ON);
12114 ScrollScreen(NULL, SCROLL_GO_ON);
12116 AdvanceFrameAndPlayerCounters(player->index_nr);
12122 player->move_delay_value = original_move_delay_value;
12125 player->is_active = FALSE;
12127 if (player->last_move_dir & MV_HORIZONTAL)
12129 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12130 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12134 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12135 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12138 #if USE_FIXED_BORDER_RUNNING_GFX
12139 if (!moved && !player->is_active)
12141 player->is_moving = FALSE;
12142 player->is_digging = FALSE;
12143 player->is_collecting = FALSE;
12144 player->is_snapping = FALSE;
12145 player->is_pushing = FALSE;
12153 if (moved & MP_MOVING && !ScreenMovPos &&
12154 (player->index_nr == game.centered_player_nr ||
12155 game.centered_player_nr == -1))
12157 if (moved & MP_MOVING && !ScreenMovPos &&
12158 (player == local_player || !options.network))
12161 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12162 int offset = game.scroll_delay_value;
12164 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12166 /* actual player has left the screen -- scroll in that direction */
12167 if (jx != old_jx) /* player has moved horizontally */
12168 scroll_x += (jx - old_jx);
12169 else /* player has moved vertically */
12170 scroll_y += (jy - old_jy);
12174 if (jx != old_jx) /* player has moved horizontally */
12176 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12177 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12178 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12180 /* don't scroll over playfield boundaries */
12181 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12182 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12184 /* don't scroll more than one field at a time */
12185 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12187 /* don't scroll against the player's moving direction */
12188 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12189 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12190 scroll_x = old_scroll_x;
12192 else /* player has moved vertically */
12194 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12195 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12196 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12198 /* don't scroll over playfield boundaries */
12199 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12200 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12202 /* don't scroll more than one field at a time */
12203 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12205 /* don't scroll against the player's moving direction */
12206 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12207 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12208 scroll_y = old_scroll_y;
12212 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12215 if (!options.network && game.centered_player_nr == -1 &&
12216 !AllPlayersInVisibleScreen())
12218 scroll_x = old_scroll_x;
12219 scroll_y = old_scroll_y;
12223 if (!options.network && !AllPlayersInVisibleScreen())
12225 scroll_x = old_scroll_x;
12226 scroll_y = old_scroll_y;
12231 ScrollScreen(player, SCROLL_INIT);
12232 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12237 player->StepFrame = 0;
12239 if (moved & MP_MOVING)
12241 if (old_jx != jx && old_jy == jy)
12242 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12243 else if (old_jx == jx && old_jy != jy)
12244 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12246 DrawLevelField(jx, jy); /* for "crumbled sand" */
12248 player->last_move_dir = player->MovDir;
12249 player->is_moving = TRUE;
12250 player->is_snapping = FALSE;
12251 player->is_switching = FALSE;
12252 player->is_dropping = FALSE;
12253 player->is_dropping_pressed = FALSE;
12254 player->drop_pressed_delay = 0;
12257 /* should better be called here than above, but this breaks some tapes */
12258 ScrollPlayer(player, SCROLL_INIT);
12263 CheckGravityMovementWhenNotMoving(player);
12265 player->is_moving = FALSE;
12267 /* at this point, the player is allowed to move, but cannot move right now
12268 (e.g. because of something blocking the way) -- ensure that the player
12269 is also allowed to move in the next frame (in old versions before 3.1.1,
12270 the player was forced to wait again for eight frames before next try) */
12272 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12273 player->move_delay = 0; /* allow direct movement in the next frame */
12276 if (player->move_delay == -1) /* not yet initialized by DigField() */
12277 player->move_delay = player->move_delay_value;
12279 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12281 TestIfPlayerTouchesBadThing(jx, jy);
12282 TestIfPlayerTouchesCustomElement(jx, jy);
12285 if (!player->active)
12286 RemovePlayer(player);
12291 void ScrollPlayer(struct PlayerInfo *player, int mode)
12293 int jx = player->jx, jy = player->jy;
12294 int last_jx = player->last_jx, last_jy = player->last_jy;
12295 int move_stepsize = TILEX / player->move_delay_value;
12297 #if USE_NEW_PLAYER_SPEED
12298 if (!player->active)
12301 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12304 if (!player->active || player->MovPos == 0)
12308 if (mode == SCROLL_INIT)
12310 player->actual_frame_counter = FrameCounter;
12311 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12313 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12314 Feld[last_jx][last_jy] == EL_EMPTY)
12316 int last_field_block_delay = 0; /* start with no blocking at all */
12317 int block_delay_adjustment = player->block_delay_adjustment;
12319 /* if player blocks last field, add delay for exactly one move */
12320 if (player->block_last_field)
12322 last_field_block_delay += player->move_delay_value;
12324 /* when blocking enabled, prevent moving up despite gravity */
12325 #if USE_PLAYER_GRAVITY
12326 if (player->gravity && player->MovDir == MV_UP)
12327 block_delay_adjustment = -1;
12329 if (game.gravity && player->MovDir == MV_UP)
12330 block_delay_adjustment = -1;
12334 /* add block delay adjustment (also possible when not blocking) */
12335 last_field_block_delay += block_delay_adjustment;
12337 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12338 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12341 #if USE_NEW_PLAYER_SPEED
12342 if (player->MovPos != 0) /* player has not yet reached destination */
12348 else if (!FrameReached(&player->actual_frame_counter, 1))
12351 #if USE_NEW_PLAYER_SPEED
12352 if (player->MovPos != 0)
12354 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12355 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12357 /* before DrawPlayer() to draw correct player graphic for this case */
12358 if (player->MovPos == 0)
12359 CheckGravityMovement(player);
12362 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12363 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12365 /* before DrawPlayer() to draw correct player graphic for this case */
12366 if (player->MovPos == 0)
12367 CheckGravityMovement(player);
12370 if (player->MovPos == 0) /* player reached destination field */
12372 if (player->move_delay_reset_counter > 0)
12374 player->move_delay_reset_counter--;
12376 if (player->move_delay_reset_counter == 0)
12378 /* continue with normal speed after quickly moving through gate */
12379 HALVE_PLAYER_SPEED(player);
12381 /* be able to make the next move without delay */
12382 player->move_delay = 0;
12386 player->last_jx = jx;
12387 player->last_jy = jy;
12389 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12390 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12391 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12392 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12393 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12394 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12396 DrawPlayer(player); /* needed here only to cleanup last field */
12397 RemovePlayer(player);
12399 if (local_player->friends_still_needed == 0 ||
12400 IS_SP_ELEMENT(Feld[jx][jy]))
12401 PlayerWins(player);
12404 /* this breaks one level: "machine", level 000 */
12406 int move_direction = player->MovDir;
12407 int enter_side = MV_DIR_OPPOSITE(move_direction);
12408 int leave_side = move_direction;
12409 int old_jx = last_jx;
12410 int old_jy = last_jy;
12411 int old_element = Feld[old_jx][old_jy];
12412 int new_element = Feld[jx][jy];
12414 if (IS_CUSTOM_ELEMENT(old_element))
12415 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12417 player->index_bit, leave_side);
12419 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12420 CE_PLAYER_LEAVES_X,
12421 player->index_bit, leave_side);
12423 if (IS_CUSTOM_ELEMENT(new_element))
12424 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12425 player->index_bit, enter_side);
12427 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12428 CE_PLAYER_ENTERS_X,
12429 player->index_bit, enter_side);
12431 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12432 CE_MOVE_OF_X, move_direction);
12435 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12437 TestIfPlayerTouchesBadThing(jx, jy);
12438 TestIfPlayerTouchesCustomElement(jx, jy);
12440 /* needed because pushed element has not yet reached its destination,
12441 so it would trigger a change event at its previous field location */
12442 if (!player->is_pushing)
12443 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12445 if (!player->active)
12446 RemovePlayer(player);
12449 if (!local_player->LevelSolved && level.use_step_counter)
12459 if (TimeLeft <= 10 && setup.time_limit)
12460 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12463 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12465 DisplayGameControlValues();
12467 DrawGameValue_Time(TimeLeft);
12470 if (!TimeLeft && setup.time_limit)
12471 for (i = 0; i < MAX_PLAYERS; i++)
12472 KillPlayer(&stored_player[i]);
12475 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12477 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12479 DisplayGameControlValues();
12482 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12483 DrawGameValue_Time(TimePlayed);
12487 if (tape.single_step && tape.recording && !tape.pausing &&
12488 !player->programmed_action)
12489 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12493 void ScrollScreen(struct PlayerInfo *player, int mode)
12495 static unsigned long screen_frame_counter = 0;
12497 if (mode == SCROLL_INIT)
12499 /* set scrolling step size according to actual player's moving speed */
12500 ScrollStepSize = TILEX / player->move_delay_value;
12502 screen_frame_counter = FrameCounter;
12503 ScreenMovDir = player->MovDir;
12504 ScreenMovPos = player->MovPos;
12505 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12508 else if (!FrameReached(&screen_frame_counter, 1))
12513 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12514 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12515 redraw_mask |= REDRAW_FIELD;
12518 ScreenMovDir = MV_NONE;
12521 void TestIfPlayerTouchesCustomElement(int x, int y)
12523 static int xy[4][2] =
12530 static int trigger_sides[4][2] =
12532 /* center side border side */
12533 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12534 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12535 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12536 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12538 static int touch_dir[4] =
12540 MV_LEFT | MV_RIGHT,
12545 int center_element = Feld[x][y]; /* should always be non-moving! */
12548 for (i = 0; i < NUM_DIRECTIONS; i++)
12550 int xx = x + xy[i][0];
12551 int yy = y + xy[i][1];
12552 int center_side = trigger_sides[i][0];
12553 int border_side = trigger_sides[i][1];
12554 int border_element;
12556 if (!IN_LEV_FIELD(xx, yy))
12559 if (IS_PLAYER(x, y))
12561 struct PlayerInfo *player = PLAYERINFO(x, y);
12563 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12564 border_element = Feld[xx][yy]; /* may be moving! */
12565 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12566 border_element = Feld[xx][yy];
12567 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12568 border_element = MovingOrBlocked2Element(xx, yy);
12570 continue; /* center and border element do not touch */
12572 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12573 player->index_bit, border_side);
12574 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12575 CE_PLAYER_TOUCHES_X,
12576 player->index_bit, border_side);
12578 else if (IS_PLAYER(xx, yy))
12580 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12582 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12584 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12585 continue; /* center and border element do not touch */
12588 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12589 player->index_bit, center_side);
12590 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12591 CE_PLAYER_TOUCHES_X,
12592 player->index_bit, center_side);
12598 #if USE_ELEMENT_TOUCHING_BUGFIX
12600 void TestIfElementTouchesCustomElement(int x, int y)
12602 static int xy[4][2] =
12609 static int trigger_sides[4][2] =
12611 /* center side border side */
12612 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12613 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12614 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12615 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12617 static int touch_dir[4] =
12619 MV_LEFT | MV_RIGHT,
12624 boolean change_center_element = FALSE;
12625 int center_element = Feld[x][y]; /* should always be non-moving! */
12626 int border_element_old[NUM_DIRECTIONS];
12629 for (i = 0; i < NUM_DIRECTIONS; i++)
12631 int xx = x + xy[i][0];
12632 int yy = y + xy[i][1];
12633 int border_element;
12635 border_element_old[i] = -1;
12637 if (!IN_LEV_FIELD(xx, yy))
12640 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12641 border_element = Feld[xx][yy]; /* may be moving! */
12642 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12643 border_element = Feld[xx][yy];
12644 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12645 border_element = MovingOrBlocked2Element(xx, yy);
12647 continue; /* center and border element do not touch */
12649 border_element_old[i] = border_element;
12652 for (i = 0; i < NUM_DIRECTIONS; i++)
12654 int xx = x + xy[i][0];
12655 int yy = y + xy[i][1];
12656 int center_side = trigger_sides[i][0];
12657 int border_element = border_element_old[i];
12659 if (border_element == -1)
12662 /* check for change of border element */
12663 CheckElementChangeBySide(xx, yy, border_element, center_element,
12664 CE_TOUCHING_X, center_side);
12667 for (i = 0; i < NUM_DIRECTIONS; i++)
12669 int border_side = trigger_sides[i][1];
12670 int border_element = border_element_old[i];
12672 if (border_element == -1)
12675 /* check for change of center element (but change it only once) */
12676 if (!change_center_element)
12677 change_center_element =
12678 CheckElementChangeBySide(x, y, center_element, border_element,
12679 CE_TOUCHING_X, border_side);
12685 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12687 static int xy[4][2] =
12694 static int trigger_sides[4][2] =
12696 /* center side border side */
12697 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12698 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12699 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12700 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12702 static int touch_dir[4] =
12704 MV_LEFT | MV_RIGHT,
12709 boolean change_center_element = FALSE;
12710 int center_element = Feld[x][y]; /* should always be non-moving! */
12713 for (i = 0; i < NUM_DIRECTIONS; i++)
12715 int xx = x + xy[i][0];
12716 int yy = y + xy[i][1];
12717 int center_side = trigger_sides[i][0];
12718 int border_side = trigger_sides[i][1];
12719 int border_element;
12721 if (!IN_LEV_FIELD(xx, yy))
12724 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12725 border_element = Feld[xx][yy]; /* may be moving! */
12726 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12727 border_element = Feld[xx][yy];
12728 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12729 border_element = MovingOrBlocked2Element(xx, yy);
12731 continue; /* center and border element do not touch */
12733 /* check for change of center element (but change it only once) */
12734 if (!change_center_element)
12735 change_center_element =
12736 CheckElementChangeBySide(x, y, center_element, border_element,
12737 CE_TOUCHING_X, border_side);
12739 /* check for change of border element */
12740 CheckElementChangeBySide(xx, yy, border_element, center_element,
12741 CE_TOUCHING_X, center_side);
12747 void TestIfElementHitsCustomElement(int x, int y, int direction)
12749 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12750 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12751 int hitx = x + dx, hity = y + dy;
12752 int hitting_element = Feld[x][y];
12753 int touched_element;
12755 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12758 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12759 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12761 if (IN_LEV_FIELD(hitx, hity))
12763 int opposite_direction = MV_DIR_OPPOSITE(direction);
12764 int hitting_side = direction;
12765 int touched_side = opposite_direction;
12766 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12767 MovDir[hitx][hity] != direction ||
12768 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12774 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12775 CE_HITTING_X, touched_side);
12777 CheckElementChangeBySide(hitx, hity, touched_element,
12778 hitting_element, CE_HIT_BY_X, hitting_side);
12780 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12781 CE_HIT_BY_SOMETHING, opposite_direction);
12785 /* "hitting something" is also true when hitting the playfield border */
12786 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12787 CE_HITTING_SOMETHING, direction);
12791 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12793 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12794 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12795 int hitx = x + dx, hity = y + dy;
12796 int hitting_element = Feld[x][y];
12797 int touched_element;
12799 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12800 !IS_FREE(hitx, hity) &&
12801 (!IS_MOVING(hitx, hity) ||
12802 MovDir[hitx][hity] != direction ||
12803 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12806 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12810 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12814 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12815 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12817 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12818 EP_CAN_SMASH_EVERYTHING, direction);
12820 if (IN_LEV_FIELD(hitx, hity))
12822 int opposite_direction = MV_DIR_OPPOSITE(direction);
12823 int hitting_side = direction;
12824 int touched_side = opposite_direction;
12826 int touched_element = MovingOrBlocked2Element(hitx, hity);
12829 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12830 MovDir[hitx][hity] != direction ||
12831 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12840 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12841 CE_SMASHED_BY_SOMETHING, opposite_direction);
12843 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12844 CE_OTHER_IS_SMASHING, touched_side);
12846 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12847 CE_OTHER_GETS_SMASHED, hitting_side);
12853 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12855 int i, kill_x = -1, kill_y = -1;
12857 int bad_element = -1;
12858 static int test_xy[4][2] =
12865 static int test_dir[4] =
12873 for (i = 0; i < NUM_DIRECTIONS; i++)
12875 int test_x, test_y, test_move_dir, test_element;
12877 test_x = good_x + test_xy[i][0];
12878 test_y = good_y + test_xy[i][1];
12880 if (!IN_LEV_FIELD(test_x, test_y))
12884 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12886 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12888 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12889 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12891 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12892 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12896 bad_element = test_element;
12902 if (kill_x != -1 || kill_y != -1)
12904 if (IS_PLAYER(good_x, good_y))
12906 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12908 if (player->shield_deadly_time_left > 0 &&
12909 !IS_INDESTRUCTIBLE(bad_element))
12910 Bang(kill_x, kill_y);
12911 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12912 KillPlayer(player);
12915 Bang(good_x, good_y);
12919 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12921 int i, kill_x = -1, kill_y = -1;
12922 int bad_element = Feld[bad_x][bad_y];
12923 static int test_xy[4][2] =
12930 static int touch_dir[4] =
12932 MV_LEFT | MV_RIGHT,
12937 static int test_dir[4] =
12945 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12948 for (i = 0; i < NUM_DIRECTIONS; i++)
12950 int test_x, test_y, test_move_dir, test_element;
12952 test_x = bad_x + test_xy[i][0];
12953 test_y = bad_y + test_xy[i][1];
12954 if (!IN_LEV_FIELD(test_x, test_y))
12958 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12960 test_element = Feld[test_x][test_y];
12962 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12963 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12965 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12966 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12968 /* good thing is player or penguin that does not move away */
12969 if (IS_PLAYER(test_x, test_y))
12971 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12973 if (bad_element == EL_ROBOT && player->is_moving)
12974 continue; /* robot does not kill player if he is moving */
12976 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12978 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12979 continue; /* center and border element do not touch */
12986 else if (test_element == EL_PENGUIN)
12995 if (kill_x != -1 || kill_y != -1)
12997 if (IS_PLAYER(kill_x, kill_y))
12999 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13001 if (player->shield_deadly_time_left > 0 &&
13002 !IS_INDESTRUCTIBLE(bad_element))
13003 Bang(bad_x, bad_y);
13004 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13005 KillPlayer(player);
13008 Bang(kill_x, kill_y);
13012 void TestIfPlayerTouchesBadThing(int x, int y)
13014 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13017 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13019 TestIfGoodThingHitsBadThing(x, y, move_dir);
13022 void TestIfBadThingTouchesPlayer(int x, int y)
13024 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13027 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13029 TestIfBadThingHitsGoodThing(x, y, move_dir);
13032 void TestIfFriendTouchesBadThing(int x, int y)
13034 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13037 void TestIfBadThingTouchesFriend(int x, int y)
13039 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13042 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13044 int i, kill_x = bad_x, kill_y = bad_y;
13045 static int xy[4][2] =
13053 for (i = 0; i < NUM_DIRECTIONS; i++)
13057 x = bad_x + xy[i][0];
13058 y = bad_y + xy[i][1];
13059 if (!IN_LEV_FIELD(x, y))
13062 element = Feld[x][y];
13063 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13064 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13072 if (kill_x != bad_x || kill_y != bad_y)
13073 Bang(bad_x, bad_y);
13076 void KillPlayer(struct PlayerInfo *player)
13078 int jx = player->jx, jy = player->jy;
13080 if (!player->active)
13083 /* the following code was introduced to prevent an infinite loop when calling
13085 -> CheckTriggeredElementChangeExt()
13086 -> ExecuteCustomElementAction()
13088 -> (infinitely repeating the above sequence of function calls)
13089 which occurs when killing the player while having a CE with the setting
13090 "kill player X when explosion of <player X>"; the solution using a new
13091 field "player->killed" was chosen for backwards compatibility, although
13092 clever use of the fields "player->active" etc. would probably also work */
13094 if (player->killed)
13098 player->killed = TRUE;
13100 /* remove accessible field at the player's position */
13101 Feld[jx][jy] = EL_EMPTY;
13103 /* deactivate shield (else Bang()/Explode() would not work right) */
13104 player->shield_normal_time_left = 0;
13105 player->shield_deadly_time_left = 0;
13108 BuryPlayer(player);
13111 static void KillPlayerUnlessEnemyProtected(int x, int y)
13113 if (!PLAYER_ENEMY_PROTECTED(x, y))
13114 KillPlayer(PLAYERINFO(x, y));
13117 static void KillPlayerUnlessExplosionProtected(int x, int y)
13119 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13120 KillPlayer(PLAYERINFO(x, y));
13123 void BuryPlayer(struct PlayerInfo *player)
13125 int jx = player->jx, jy = player->jy;
13127 if (!player->active)
13130 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13131 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13133 player->GameOver = TRUE;
13134 RemovePlayer(player);
13137 void RemovePlayer(struct PlayerInfo *player)
13139 int jx = player->jx, jy = player->jy;
13140 int i, found = FALSE;
13142 player->present = FALSE;
13143 player->active = FALSE;
13145 if (!ExplodeField[jx][jy])
13146 StorePlayer[jx][jy] = 0;
13148 if (player->is_moving)
13149 DrawLevelField(player->last_jx, player->last_jy);
13151 for (i = 0; i < MAX_PLAYERS; i++)
13152 if (stored_player[i].active)
13156 AllPlayersGone = TRUE;
13162 #if USE_NEW_SNAP_DELAY
13163 static void setFieldForSnapping(int x, int y, int element, int direction)
13165 struct ElementInfo *ei = &element_info[element];
13166 int direction_bit = MV_DIR_TO_BIT(direction);
13167 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13168 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13169 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13171 Feld[x][y] = EL_ELEMENT_SNAPPING;
13172 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13174 ResetGfxAnimation(x, y);
13176 GfxElement[x][y] = element;
13177 GfxAction[x][y] = action;
13178 GfxDir[x][y] = direction;
13179 GfxFrame[x][y] = -1;
13184 =============================================================================
13185 checkDiagonalPushing()
13186 -----------------------------------------------------------------------------
13187 check if diagonal input device direction results in pushing of object
13188 (by checking if the alternative direction is walkable, diggable, ...)
13189 =============================================================================
13192 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13193 int x, int y, int real_dx, int real_dy)
13195 int jx, jy, dx, dy, xx, yy;
13197 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13200 /* diagonal direction: check alternative direction */
13205 xx = jx + (dx == 0 ? real_dx : 0);
13206 yy = jy + (dy == 0 ? real_dy : 0);
13208 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13212 =============================================================================
13214 -----------------------------------------------------------------------------
13215 x, y: field next to player (non-diagonal) to try to dig to
13216 real_dx, real_dy: direction as read from input device (can be diagonal)
13217 =============================================================================
13220 int DigField(struct PlayerInfo *player,
13221 int oldx, int oldy, int x, int y,
13222 int real_dx, int real_dy, int mode)
13224 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13225 boolean player_was_pushing = player->is_pushing;
13226 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13227 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13228 int jx = oldx, jy = oldy;
13229 int dx = x - jx, dy = y - jy;
13230 int nextx = x + dx, nexty = y + dy;
13231 int move_direction = (dx == -1 ? MV_LEFT :
13232 dx == +1 ? MV_RIGHT :
13234 dy == +1 ? MV_DOWN : MV_NONE);
13235 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13236 int dig_side = MV_DIR_OPPOSITE(move_direction);
13237 int old_element = Feld[jx][jy];
13238 #if USE_FIXED_DONT_RUN_INTO
13239 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13245 if (is_player) /* function can also be called by EL_PENGUIN */
13247 if (player->MovPos == 0)
13249 player->is_digging = FALSE;
13250 player->is_collecting = FALSE;
13253 if (player->MovPos == 0) /* last pushing move finished */
13254 player->is_pushing = FALSE;
13256 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13258 player->is_switching = FALSE;
13259 player->push_delay = -1;
13261 return MP_NO_ACTION;
13265 #if !USE_FIXED_DONT_RUN_INTO
13266 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13267 return MP_NO_ACTION;
13270 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13271 old_element = Back[jx][jy];
13273 /* in case of element dropped at player position, check background */
13274 else if (Back[jx][jy] != EL_EMPTY &&
13275 game.engine_version >= VERSION_IDENT(2,2,0,0))
13276 old_element = Back[jx][jy];
13278 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13279 return MP_NO_ACTION; /* field has no opening in this direction */
13281 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13282 return MP_NO_ACTION; /* field has no opening in this direction */
13284 #if USE_FIXED_DONT_RUN_INTO
13285 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13289 Feld[jx][jy] = player->artwork_element;
13290 InitMovingField(jx, jy, MV_DOWN);
13291 Store[jx][jy] = EL_ACID;
13292 ContinueMoving(jx, jy);
13293 BuryPlayer(player);
13295 return MP_DONT_RUN_INTO;
13299 #if USE_FIXED_DONT_RUN_INTO
13300 if (player_can_move && DONT_RUN_INTO(element))
13302 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13304 return MP_DONT_RUN_INTO;
13308 #if USE_FIXED_DONT_RUN_INTO
13309 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13310 return MP_NO_ACTION;
13313 #if !USE_FIXED_DONT_RUN_INTO
13314 element = Feld[x][y];
13317 collect_count = element_info[element].collect_count_initial;
13319 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13320 return MP_NO_ACTION;
13322 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13323 player_can_move = player_can_move_or_snap;
13325 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13326 game.engine_version >= VERSION_IDENT(2,2,0,0))
13328 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13329 player->index_bit, dig_side);
13330 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13331 player->index_bit, dig_side);
13333 if (element == EL_DC_LANDMINE)
13336 if (Feld[x][y] != element) /* field changed by snapping */
13339 return MP_NO_ACTION;
13342 #if USE_PLAYER_GRAVITY
13343 if (player->gravity && is_player && !player->is_auto_moving &&
13344 canFallDown(player) && move_direction != MV_DOWN &&
13345 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13346 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13348 if (game.gravity && is_player && !player->is_auto_moving &&
13349 canFallDown(player) && move_direction != MV_DOWN &&
13350 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13351 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13354 if (player_can_move &&
13355 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13357 int sound_element = SND_ELEMENT(element);
13358 int sound_action = ACTION_WALKING;
13360 if (IS_RND_GATE(element))
13362 if (!player->key[RND_GATE_NR(element)])
13363 return MP_NO_ACTION;
13365 else if (IS_RND_GATE_GRAY(element))
13367 if (!player->key[RND_GATE_GRAY_NR(element)])
13368 return MP_NO_ACTION;
13370 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13372 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13373 return MP_NO_ACTION;
13375 else if (element == EL_EXIT_OPEN ||
13376 element == EL_EM_EXIT_OPEN ||
13377 element == EL_STEEL_EXIT_OPEN ||
13378 element == EL_EM_STEEL_EXIT_OPEN ||
13379 element == EL_SP_EXIT_OPEN ||
13380 element == EL_SP_EXIT_OPENING)
13382 sound_action = ACTION_PASSING; /* player is passing exit */
13384 else if (element == EL_EMPTY)
13386 sound_action = ACTION_MOVING; /* nothing to walk on */
13389 /* play sound from background or player, whatever is available */
13390 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13391 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13393 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13395 else if (player_can_move &&
13396 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13398 if (!ACCESS_FROM(element, opposite_direction))
13399 return MP_NO_ACTION; /* field not accessible from this direction */
13401 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13402 return MP_NO_ACTION;
13404 if (IS_EM_GATE(element))
13406 if (!player->key[EM_GATE_NR(element)])
13407 return MP_NO_ACTION;
13409 else if (IS_EM_GATE_GRAY(element))
13411 if (!player->key[EM_GATE_GRAY_NR(element)])
13412 return MP_NO_ACTION;
13414 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13416 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13417 return MP_NO_ACTION;
13419 else if (IS_EMC_GATE(element))
13421 if (!player->key[EMC_GATE_NR(element)])
13422 return MP_NO_ACTION;
13424 else if (IS_EMC_GATE_GRAY(element))
13426 if (!player->key[EMC_GATE_GRAY_NR(element)])
13427 return MP_NO_ACTION;
13429 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13431 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13432 return MP_NO_ACTION;
13434 else if (element == EL_DC_GATE_WHITE ||
13435 element == EL_DC_GATE_WHITE_GRAY ||
13436 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13438 if (player->num_white_keys == 0)
13439 return MP_NO_ACTION;
13441 player->num_white_keys--;
13443 else if (IS_SP_PORT(element))
13445 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13446 element == EL_SP_GRAVITY_PORT_RIGHT ||
13447 element == EL_SP_GRAVITY_PORT_UP ||
13448 element == EL_SP_GRAVITY_PORT_DOWN)
13449 #if USE_PLAYER_GRAVITY
13450 player->gravity = !player->gravity;
13452 game.gravity = !game.gravity;
13454 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13455 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13456 element == EL_SP_GRAVITY_ON_PORT_UP ||
13457 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13458 #if USE_PLAYER_GRAVITY
13459 player->gravity = TRUE;
13461 game.gravity = TRUE;
13463 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13464 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13465 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13466 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13467 #if USE_PLAYER_GRAVITY
13468 player->gravity = FALSE;
13470 game.gravity = FALSE;
13474 /* automatically move to the next field with double speed */
13475 player->programmed_action = move_direction;
13477 if (player->move_delay_reset_counter == 0)
13479 player->move_delay_reset_counter = 2; /* two double speed steps */
13481 DOUBLE_PLAYER_SPEED(player);
13484 PlayLevelSoundAction(x, y, ACTION_PASSING);
13486 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13490 if (mode != DF_SNAP)
13492 GfxElement[x][y] = GFX_ELEMENT(element);
13493 player->is_digging = TRUE;
13496 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13498 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13499 player->index_bit, dig_side);
13501 if (mode == DF_SNAP)
13503 #if USE_NEW_SNAP_DELAY
13504 if (level.block_snap_field)
13505 setFieldForSnapping(x, y, element, move_direction);
13507 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13509 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13512 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13513 player->index_bit, dig_side);
13516 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13520 if (is_player && mode != DF_SNAP)
13522 GfxElement[x][y] = element;
13523 player->is_collecting = TRUE;
13526 if (element == EL_SPEED_PILL)
13528 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13530 else if (element == EL_EXTRA_TIME && level.time > 0)
13532 TimeLeft += level.extra_time;
13535 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13537 DisplayGameControlValues();
13539 DrawGameValue_Time(TimeLeft);
13542 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13544 player->shield_normal_time_left += level.shield_normal_time;
13545 if (element == EL_SHIELD_DEADLY)
13546 player->shield_deadly_time_left += level.shield_deadly_time;
13548 else if (element == EL_DYNAMITE ||
13549 element == EL_EM_DYNAMITE ||
13550 element == EL_SP_DISK_RED)
13552 if (player->inventory_size < MAX_INVENTORY_SIZE)
13553 player->inventory_element[player->inventory_size++] = element;
13555 DrawGameDoorValues();
13557 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13559 player->dynabomb_count++;
13560 player->dynabombs_left++;
13562 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13564 player->dynabomb_size++;
13566 else if (element == EL_DYNABOMB_INCREASE_POWER)
13568 player->dynabomb_xl = TRUE;
13570 else if (IS_KEY(element))
13572 player->key[KEY_NR(element)] = TRUE;
13574 DrawGameDoorValues();
13576 else if (element == EL_DC_KEY_WHITE)
13578 player->num_white_keys++;
13580 /* display white keys? */
13581 /* DrawGameDoorValues(); */
13583 else if (IS_ENVELOPE(element))
13585 player->show_envelope = element;
13587 else if (element == EL_EMC_LENSES)
13589 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13591 RedrawAllInvisibleElementsForLenses();
13593 else if (element == EL_EMC_MAGNIFIER)
13595 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13597 RedrawAllInvisibleElementsForMagnifier();
13599 else if (IS_DROPPABLE(element) ||
13600 IS_THROWABLE(element)) /* can be collected and dropped */
13604 if (collect_count == 0)
13605 player->inventory_infinite_element = element;
13607 for (i = 0; i < collect_count; i++)
13608 if (player->inventory_size < MAX_INVENTORY_SIZE)
13609 player->inventory_element[player->inventory_size++] = element;
13611 DrawGameDoorValues();
13613 else if (collect_count > 0)
13615 local_player->gems_still_needed -= collect_count;
13616 if (local_player->gems_still_needed < 0)
13617 local_player->gems_still_needed = 0;
13620 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13622 DisplayGameControlValues();
13624 DrawGameValue_Emeralds(local_player->gems_still_needed);
13628 RaiseScoreElement(element);
13629 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13632 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13633 player->index_bit, dig_side);
13635 if (mode == DF_SNAP)
13637 #if USE_NEW_SNAP_DELAY
13638 if (level.block_snap_field)
13639 setFieldForSnapping(x, y, element, move_direction);
13641 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13643 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13646 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13647 player->index_bit, dig_side);
13650 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13652 if (mode == DF_SNAP && element != EL_BD_ROCK)
13653 return MP_NO_ACTION;
13655 if (CAN_FALL(element) && dy)
13656 return MP_NO_ACTION;
13658 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13659 !(element == EL_SPRING && level.use_spring_bug))
13660 return MP_NO_ACTION;
13662 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13663 ((move_direction & MV_VERTICAL &&
13664 ((element_info[element].move_pattern & MV_LEFT &&
13665 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13666 (element_info[element].move_pattern & MV_RIGHT &&
13667 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13668 (move_direction & MV_HORIZONTAL &&
13669 ((element_info[element].move_pattern & MV_UP &&
13670 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13671 (element_info[element].move_pattern & MV_DOWN &&
13672 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13673 return MP_NO_ACTION;
13675 /* do not push elements already moving away faster than player */
13676 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13677 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13678 return MP_NO_ACTION;
13680 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13682 if (player->push_delay_value == -1 || !player_was_pushing)
13683 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13685 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13687 if (player->push_delay_value == -1)
13688 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13690 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13692 if (!player->is_pushing)
13693 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13696 player->is_pushing = TRUE;
13697 player->is_active = TRUE;
13699 if (!(IN_LEV_FIELD(nextx, nexty) &&
13700 (IS_FREE(nextx, nexty) ||
13701 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13702 IS_SB_ELEMENT(element)))))
13703 return MP_NO_ACTION;
13705 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13706 return MP_NO_ACTION;
13708 if (player->push_delay == -1) /* new pushing; restart delay */
13709 player->push_delay = 0;
13711 if (player->push_delay < player->push_delay_value &&
13712 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13713 element != EL_SPRING && element != EL_BALLOON)
13715 /* make sure that there is no move delay before next try to push */
13716 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13717 player->move_delay = 0;
13719 return MP_NO_ACTION;
13722 if (IS_SB_ELEMENT(element))
13724 if (element == EL_SOKOBAN_FIELD_FULL)
13726 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13727 local_player->sokobanfields_still_needed++;
13730 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13732 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13733 local_player->sokobanfields_still_needed--;
13736 Feld[x][y] = EL_SOKOBAN_OBJECT;
13738 if (Back[x][y] == Back[nextx][nexty])
13739 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13740 else if (Back[x][y] != 0)
13741 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13744 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13747 if (local_player->sokobanfields_still_needed == 0 &&
13748 game.emulation == EMU_SOKOBAN)
13750 PlayerWins(player);
13752 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13756 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13758 InitMovingField(x, y, move_direction);
13759 GfxAction[x][y] = ACTION_PUSHING;
13761 if (mode == DF_SNAP)
13762 ContinueMoving(x, y);
13764 MovPos[x][y] = (dx != 0 ? dx : dy);
13766 Pushed[x][y] = TRUE;
13767 Pushed[nextx][nexty] = TRUE;
13769 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13770 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13772 player->push_delay_value = -1; /* get new value later */
13774 /* check for element change _after_ element has been pushed */
13775 if (game.use_change_when_pushing_bug)
13777 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13778 player->index_bit, dig_side);
13779 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13780 player->index_bit, dig_side);
13783 else if (IS_SWITCHABLE(element))
13785 if (PLAYER_SWITCHING(player, x, y))
13787 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13788 player->index_bit, dig_side);
13793 player->is_switching = TRUE;
13794 player->switch_x = x;
13795 player->switch_y = y;
13797 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13799 if (element == EL_ROBOT_WHEEL)
13801 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13805 DrawLevelField(x, y);
13807 else if (element == EL_SP_TERMINAL)
13811 SCAN_PLAYFIELD(xx, yy)
13813 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13815 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13816 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13819 else if (IS_BELT_SWITCH(element))
13821 ToggleBeltSwitch(x, y);
13823 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13824 element == EL_SWITCHGATE_SWITCH_DOWN ||
13825 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13826 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13828 ToggleSwitchgateSwitch(x, y);
13830 else if (element == EL_LIGHT_SWITCH ||
13831 element == EL_LIGHT_SWITCH_ACTIVE)
13833 ToggleLightSwitch(x, y);
13835 else if (element == EL_TIMEGATE_SWITCH ||
13836 element == EL_DC_TIMEGATE_SWITCH)
13838 ActivateTimegateSwitch(x, y);
13840 else if (element == EL_BALLOON_SWITCH_LEFT ||
13841 element == EL_BALLOON_SWITCH_RIGHT ||
13842 element == EL_BALLOON_SWITCH_UP ||
13843 element == EL_BALLOON_SWITCH_DOWN ||
13844 element == EL_BALLOON_SWITCH_NONE ||
13845 element == EL_BALLOON_SWITCH_ANY)
13847 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13848 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13849 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13850 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13851 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13854 else if (element == EL_LAMP)
13856 Feld[x][y] = EL_LAMP_ACTIVE;
13857 local_player->lights_still_needed--;
13859 ResetGfxAnimation(x, y);
13860 DrawLevelField(x, y);
13862 else if (element == EL_TIME_ORB_FULL)
13864 Feld[x][y] = EL_TIME_ORB_EMPTY;
13866 if (level.time > 0 || level.use_time_orb_bug)
13868 TimeLeft += level.time_orb_time;
13871 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13873 DisplayGameControlValues();
13875 DrawGameValue_Time(TimeLeft);
13879 ResetGfxAnimation(x, y);
13880 DrawLevelField(x, y);
13882 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13883 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13887 game.ball_state = !game.ball_state;
13889 SCAN_PLAYFIELD(xx, yy)
13891 int e = Feld[xx][yy];
13893 if (game.ball_state)
13895 if (e == EL_EMC_MAGIC_BALL)
13896 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13897 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13898 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13902 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13903 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13904 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13905 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13910 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13911 player->index_bit, dig_side);
13913 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13914 player->index_bit, dig_side);
13916 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13917 player->index_bit, dig_side);
13923 if (!PLAYER_SWITCHING(player, x, y))
13925 player->is_switching = TRUE;
13926 player->switch_x = x;
13927 player->switch_y = y;
13929 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13930 player->index_bit, dig_side);
13931 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13932 player->index_bit, dig_side);
13934 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13935 player->index_bit, dig_side);
13936 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13937 player->index_bit, dig_side);
13940 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13941 player->index_bit, dig_side);
13942 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13943 player->index_bit, dig_side);
13945 return MP_NO_ACTION;
13948 player->push_delay = -1;
13950 if (is_player) /* function can also be called by EL_PENGUIN */
13952 if (Feld[x][y] != element) /* really digged/collected something */
13954 player->is_collecting = !player->is_digging;
13955 player->is_active = TRUE;
13962 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13964 int jx = player->jx, jy = player->jy;
13965 int x = jx + dx, y = jy + dy;
13966 int snap_direction = (dx == -1 ? MV_LEFT :
13967 dx == +1 ? MV_RIGHT :
13969 dy == +1 ? MV_DOWN : MV_NONE);
13970 boolean can_continue_snapping = (level.continuous_snapping &&
13971 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13973 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13976 if (!player->active || !IN_LEV_FIELD(x, y))
13984 if (player->MovPos == 0)
13985 player->is_pushing = FALSE;
13987 player->is_snapping = FALSE;
13989 if (player->MovPos == 0)
13991 player->is_moving = FALSE;
13992 player->is_digging = FALSE;
13993 player->is_collecting = FALSE;
13999 #if USE_NEW_CONTINUOUS_SNAPPING
14000 /* prevent snapping with already pressed snap key when not allowed */
14001 if (player->is_snapping && !can_continue_snapping)
14004 if (player->is_snapping)
14008 player->MovDir = snap_direction;
14010 if (player->MovPos == 0)
14012 player->is_moving = FALSE;
14013 player->is_digging = FALSE;
14014 player->is_collecting = FALSE;
14017 player->is_dropping = FALSE;
14018 player->is_dropping_pressed = FALSE;
14019 player->drop_pressed_delay = 0;
14021 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14024 player->is_snapping = TRUE;
14025 player->is_active = TRUE;
14027 if (player->MovPos == 0)
14029 player->is_moving = FALSE;
14030 player->is_digging = FALSE;
14031 player->is_collecting = FALSE;
14034 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14035 DrawLevelField(player->last_jx, player->last_jy);
14037 DrawLevelField(x, y);
14042 boolean DropElement(struct PlayerInfo *player)
14044 int old_element, new_element;
14045 int dropx = player->jx, dropy = player->jy;
14046 int drop_direction = player->MovDir;
14047 int drop_side = drop_direction;
14048 int drop_element = (player->inventory_size > 0 ?
14049 player->inventory_element[player->inventory_size - 1] :
14050 player->inventory_infinite_element != EL_UNDEFINED ?
14051 player->inventory_infinite_element :
14052 player->dynabombs_left > 0 ?
14053 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14056 player->is_dropping_pressed = TRUE;
14058 /* do not drop an element on top of another element; when holding drop key
14059 pressed without moving, dropped element must move away before the next
14060 element can be dropped (this is especially important if the next element
14061 is dynamite, which can be placed on background for historical reasons) */
14062 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14065 if (IS_THROWABLE(drop_element))
14067 dropx += GET_DX_FROM_DIR(drop_direction);
14068 dropy += GET_DY_FROM_DIR(drop_direction);
14070 if (!IN_LEV_FIELD(dropx, dropy))
14074 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14075 new_element = drop_element; /* default: no change when dropping */
14077 /* check if player is active, not moving and ready to drop */
14078 if (!player->active || player->MovPos || player->drop_delay > 0)
14081 /* check if player has anything that can be dropped */
14082 if (new_element == EL_UNDEFINED)
14085 /* check if drop key was pressed long enough for EM style dynamite */
14086 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14089 /* check if anything can be dropped at the current position */
14090 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14093 /* collected custom elements can only be dropped on empty fields */
14094 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14097 if (old_element != EL_EMPTY)
14098 Back[dropx][dropy] = old_element; /* store old element on this field */
14100 ResetGfxAnimation(dropx, dropy);
14101 ResetRandomAnimationValue(dropx, dropy);
14103 if (player->inventory_size > 0 ||
14104 player->inventory_infinite_element != EL_UNDEFINED)
14106 if (player->inventory_size > 0)
14108 player->inventory_size--;
14110 DrawGameDoorValues();
14112 if (new_element == EL_DYNAMITE)
14113 new_element = EL_DYNAMITE_ACTIVE;
14114 else if (new_element == EL_EM_DYNAMITE)
14115 new_element = EL_EM_DYNAMITE_ACTIVE;
14116 else if (new_element == EL_SP_DISK_RED)
14117 new_element = EL_SP_DISK_RED_ACTIVE;
14120 Feld[dropx][dropy] = new_element;
14122 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14123 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14124 el2img(Feld[dropx][dropy]), 0);
14126 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14128 /* needed if previous element just changed to "empty" in the last frame */
14129 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14131 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14132 player->index_bit, drop_side);
14133 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14135 player->index_bit, drop_side);
14137 TestIfElementTouchesCustomElement(dropx, dropy);
14139 else /* player is dropping a dyna bomb */
14141 player->dynabombs_left--;
14143 Feld[dropx][dropy] = new_element;
14145 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14146 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14147 el2img(Feld[dropx][dropy]), 0);
14149 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14152 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14153 InitField_WithBug1(dropx, dropy, FALSE);
14155 new_element = Feld[dropx][dropy]; /* element might have changed */
14157 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14158 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14160 int move_direction, nextx, nexty;
14162 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14163 MovDir[dropx][dropy] = drop_direction;
14165 move_direction = MovDir[dropx][dropy];
14166 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14167 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14169 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14171 #if USE_FIX_IMPACT_COLLISION
14172 /* do not cause impact style collision by dropping elements that can fall */
14173 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14175 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14179 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14180 player->is_dropping = TRUE;
14182 player->drop_pressed_delay = 0;
14183 player->is_dropping_pressed = FALSE;
14185 player->drop_x = dropx;
14186 player->drop_y = dropy;
14191 /* ------------------------------------------------------------------------- */
14192 /* game sound playing functions */
14193 /* ------------------------------------------------------------------------- */
14195 static int *loop_sound_frame = NULL;
14196 static int *loop_sound_volume = NULL;
14198 void InitPlayLevelSound()
14200 int num_sounds = getSoundListSize();
14202 checked_free(loop_sound_frame);
14203 checked_free(loop_sound_volume);
14205 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14206 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14209 static void PlayLevelSound(int x, int y, int nr)
14211 int sx = SCREENX(x), sy = SCREENY(y);
14212 int volume, stereo_position;
14213 int max_distance = 8;
14214 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14216 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14217 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14220 if (!IN_LEV_FIELD(x, y) ||
14221 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14222 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14225 volume = SOUND_MAX_VOLUME;
14227 if (!IN_SCR_FIELD(sx, sy))
14229 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14230 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14232 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14235 stereo_position = (SOUND_MAX_LEFT +
14236 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14237 (SCR_FIELDX + 2 * max_distance));
14239 if (IS_LOOP_SOUND(nr))
14241 /* This assures that quieter loop sounds do not overwrite louder ones,
14242 while restarting sound volume comparison with each new game frame. */
14244 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14247 loop_sound_volume[nr] = volume;
14248 loop_sound_frame[nr] = FrameCounter;
14251 PlaySoundExt(nr, volume, stereo_position, type);
14254 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14256 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14257 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14258 y < LEVELY(BY1) ? LEVELY(BY1) :
14259 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14263 static void PlayLevelSoundAction(int x, int y, int action)
14265 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14268 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14270 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14272 if (sound_effect != SND_UNDEFINED)
14273 PlayLevelSound(x, y, sound_effect);
14276 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14279 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14281 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14282 PlayLevelSound(x, y, sound_effect);
14285 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14287 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14289 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14290 PlayLevelSound(x, y, sound_effect);
14293 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14295 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14297 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14298 StopSound(sound_effect);
14301 static void PlayLevelMusic()
14303 if (levelset.music[level_nr] != MUS_UNDEFINED)
14304 PlayMusic(levelset.music[level_nr]); /* from config file */
14306 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14309 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14311 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14312 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14313 int x = xx - 1 - offset;
14314 int y = yy - 1 - offset;
14319 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14323 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14327 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14331 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14335 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14339 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14343 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14346 case SAMPLE_android_clone:
14347 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14350 case SAMPLE_android_move:
14351 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14354 case SAMPLE_spring:
14355 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14359 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14363 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14366 case SAMPLE_eater_eat:
14367 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14371 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14374 case SAMPLE_collect:
14375 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14378 case SAMPLE_diamond:
14379 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14382 case SAMPLE_squash:
14383 /* !!! CHECK THIS !!! */
14385 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14387 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14391 case SAMPLE_wonderfall:
14392 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14396 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14400 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14404 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14408 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14412 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14416 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14419 case SAMPLE_wonder:
14420 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14424 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14427 case SAMPLE_exit_open:
14428 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14431 case SAMPLE_exit_leave:
14432 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14435 case SAMPLE_dynamite:
14436 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14440 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14444 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14448 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14452 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14456 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14460 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14464 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14470 void ChangeTime(int value)
14472 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14476 /* EMC game engine uses value from time counter of RND game engine */
14477 level.native_em_level->lev->time = *time;
14479 DrawGameValue_Time(*time);
14482 void RaiseScore(int value)
14484 /* EMC game engine and RND game engine have separate score counters */
14485 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14486 &level.native_em_level->lev->score : &local_player->score);
14490 DrawGameValue_Score(*score);
14494 void RaiseScore(int value)
14496 local_player->score += value;
14499 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14501 DisplayGameControlValues();
14503 DrawGameValue_Score(local_player->score);
14507 void RaiseScoreElement(int element)
14512 case EL_BD_DIAMOND:
14513 case EL_EMERALD_YELLOW:
14514 case EL_EMERALD_RED:
14515 case EL_EMERALD_PURPLE:
14516 case EL_SP_INFOTRON:
14517 RaiseScore(level.score[SC_EMERALD]);
14520 RaiseScore(level.score[SC_DIAMOND]);
14523 RaiseScore(level.score[SC_CRYSTAL]);
14526 RaiseScore(level.score[SC_PEARL]);
14529 case EL_BD_BUTTERFLY:
14530 case EL_SP_ELECTRON:
14531 RaiseScore(level.score[SC_BUG]);
14534 case EL_BD_FIREFLY:
14535 case EL_SP_SNIKSNAK:
14536 RaiseScore(level.score[SC_SPACESHIP]);
14539 case EL_DARK_YAMYAM:
14540 RaiseScore(level.score[SC_YAMYAM]);
14543 RaiseScore(level.score[SC_ROBOT]);
14546 RaiseScore(level.score[SC_PACMAN]);
14549 RaiseScore(level.score[SC_NUT]);
14552 case EL_EM_DYNAMITE:
14553 case EL_SP_DISK_RED:
14554 case EL_DYNABOMB_INCREASE_NUMBER:
14555 case EL_DYNABOMB_INCREASE_SIZE:
14556 case EL_DYNABOMB_INCREASE_POWER:
14557 RaiseScore(level.score[SC_DYNAMITE]);
14559 case EL_SHIELD_NORMAL:
14560 case EL_SHIELD_DEADLY:
14561 RaiseScore(level.score[SC_SHIELD]);
14563 case EL_EXTRA_TIME:
14564 RaiseScore(level.extra_time_score);
14578 case EL_DC_KEY_WHITE:
14579 RaiseScore(level.score[SC_KEY]);
14582 RaiseScore(element_info[element].collect_score);
14587 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14589 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14591 #if defined(NETWORK_AVALIABLE)
14592 if (options.network)
14593 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14602 FadeSkipNextFadeIn();
14604 fading = fading_none;
14608 OpenDoor(DOOR_CLOSE_1);
14611 game_status = GAME_MODE_MAIN;
14614 DrawAndFadeInMainMenu(REDRAW_FIELD);
14622 FadeOut(REDRAW_FIELD);
14625 game_status = GAME_MODE_MAIN;
14627 DrawAndFadeInMainMenu(REDRAW_FIELD);
14631 else /* continue playing the game */
14633 if (tape.playing && tape.deactivate_display)
14634 TapeDeactivateDisplayOff(TRUE);
14636 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14638 if (tape.playing && tape.deactivate_display)
14639 TapeDeactivateDisplayOn();
14643 void RequestQuitGame(boolean ask_if_really_quit)
14645 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14646 boolean skip_request = AllPlayersGone || quick_quit;
14648 RequestQuitGameExt(skip_request, quick_quit,
14649 "Do you really want to quit the game ?");
14653 /* ------------------------------------------------------------------------- */
14654 /* random generator functions */
14655 /* ------------------------------------------------------------------------- */
14657 unsigned int InitEngineRandom_RND(long seed)
14659 game.num_random_calls = 0;
14662 unsigned int rnd_seed = InitEngineRandom(seed);
14664 printf("::: START RND: %d\n", rnd_seed);
14669 return InitEngineRandom(seed);
14675 unsigned int RND(int max)
14679 game.num_random_calls++;
14681 return GetEngineRandom(max);
14688 /* ------------------------------------------------------------------------- */
14689 /* game engine snapshot handling functions */
14690 /* ------------------------------------------------------------------------- */
14692 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14694 struct EngineSnapshotInfo
14696 /* runtime values for custom element collect score */
14697 int collect_score[NUM_CUSTOM_ELEMENTS];
14699 /* runtime values for group element choice position */
14700 int choice_pos[NUM_GROUP_ELEMENTS];
14702 /* runtime values for belt position animations */
14703 int belt_graphic[4 * NUM_BELT_PARTS];
14704 int belt_anim_mode[4 * NUM_BELT_PARTS];
14707 struct EngineSnapshotNodeInfo
14714 static struct EngineSnapshotInfo engine_snapshot_rnd;
14715 static ListNode *engine_snapshot_list = NULL;
14716 static char *snapshot_level_identifier = NULL;
14717 static int snapshot_level_nr = -1;
14719 void FreeEngineSnapshot()
14721 while (engine_snapshot_list != NULL)
14722 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14725 setString(&snapshot_level_identifier, NULL);
14726 snapshot_level_nr = -1;
14729 static void SaveEngineSnapshotValues_RND()
14731 static int belt_base_active_element[4] =
14733 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14734 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14735 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14736 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14740 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14742 int element = EL_CUSTOM_START + i;
14744 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14747 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14749 int element = EL_GROUP_START + i;
14751 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14754 for (i = 0; i < 4; i++)
14756 for (j = 0; j < NUM_BELT_PARTS; j++)
14758 int element = belt_base_active_element[i] + j;
14759 int graphic = el2img(element);
14760 int anim_mode = graphic_info[graphic].anim_mode;
14762 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14763 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14768 static void LoadEngineSnapshotValues_RND()
14770 unsigned long num_random_calls = game.num_random_calls;
14773 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14775 int element = EL_CUSTOM_START + i;
14777 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14780 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14782 int element = EL_GROUP_START + i;
14784 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14787 for (i = 0; i < 4; i++)
14789 for (j = 0; j < NUM_BELT_PARTS; j++)
14791 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14792 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14794 graphic_info[graphic].anim_mode = anim_mode;
14798 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14800 InitRND(tape.random_seed);
14801 for (i = 0; i < num_random_calls; i++)
14805 if (game.num_random_calls != num_random_calls)
14807 Error(ERR_INFO, "number of random calls out of sync");
14808 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14809 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14810 Error(ERR_EXIT, "this should not happen -- please debug");
14814 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14816 struct EngineSnapshotNodeInfo *bi =
14817 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14819 bi->buffer_orig = buffer;
14820 bi->buffer_copy = checked_malloc(size);
14823 memcpy(bi->buffer_copy, buffer, size);
14825 addNodeToList(&engine_snapshot_list, NULL, bi);
14828 void SaveEngineSnapshot()
14830 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14832 if (level_editor_test_game) /* do not save snapshots from editor */
14835 /* copy some special values to a structure better suited for the snapshot */
14837 SaveEngineSnapshotValues_RND();
14838 SaveEngineSnapshotValues_EM();
14840 /* save values stored in special snapshot structure */
14842 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14843 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14845 /* save further RND engine values */
14847 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14848 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14849 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14851 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14852 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14853 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14854 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14856 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14857 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14858 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14859 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14860 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14862 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14863 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14864 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14866 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14868 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14870 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14871 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14873 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14874 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14875 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14876 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14877 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14878 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14879 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14880 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14881 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14882 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14883 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14884 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14885 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14886 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14887 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14888 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14889 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14890 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14892 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14893 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14895 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14896 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14897 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14899 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14900 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14902 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14903 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14904 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14905 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14906 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14908 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14909 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14911 /* save level identification information */
14913 setString(&snapshot_level_identifier, leveldir_current->identifier);
14914 snapshot_level_nr = level_nr;
14917 ListNode *node = engine_snapshot_list;
14920 while (node != NULL)
14922 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14927 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14931 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14933 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14936 void LoadEngineSnapshot()
14938 ListNode *node = engine_snapshot_list;
14940 if (engine_snapshot_list == NULL)
14943 while (node != NULL)
14945 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14950 /* restore special values from snapshot structure */
14952 LoadEngineSnapshotValues_RND();
14953 LoadEngineSnapshotValues_EM();
14956 boolean CheckEngineSnapshot()
14958 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14959 snapshot_level_nr == level_nr);
14963 /* ---------- new game button stuff ---------------------------------------- */
14965 /* graphic position values for game buttons */
14966 #define GAME_BUTTON_XSIZE 30
14967 #define GAME_BUTTON_YSIZE 30
14968 #define GAME_BUTTON_XPOS 5
14969 #define GAME_BUTTON_YPOS 215
14970 #define SOUND_BUTTON_XPOS 5
14971 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14973 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14974 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14975 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14976 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14977 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14978 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14986 } gamebutton_info[NUM_GAME_BUTTONS] =
14990 &game.button.stop.x, &game.button.stop.y,
14991 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14996 &game.button.pause.x, &game.button.pause.y,
14997 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14998 GAME_CTRL_ID_PAUSE,
15002 &game.button.play.x, &game.button.play.y,
15003 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15008 &game.button.sound_music.x, &game.button.sound_music.y,
15009 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15010 SOUND_CTRL_ID_MUSIC,
15011 "background music on/off"
15014 &game.button.sound_loops.x, &game.button.sound_loops.y,
15015 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15016 SOUND_CTRL_ID_LOOPS,
15017 "sound loops on/off"
15020 &game.button.sound_simple.x,&game.button.sound_simple.y,
15021 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15022 SOUND_CTRL_ID_SIMPLE,
15023 "normal sounds on/off"
15027 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15032 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15033 GAME_CTRL_ID_PAUSE,
15037 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15042 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15043 SOUND_CTRL_ID_MUSIC,
15044 "background music on/off"
15047 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15048 SOUND_CTRL_ID_LOOPS,
15049 "sound loops on/off"
15052 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15053 SOUND_CTRL_ID_SIMPLE,
15054 "normal sounds on/off"
15059 void CreateGameButtons()
15063 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15065 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15066 struct GadgetInfo *gi;
15069 unsigned long event_mask;
15071 int gd_xoffset, gd_yoffset;
15072 int gd_x1, gd_x2, gd_y1, gd_y2;
15075 x = DX + *gamebutton_info[i].x;
15076 y = DY + *gamebutton_info[i].y;
15077 gd_xoffset = gamebutton_info[i].gd_x;
15078 gd_yoffset = gamebutton_info[i].gd_y;
15079 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15080 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15082 if (id == GAME_CTRL_ID_STOP ||
15083 id == GAME_CTRL_ID_PAUSE ||
15084 id == GAME_CTRL_ID_PLAY)
15086 button_type = GD_TYPE_NORMAL_BUTTON;
15088 event_mask = GD_EVENT_RELEASED;
15089 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15090 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15094 button_type = GD_TYPE_CHECK_BUTTON;
15096 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15097 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15098 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15099 event_mask = GD_EVENT_PRESSED;
15100 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15101 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15104 gi = CreateGadget(GDI_CUSTOM_ID, id,
15105 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15110 GDI_X, DX + gd_xoffset,
15111 GDI_Y, DY + gd_yoffset,
15113 GDI_WIDTH, GAME_BUTTON_XSIZE,
15114 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15115 GDI_TYPE, button_type,
15116 GDI_STATE, GD_BUTTON_UNPRESSED,
15117 GDI_CHECKED, checked,
15118 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15119 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15120 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15121 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15122 GDI_EVENT_MASK, event_mask,
15123 GDI_CALLBACK_ACTION, HandleGameButtons,
15127 Error(ERR_EXIT, "cannot create gadget");
15129 game_gadget[id] = gi;
15133 void FreeGameButtons()
15137 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15138 FreeGadget(game_gadget[i]);
15141 static void MapGameButtons()
15145 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15146 MapGadget(game_gadget[i]);
15149 void UnmapGameButtons()
15153 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15154 UnmapGadget(game_gadget[i]);
15157 static void HandleGameButtons(struct GadgetInfo *gi)
15159 int id = gi->custom_id;
15161 if (game_status != GAME_MODE_PLAYING)
15166 case GAME_CTRL_ID_STOP:
15170 RequestQuitGame(TRUE);
15173 case GAME_CTRL_ID_PAUSE:
15174 if (options.network)
15176 #if defined(NETWORK_AVALIABLE)
15178 SendToServer_ContinuePlaying();
15180 SendToServer_PausePlaying();
15184 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15187 case GAME_CTRL_ID_PLAY:
15190 #if defined(NETWORK_AVALIABLE)
15191 if (options.network)
15192 SendToServer_ContinuePlaying();
15196 tape.pausing = FALSE;
15197 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15202 case SOUND_CTRL_ID_MUSIC:
15203 if (setup.sound_music)
15205 setup.sound_music = FALSE;
15208 else if (audio.music_available)
15210 setup.sound = setup.sound_music = TRUE;
15212 SetAudioMode(setup.sound);
15218 case SOUND_CTRL_ID_LOOPS:
15219 if (setup.sound_loops)
15220 setup.sound_loops = FALSE;
15221 else if (audio.loops_available)
15223 setup.sound = setup.sound_loops = TRUE;
15224 SetAudioMode(setup.sound);
15228 case SOUND_CTRL_ID_SIMPLE:
15229 if (setup.sound_simple)
15230 setup.sound_simple = FALSE;
15231 else if (audio.sound_available)
15233 setup.sound = setup.sound_simple = TRUE;
15234 SetAudioMode(setup.sound);