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 void InitGameControlValues()
1729 for (i = 0; i < NUM_GAME_CONTROLS; i++)
1730 game_control_value[i] = last_game_control_value[i] = -1;
1732 for (i = 0; game_controls[i].nr != -1; i++)
1734 int nr = game_controls[i].nr;
1735 int type = game_controls[i].type;
1736 struct TextPosInfo *pos = game_controls[i].pos;
1738 game_control_value[nr] = last_game_control_value[nr] = -1;
1740 /* determine panel value width for later calculation of alignment */
1741 if (type == TYPE_INTEGER || type == TYPE_STRING)
1742 pos->width = pos->chars * getFontWidth(pos->font);
1743 else if (type == TYPE_ELEMENT)
1744 pos->width = MINI_TILESIZE;
1748 void UpdateGameControlValues()
1752 game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1753 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1755 game_control_value[GAME_CONTROL_INVENTORY] = 0;
1756 for (i = 0; i < MAX_NUM_KEYS; i++)
1757 game_control_value[GAME_CONTROL_KEY_1 + i] = 0;
1758 game_control_value[GAME_CONTROL_KEY_WHITE] = 0;
1759 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1761 if (game.centered_player_nr == -1)
1763 for (i = 0; i < MAX_PLAYERS; i++)
1765 for (j = 0; j < MAX_NUM_KEYS; j++)
1766 if (stored_player[i].key[j])
1767 game_control_value[GAME_CONTROL_KEY_1 + j] = 1;
1769 game_control_value[GAME_CONTROL_INVENTORY] +=
1770 stored_player[i].inventory_size;
1772 if (stored_player[i].num_white_keys > 0)
1773 game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1775 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1776 stored_player[i].num_white_keys;
1781 int player_nr = game.centered_player_nr;
1783 for (i = 0; i < MAX_NUM_KEYS; i++)
1784 if (stored_player[player_nr].key[i])
1785 game_control_value[GAME_CONTROL_KEY_1 + i] = 1;
1787 game_control_value[GAME_CONTROL_INVENTORY] +=
1788 stored_player[player_nr].inventory_size;
1790 if (stored_player[player_nr].num_white_keys > 0)
1791 game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1793 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1794 stored_player[player_nr].num_white_keys;
1797 game_control_value[GAME_CONTROL_SCORE] = (local_player->LevelSolved ?
1798 local_player->score_final :
1799 local_player->score);
1801 game_control_value[GAME_CONTROL_TIME] = (level.time == 0 ?
1805 game_control_value[GAME_CONTROL_TIME_HH] = TapeTime / 3600;
1806 game_control_value[GAME_CONTROL_TIME_MM] = (TapeTime / 60) % 60;
1807 game_control_value[GAME_CONTROL_TIME_SS] = TapeTime % 60;
1809 for (i = 0; i < 8; i++)
1810 game_control_value[GAME_CONTROL_DROP_NEXT_1 + i] = EL_UNDEFINED;
1812 game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1813 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1815 game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1816 local_player->shield_normal_time_left;
1817 game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1818 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1820 game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1821 local_player->shield_deadly_time_left;
1823 if (local_player->gems_still_needed > 0 ||
1824 local_player->sokobanfields_still_needed > 0 ||
1825 local_player->lights_still_needed > 0)
1827 game_control_value[GAME_CONTROL_EXIT] = EL_EXIT_CLOSED;
1828 game_control_value[GAME_CONTROL_EM_EXIT] = EL_EM_EXIT_CLOSED;
1829 game_control_value[GAME_CONTROL_SP_EXIT] = EL_SP_EXIT_CLOSED;
1830 game_control_value[GAME_CONTROL_STEEL_EXIT] = EL_STEEL_EXIT_CLOSED;
1831 game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED;
1835 game_control_value[GAME_CONTROL_EXIT] = EL_EXIT_OPEN;
1836 game_control_value[GAME_CONTROL_EM_EXIT] = EL_EM_EXIT_OPEN;
1837 game_control_value[GAME_CONTROL_SP_EXIT] = EL_SP_EXIT_OPEN;
1838 game_control_value[GAME_CONTROL_STEEL_EXIT] = EL_STEEL_EXIT_OPEN;
1839 game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN;
1842 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED;
1843 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED;
1845 game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
1846 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1847 game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1849 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
1850 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1851 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1852 game.timegate_time_left;
1854 game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED;
1856 game_control_value[GAME_CONTROL_EMC_LENSES] =
1857 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1858 game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1860 game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
1861 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1862 game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1864 game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
1865 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
1866 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1867 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
1868 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
1869 EL_BALLOON_SWITCH_NONE);
1871 game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1872 local_player->dynabomb_count;
1873 game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1874 local_player->dynabomb_size;
1875 game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1876 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1878 game_control_value[GAME_CONTROL_PENGUINS] =
1879 local_player->friends_still_needed;
1881 game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1882 local_player->sokobanfields_still_needed;
1883 game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1884 local_player->sokobanfields_still_needed;
1886 game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
1888 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
1889 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
1890 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
1891 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
1892 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
1893 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
1894 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
1895 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
1897 game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED;
1898 game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
1899 game.magic_wall_time_left;
1900 game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED;
1901 game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED;
1903 game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
1904 game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
1905 game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
1908 void DisplayGameControlValues()
1912 for (i = 0; game_controls[i].nr != -1; i++)
1914 int nr = game_controls[i].nr;
1915 int type = game_controls[i].type;
1916 struct TextPosInfo *pos = game_controls[i].pos;
1917 int value = game_control_value[nr];
1918 int last_value = last_game_control_value[nr];
1919 int chars = pos->chars;
1920 int font = pos->font;
1922 if (value == last_value)
1925 last_game_control_value[nr] = value;
1928 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
1931 if (PANEL_DEACTIVATED(pos))
1934 if (type == TYPE_INTEGER)
1936 if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
1938 boolean use_dynamic_chars = (pos->chars == -1 ? TRUE : FALSE);
1940 if (use_dynamic_chars) /* use dynamic number of chars */
1942 int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
1943 int chars1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
1944 int chars2 = chars1 + 1;
1945 int font1 = pos->font;
1946 int font2 = pos->font_alt;
1948 chars = (value < value_change ? chars1 : chars2);
1949 font = (value < value_change ? font1 : font2);
1951 /* clear background if value just changed its size (dynamic chars) */
1952 if ((last_value < value_change) != (value < value_change))
1954 int width1 = chars1 * getFontWidth(font1);
1955 int width2 = chars2 * getFontWidth(font2);
1956 int max_width = MAX(width1, width2);
1957 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
1959 pos->width = max_width;
1961 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1962 max_width, max_height);
1966 pos->width = chars * getFontWidth(font);
1969 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font);
1971 else if (type == TYPE_ELEMENT)
1973 if (nr >= GAME_CONTROL_KEY_1 && nr <= GAME_CONTROL_KEY_8)
1975 int key_nr = nr - GAME_CONTROL_KEY_1;
1976 int src_x = DOOR_GFX_PAGEX5 + 18 + (key_nr % STD_NUM_KEYS) * MINI_TILEX;
1977 int src_y = DOOR_GFX_PAGEY1 + 123;
1978 int dst_x = PANEL_XPOS(pos);
1979 int dst_y = PANEL_YPOS(pos);
1980 int element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1981 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1982 EL_EM_KEY_1 : EL_KEY_1) + key_nr;
1983 int graphic = el2edimg(element);
1986 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1988 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1989 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1991 else if (value != EL_UNDEFINED)
1993 int graphic = el2edimg(value);
1994 int dst_x = PANEL_XPOS(pos);
1995 int dst_y = PANEL_YPOS(pos);
1997 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2000 else if (type == TYPE_STRING)
2002 char *s = (nr == GAME_CONTROL_PLAYER_NAME ? setup.player_name :
2003 nr == GAME_CONTROL_LEVEL_NAME ? level.name :
2004 nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2008 char *s_cut = getStringCopyN(s, pos->chars);
2010 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, pos->font);
2016 redraw_mask |= REDRAW_DOOR_1;
2020 void DrawGameValue_Emeralds(int value)
2022 struct TextPosInfo *pos = &game.panel.gems;
2024 int font_nr = pos->font;
2026 int font_nr = FONT_TEXT_2;
2028 int font_width = getFontWidth(font_nr);
2029 int chars = pos->chars;
2032 return; /* !!! USE NEW STUFF !!! */
2035 if (PANEL_DEACTIVATED(pos))
2038 pos->width = chars * font_width;
2040 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2043 void DrawGameValue_Dynamite(int value)
2045 struct TextPosInfo *pos = &game.panel.inventory;
2047 int font_nr = pos->font;
2049 int font_nr = FONT_TEXT_2;
2051 int font_width = getFontWidth(font_nr);
2052 int chars = pos->chars;
2055 return; /* !!! USE NEW STUFF !!! */
2058 if (PANEL_DEACTIVATED(pos))
2061 pos->width = chars * font_width;
2063 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2066 void DrawGameValue_Score(int value)
2068 struct TextPosInfo *pos = &game.panel.score;
2070 int font_nr = pos->font;
2072 int font_nr = FONT_TEXT_2;
2074 int font_width = getFontWidth(font_nr);
2075 int chars = pos->chars;
2078 return; /* !!! USE NEW STUFF !!! */
2081 if (PANEL_DEACTIVATED(pos))
2084 pos->width = chars * font_width;
2086 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2089 void DrawGameValue_Time(int value)
2091 struct TextPosInfo *pos = &game.panel.time;
2092 static int last_value = -1;
2095 int chars = pos->chars;
2097 int font1_nr = pos->font;
2098 int font2_nr = pos->font_alt;
2100 int font1_nr = FONT_TEXT_2;
2101 int font2_nr = FONT_TEXT_1;
2103 int font_nr = font1_nr;
2104 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2107 return; /* !!! USE NEW STUFF !!! */
2110 if (PANEL_DEACTIVATED(pos))
2113 if (use_dynamic_chars) /* use dynamic number of chars */
2115 chars = (value < 1000 ? chars1 : chars2);
2116 font_nr = (value < 1000 ? font1_nr : font2_nr);
2119 /* clear background if value just changed its size (dynamic chars only) */
2120 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2122 int width1 = chars1 * getFontWidth(font1_nr);
2123 int width2 = chars2 * getFontWidth(font2_nr);
2124 int max_width = MAX(width1, width2);
2125 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2127 pos->width = max_width;
2129 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2130 max_width, max_height);
2133 pos->width = chars * getFontWidth(font_nr);
2135 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2140 void DrawGameValue_Level(int value)
2142 struct TextPosInfo *pos = &game.panel.level_number;
2145 int chars = pos->chars;
2147 int font1_nr = pos->font;
2148 int font2_nr = pos->font_alt;
2150 int font1_nr = FONT_TEXT_2;
2151 int font2_nr = FONT_TEXT_1;
2153 int font_nr = font1_nr;
2154 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2157 return; /* !!! USE NEW STUFF !!! */
2160 if (PANEL_DEACTIVATED(pos))
2163 if (use_dynamic_chars) /* use dynamic number of chars */
2165 chars = (level_nr < 100 ? chars1 : chars2);
2166 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2169 pos->width = chars * getFontWidth(font_nr);
2171 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2174 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2177 struct TextPosInfo *pos = &game.panel.keys;
2180 int base_key_graphic = EL_KEY_1;
2185 return; /* !!! USE NEW STUFF !!! */
2189 if (PANEL_DEACTIVATED(pos))
2194 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2195 base_key_graphic = EL_EM_KEY_1;
2199 pos->width = 4 * MINI_TILEX;
2203 for (i = 0; i < MAX_NUM_KEYS; i++)
2205 /* currently only 4 of 8 possible keys are displayed */
2206 for (i = 0; i < STD_NUM_KEYS; i++)
2210 struct TextPosInfo *pos = &game.panel.key[i];
2212 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2213 int src_y = DOOR_GFX_PAGEY1 + 123;
2215 int dst_x = PANEL_XPOS(pos);
2216 int dst_y = PANEL_YPOS(pos);
2218 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2219 int dst_y = PANEL_YPOS(pos);
2223 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2224 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2226 int graphic = el2edimg(element);
2230 if (PANEL_DEACTIVATED(pos))
2235 /* masked blit with tiles from half-size scaled bitmap does not work yet
2236 (no mask bitmap created for these sizes after loading and scaling) --
2237 solution: load without creating mask, scale, then create final mask */
2239 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2240 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2245 int graphic = el2edimg(base_key_graphic + i);
2250 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2252 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2253 dst_x - src_x, dst_y - src_y);
2254 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2260 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2262 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2263 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2266 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2268 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2269 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2277 void DrawGameValue_Emeralds(int value)
2279 int font_nr = FONT_TEXT_2;
2280 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2282 if (PANEL_DEACTIVATED(game.panel.gems))
2285 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2288 void DrawGameValue_Dynamite(int value)
2290 int font_nr = FONT_TEXT_2;
2291 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2293 if (PANEL_DEACTIVATED(game.panel.inventory))
2296 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2299 void DrawGameValue_Score(int value)
2301 int font_nr = FONT_TEXT_2;
2302 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2304 if (PANEL_DEACTIVATED(game.panel.score))
2307 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2310 void DrawGameValue_Time(int value)
2312 int font1_nr = FONT_TEXT_2;
2314 int font2_nr = FONT_TEXT_1;
2316 int font2_nr = FONT_LEVEL_NUMBER;
2318 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2319 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2321 if (PANEL_DEACTIVATED(game.panel.time))
2324 /* clear background if value just changed its size */
2325 if (value == 999 || value == 1000)
2326 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2329 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2331 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2334 void DrawGameValue_Level(int value)
2336 int font1_nr = FONT_TEXT_2;
2338 int font2_nr = FONT_TEXT_1;
2340 int font2_nr = FONT_LEVEL_NUMBER;
2343 if (PANEL_DEACTIVATED(game.panel.level))
2347 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2349 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2352 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2354 int base_key_graphic = EL_KEY_1;
2357 if (PANEL_DEACTIVATED(game.panel.keys))
2360 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2361 base_key_graphic = EL_EM_KEY_1;
2363 /* currently only 4 of 8 possible keys are displayed */
2364 for (i = 0; i < STD_NUM_KEYS; i++)
2366 int x = XX_KEYS + i * MINI_TILEX;
2370 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2372 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2373 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2379 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2382 int key[MAX_NUM_KEYS];
2385 /* prevent EM engine from updating time/score values parallel to GameWon() */
2386 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2387 local_player->LevelSolved)
2390 for (i = 0; i < MAX_NUM_KEYS; i++)
2391 key[i] = key_bits & (1 << i);
2393 DrawGameValue_Level(level_nr);
2395 DrawGameValue_Emeralds(emeralds);
2396 DrawGameValue_Dynamite(dynamite);
2397 DrawGameValue_Score(score);
2398 DrawGameValue_Time(time);
2400 DrawGameValue_Keys(key);
2403 void DrawGameDoorValues()
2405 UpdateGameControlValues();
2406 DisplayGameControlValues();
2409 void DrawGameDoorValues_OLD()
2411 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2412 int dynamite_value = 0;
2413 int score_value = (local_player->LevelSolved ? local_player->score_final :
2414 local_player->score);
2415 int gems_value = local_player->gems_still_needed;
2419 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2421 DrawGameDoorValues_EM();
2426 if (game.centered_player_nr == -1)
2428 for (i = 0; i < MAX_PLAYERS; i++)
2430 for (j = 0; j < MAX_NUM_KEYS; j++)
2431 if (stored_player[i].key[j])
2432 key_bits |= (1 << j);
2434 dynamite_value += stored_player[i].inventory_size;
2439 int player_nr = game.centered_player_nr;
2441 for (i = 0; i < MAX_NUM_KEYS; i++)
2442 if (stored_player[player_nr].key[i])
2443 key_bits |= (1 << i);
2445 dynamite_value = stored_player[player_nr].inventory_size;
2448 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2454 =============================================================================
2456 -----------------------------------------------------------------------------
2457 initialize game engine due to level / tape version number
2458 =============================================================================
2461 static void InitGameEngine()
2463 int i, j, k, l, x, y;
2465 /* set game engine from tape file when re-playing, else from level file */
2466 game.engine_version = (tape.playing ? tape.engine_version :
2467 level.game_version);
2469 /* ---------------------------------------------------------------------- */
2470 /* set flags for bugs and changes according to active game engine version */
2471 /* ---------------------------------------------------------------------- */
2474 Summary of bugfix/change:
2475 Fixed handling for custom elements that change when pushed by the player.
2477 Fixed/changed in version:
2481 Before 3.1.0, custom elements that "change when pushing" changed directly
2482 after the player started pushing them (until then handled in "DigField()").
2483 Since 3.1.0, these custom elements are not changed until the "pushing"
2484 move of the element is finished (now handled in "ContinueMoving()").
2486 Affected levels/tapes:
2487 The first condition is generally needed for all levels/tapes before version
2488 3.1.0, which might use the old behaviour before it was changed; known tapes
2489 that are affected are some tapes from the level set "Walpurgis Gardens" by
2491 The second condition is an exception from the above case and is needed for
2492 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2493 above (including some development versions of 3.1.0), but before it was
2494 known that this change would break tapes like the above and was fixed in
2495 3.1.1, so that the changed behaviour was active although the engine version
2496 while recording maybe was before 3.1.0. There is at least one tape that is
2497 affected by this exception, which is the tape for the one-level set "Bug
2498 Machine" by Juergen Bonhagen.
2501 game.use_change_when_pushing_bug =
2502 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2504 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2505 tape.game_version < VERSION_IDENT(3,1,1,0)));
2508 Summary of bugfix/change:
2509 Fixed handling for blocking the field the player leaves when moving.
2511 Fixed/changed in version:
2515 Before 3.1.1, when "block last field when moving" was enabled, the field
2516 the player is leaving when moving was blocked for the time of the move,
2517 and was directly unblocked afterwards. This resulted in the last field
2518 being blocked for exactly one less than the number of frames of one player
2519 move. Additionally, even when blocking was disabled, the last field was
2520 blocked for exactly one frame.
2521 Since 3.1.1, due to changes in player movement handling, the last field
2522 is not blocked at all when blocking is disabled. When blocking is enabled,
2523 the last field is blocked for exactly the number of frames of one player
2524 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2525 last field is blocked for exactly one more than the number of frames of
2528 Affected levels/tapes:
2529 (!!! yet to be determined -- probably many !!!)
2532 game.use_block_last_field_bug =
2533 (game.engine_version < VERSION_IDENT(3,1,1,0));
2536 Summary of bugfix/change:
2537 Changed behaviour of CE changes with multiple changes per single frame.
2539 Fixed/changed in version:
2543 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2544 This resulted in race conditions where CEs seem to behave strange in some
2545 situations (where triggered CE changes were just skipped because there was
2546 already a CE change on that tile in the playfield in that engine frame).
2547 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2548 (The number of changes per frame must be limited in any case, because else
2549 it is easily possible to define CE changes that would result in an infinite
2550 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2551 should be set large enough so that it would only be reached in cases where
2552 the corresponding CE change conditions run into a loop. Therefore, it seems
2553 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2554 maximal number of change pages for custom elements.)
2556 Affected levels/tapes:
2560 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2561 game.max_num_changes_per_frame = 1;
2563 game.max_num_changes_per_frame =
2564 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2567 /* ---------------------------------------------------------------------- */
2569 /* default scan direction: scan playfield from top/left to bottom/right */
2570 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2572 /* dynamically adjust element properties according to game engine version */
2573 InitElementPropertiesEngine(game.engine_version);
2576 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2577 printf(" tape version == %06d [%s] [file: %06d]\n",
2578 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2580 printf(" => game.engine_version == %06d\n", game.engine_version);
2583 /* ---------- initialize player's initial move delay --------------------- */
2585 /* dynamically adjust player properties according to level information */
2586 for (i = 0; i < MAX_PLAYERS; i++)
2587 game.initial_move_delay_value[i] =
2588 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2590 /* dynamically adjust player properties according to game engine version */
2591 for (i = 0; i < MAX_PLAYERS; i++)
2592 game.initial_move_delay[i] =
2593 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2594 game.initial_move_delay_value[i] : 0);
2596 /* ---------- initialize player's initial push delay --------------------- */
2598 /* dynamically adjust player properties according to game engine version */
2599 game.initial_push_delay_value =
2600 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2602 /* ---------- initialize changing elements ------------------------------- */
2604 /* initialize changing elements information */
2605 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2607 struct ElementInfo *ei = &element_info[i];
2609 /* this pointer might have been changed in the level editor */
2610 ei->change = &ei->change_page[0];
2612 if (!IS_CUSTOM_ELEMENT(i))
2614 ei->change->target_element = EL_EMPTY_SPACE;
2615 ei->change->delay_fixed = 0;
2616 ei->change->delay_random = 0;
2617 ei->change->delay_frames = 1;
2620 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2622 ei->has_change_event[j] = FALSE;
2624 ei->event_page_nr[j] = 0;
2625 ei->event_page[j] = &ei->change_page[0];
2629 /* add changing elements from pre-defined list */
2630 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2632 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2633 struct ElementInfo *ei = &element_info[ch_delay->element];
2635 ei->change->target_element = ch_delay->target_element;
2636 ei->change->delay_fixed = ch_delay->change_delay;
2638 ei->change->pre_change_function = ch_delay->pre_change_function;
2639 ei->change->change_function = ch_delay->change_function;
2640 ei->change->post_change_function = ch_delay->post_change_function;
2642 ei->change->can_change = TRUE;
2643 ei->change->can_change_or_has_action = TRUE;
2645 ei->has_change_event[CE_DELAY] = TRUE;
2647 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2648 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2651 /* ---------- initialize internal run-time variables ------------- */
2653 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2655 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2657 for (j = 0; j < ei->num_change_pages; j++)
2659 ei->change_page[j].can_change_or_has_action =
2660 (ei->change_page[j].can_change |
2661 ei->change_page[j].has_action);
2665 /* add change events from custom element configuration */
2666 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2668 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2670 for (j = 0; j < ei->num_change_pages; j++)
2672 if (!ei->change_page[j].can_change_or_has_action)
2675 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2677 /* only add event page for the first page found with this event */
2678 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2680 ei->has_change_event[k] = TRUE;
2682 ei->event_page_nr[k] = j;
2683 ei->event_page[k] = &ei->change_page[j];
2689 /* ---------- initialize run-time trigger player and element ------------- */
2691 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2693 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2695 for (j = 0; j < ei->num_change_pages; j++)
2697 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2698 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2699 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2700 ei->change_page[j].actual_trigger_ce_value = 0;
2701 ei->change_page[j].actual_trigger_ce_score = 0;
2705 /* ---------- initialize trigger events ---------------------------------- */
2707 /* initialize trigger events information */
2708 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2709 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2710 trigger_events[i][j] = FALSE;
2712 /* add trigger events from element change event properties */
2713 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2715 struct ElementInfo *ei = &element_info[i];
2717 for (j = 0; j < ei->num_change_pages; j++)
2719 if (!ei->change_page[j].can_change_or_has_action)
2722 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2724 int trigger_element = ei->change_page[j].trigger_element;
2726 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2728 if (ei->change_page[j].has_event[k])
2730 if (IS_GROUP_ELEMENT(trigger_element))
2732 struct ElementGroupInfo *group =
2733 element_info[trigger_element].group;
2735 for (l = 0; l < group->num_elements_resolved; l++)
2736 trigger_events[group->element_resolved[l]][k] = TRUE;
2738 else if (trigger_element == EL_ANY_ELEMENT)
2739 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2740 trigger_events[l][k] = TRUE;
2742 trigger_events[trigger_element][k] = TRUE;
2749 /* ---------- initialize push delay -------------------------------------- */
2751 /* initialize push delay values to default */
2752 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2754 if (!IS_CUSTOM_ELEMENT(i))
2756 /* set default push delay values (corrected since version 3.0.7-1) */
2757 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2759 element_info[i].push_delay_fixed = 2;
2760 element_info[i].push_delay_random = 8;
2764 element_info[i].push_delay_fixed = 8;
2765 element_info[i].push_delay_random = 8;
2770 /* set push delay value for certain elements from pre-defined list */
2771 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2773 int e = push_delay_list[i].element;
2775 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2776 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2779 /* set push delay value for Supaplex elements for newer engine versions */
2780 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2782 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2784 if (IS_SP_ELEMENT(i))
2786 /* set SP push delay to just enough to push under a falling zonk */
2787 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2789 element_info[i].push_delay_fixed = delay;
2790 element_info[i].push_delay_random = 0;
2795 /* ---------- initialize move stepsize ----------------------------------- */
2797 /* initialize move stepsize values to default */
2798 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2799 if (!IS_CUSTOM_ELEMENT(i))
2800 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2802 /* set move stepsize value for certain elements from pre-defined list */
2803 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2805 int e = move_stepsize_list[i].element;
2807 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2810 /* ---------- initialize collect score ----------------------------------- */
2812 /* initialize collect score values for custom elements from initial value */
2813 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2814 if (IS_CUSTOM_ELEMENT(i))
2815 element_info[i].collect_score = element_info[i].collect_score_initial;
2817 /* ---------- initialize collect count ----------------------------------- */
2819 /* initialize collect count values for non-custom elements */
2820 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2821 if (!IS_CUSTOM_ELEMENT(i))
2822 element_info[i].collect_count_initial = 0;
2824 /* add collect count values for all elements from pre-defined list */
2825 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2826 element_info[collect_count_list[i].element].collect_count_initial =
2827 collect_count_list[i].count;
2829 /* ---------- initialize access direction -------------------------------- */
2831 /* initialize access direction values to default (access from every side) */
2832 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2833 if (!IS_CUSTOM_ELEMENT(i))
2834 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2836 /* set access direction value for certain elements from pre-defined list */
2837 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2838 element_info[access_direction_list[i].element].access_direction =
2839 access_direction_list[i].direction;
2841 /* ---------- initialize explosion content ------------------------------- */
2842 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2844 if (IS_CUSTOM_ELEMENT(i))
2847 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2849 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2851 element_info[i].content.e[x][y] =
2852 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2853 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2854 i == EL_PLAYER_3 ? EL_EMERALD :
2855 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2856 i == EL_MOLE ? EL_EMERALD_RED :
2857 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2858 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2859 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2860 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2861 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2862 i == EL_WALL_EMERALD ? EL_EMERALD :
2863 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2864 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2865 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2866 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2867 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2868 i == EL_WALL_PEARL ? EL_PEARL :
2869 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2874 /* ---------- initialize recursion detection ------------------------------ */
2875 recursion_loop_depth = 0;
2876 recursion_loop_detected = FALSE;
2877 recursion_loop_element = EL_UNDEFINED;
2880 int get_num_special_action(int element, int action_first, int action_last)
2882 int num_special_action = 0;
2885 for (i = action_first; i <= action_last; i++)
2887 boolean found = FALSE;
2889 for (j = 0; j < NUM_DIRECTIONS; j++)
2890 if (el_act_dir2img(element, i, j) !=
2891 el_act_dir2img(element, ACTION_DEFAULT, j))
2895 num_special_action++;
2900 return num_special_action;
2905 =============================================================================
2907 -----------------------------------------------------------------------------
2908 initialize and start new game
2909 =============================================================================
2914 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2915 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2916 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2918 boolean do_fading = (game_status == GAME_MODE_MAIN);
2922 game_status = GAME_MODE_PLAYING;
2925 InitGameControlValues();
2927 /* don't play tapes over network */
2928 network_playing = (options.network && !tape.playing);
2930 for (i = 0; i < MAX_PLAYERS; i++)
2932 struct PlayerInfo *player = &stored_player[i];
2934 player->index_nr = i;
2935 player->index_bit = (1 << i);
2936 player->element_nr = EL_PLAYER_1 + i;
2938 player->present = FALSE;
2939 player->active = FALSE;
2940 player->killed = FALSE;
2943 player->effective_action = 0;
2944 player->programmed_action = 0;
2947 player->score_final = 0;
2949 player->gems_still_needed = level.gems_needed;
2950 player->sokobanfields_still_needed = 0;
2951 player->lights_still_needed = 0;
2952 player->friends_still_needed = 0;
2954 for (j = 0; j < MAX_NUM_KEYS; j++)
2955 player->key[j] = FALSE;
2957 player->num_white_keys = 0;
2959 player->dynabomb_count = 0;
2960 player->dynabomb_size = 1;
2961 player->dynabombs_left = 0;
2962 player->dynabomb_xl = FALSE;
2964 player->MovDir = MV_NONE;
2967 player->GfxDir = MV_NONE;
2968 player->GfxAction = ACTION_DEFAULT;
2970 player->StepFrame = 0;
2972 player->use_murphy = FALSE;
2973 player->artwork_element =
2974 (level.use_artwork_element[i] ? level.artwork_element[i] :
2975 player->element_nr);
2977 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2978 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2980 player->gravity = level.initial_player_gravity[i];
2982 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2984 player->actual_frame_counter = 0;
2986 player->step_counter = 0;
2988 player->last_move_dir = MV_NONE;
2990 player->is_active = FALSE;
2992 player->is_waiting = FALSE;
2993 player->is_moving = FALSE;
2994 player->is_auto_moving = FALSE;
2995 player->is_digging = FALSE;
2996 player->is_snapping = FALSE;
2997 player->is_collecting = FALSE;
2998 player->is_pushing = FALSE;
2999 player->is_switching = FALSE;
3000 player->is_dropping = FALSE;
3001 player->is_dropping_pressed = FALSE;
3003 player->is_bored = FALSE;
3004 player->is_sleeping = FALSE;
3006 player->frame_counter_bored = -1;
3007 player->frame_counter_sleeping = -1;
3009 player->anim_delay_counter = 0;
3010 player->post_delay_counter = 0;
3012 player->dir_waiting = MV_NONE;
3013 player->action_waiting = ACTION_DEFAULT;
3014 player->last_action_waiting = ACTION_DEFAULT;
3015 player->special_action_bored = ACTION_DEFAULT;
3016 player->special_action_sleeping = ACTION_DEFAULT;
3018 player->switch_x = -1;
3019 player->switch_y = -1;
3021 player->drop_x = -1;
3022 player->drop_y = -1;
3024 player->show_envelope = 0;
3026 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3028 player->push_delay = -1; /* initialized when pushing starts */
3029 player->push_delay_value = game.initial_push_delay_value;
3031 player->drop_delay = 0;
3032 player->drop_pressed_delay = 0;
3034 player->last_jx = -1;
3035 player->last_jy = -1;
3039 player->shield_normal_time_left = 0;
3040 player->shield_deadly_time_left = 0;
3042 player->inventory_infinite_element = EL_UNDEFINED;
3043 player->inventory_size = 0;
3045 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3046 SnapField(player, 0, 0);
3048 player->LevelSolved = FALSE;
3049 player->GameOver = FALSE;
3051 player->LevelSolved_GameWon = FALSE;
3052 player->LevelSolved_GameEnd = FALSE;
3053 player->LevelSolved_PanelOff = FALSE;
3054 player->LevelSolved_SaveTape = FALSE;
3055 player->LevelSolved_SaveScore = FALSE;
3058 network_player_action_received = FALSE;
3060 #if defined(NETWORK_AVALIABLE)
3061 /* initial null action */
3062 if (network_playing)
3063 SendToServer_MovePlayer(MV_NONE);
3072 TimeLeft = level.time;
3075 ScreenMovDir = MV_NONE;
3079 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3081 AllPlayersGone = FALSE;
3083 game.yamyam_content_nr = 0;
3084 game.magic_wall_active = FALSE;
3085 game.magic_wall_time_left = 0;
3086 game.light_time_left = 0;
3087 game.timegate_time_left = 0;
3088 game.switchgate_pos = 0;
3089 game.wind_direction = level.wind_direction_initial;
3091 #if !USE_PLAYER_GRAVITY
3092 game.gravity = FALSE;
3093 game.explosions_delayed = TRUE;
3096 game.lenses_time_left = 0;
3097 game.magnify_time_left = 0;
3099 game.ball_state = level.ball_state_initial;
3100 game.ball_content_nr = 0;
3102 game.envelope_active = FALSE;
3104 /* set focus to local player for network games, else to all players */
3105 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3106 game.centered_player_nr_next = game.centered_player_nr;
3107 game.set_centered_player = FALSE;
3109 if (network_playing && tape.recording)
3111 /* store client dependent player focus when recording network games */
3112 tape.centered_player_nr_next = game.centered_player_nr_next;
3113 tape.set_centered_player = TRUE;
3116 for (i = 0; i < NUM_BELTS; i++)
3118 game.belt_dir[i] = MV_NONE;
3119 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3122 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3123 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3125 SCAN_PLAYFIELD(x, y)
3127 Feld[x][y] = level.field[x][y];
3128 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3129 ChangeDelay[x][y] = 0;
3130 ChangePage[x][y] = -1;
3131 #if USE_NEW_CUSTOM_VALUE
3132 CustomValue[x][y] = 0; /* initialized in InitField() */
3134 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3136 WasJustMoving[x][y] = 0;
3137 WasJustFalling[x][y] = 0;
3138 CheckCollision[x][y] = 0;
3139 CheckImpact[x][y] = 0;
3141 Pushed[x][y] = FALSE;
3143 ChangeCount[x][y] = 0;
3144 ChangeEvent[x][y] = -1;
3146 ExplodePhase[x][y] = 0;
3147 ExplodeDelay[x][y] = 0;
3148 ExplodeField[x][y] = EX_TYPE_NONE;
3150 RunnerVisit[x][y] = 0;
3151 PlayerVisit[x][y] = 0;
3154 GfxRandom[x][y] = INIT_GFX_RANDOM();
3155 GfxElement[x][y] = EL_UNDEFINED;
3156 GfxAction[x][y] = ACTION_DEFAULT;
3157 GfxDir[x][y] = MV_NONE;
3160 SCAN_PLAYFIELD(x, y)
3162 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3164 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3166 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3169 InitField(x, y, TRUE);
3174 for (i = 0; i < MAX_PLAYERS; i++)
3176 struct PlayerInfo *player = &stored_player[i];
3178 /* set number of special actions for bored and sleeping animation */
3179 player->num_special_action_bored =
3180 get_num_special_action(player->artwork_element,
3181 ACTION_BORING_1, ACTION_BORING_LAST);
3182 player->num_special_action_sleeping =
3183 get_num_special_action(player->artwork_element,
3184 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3187 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3188 emulate_sb ? EMU_SOKOBAN :
3189 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3191 #if USE_NEW_ALL_SLIPPERY
3192 /* initialize type of slippery elements */
3193 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3195 if (!IS_CUSTOM_ELEMENT(i))
3197 /* default: elements slip down either to the left or right randomly */
3198 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3200 /* SP style elements prefer to slip down on the left side */
3201 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3202 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3204 /* BD style elements prefer to slip down on the left side */
3205 if (game.emulation == EMU_BOULDERDASH)
3206 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3211 /* initialize explosion and ignition delay */
3212 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3214 if (!IS_CUSTOM_ELEMENT(i))
3217 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3218 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3219 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3220 int last_phase = (num_phase + 1) * delay;
3221 int half_phase = (num_phase / 2) * delay;
3223 element_info[i].explosion_delay = last_phase - 1;
3224 element_info[i].ignition_delay = half_phase;
3226 if (i == EL_BLACK_ORB)
3227 element_info[i].ignition_delay = 1;
3231 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3232 element_info[i].explosion_delay = 1;
3234 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3235 element_info[i].ignition_delay = 1;
3239 /* correct non-moving belts to start moving left */
3240 for (i = 0; i < NUM_BELTS; i++)
3241 if (game.belt_dir[i] == MV_NONE)
3242 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3244 /* check if any connected player was not found in playfield */
3245 for (i = 0; i < MAX_PLAYERS; i++)
3247 struct PlayerInfo *player = &stored_player[i];
3249 if (player->connected && !player->present)
3251 for (j = 0; j < MAX_PLAYERS; j++)
3253 struct PlayerInfo *some_player = &stored_player[j];
3254 int jx = some_player->jx, jy = some_player->jy;
3256 /* assign first free player found that is present in the playfield */
3257 if (some_player->present && !some_player->connected)
3259 player->present = TRUE;
3260 player->active = TRUE;
3262 some_player->present = FALSE;
3263 some_player->active = FALSE;
3265 player->artwork_element = some_player->artwork_element;
3267 player->block_last_field = some_player->block_last_field;
3268 player->block_delay_adjustment = some_player->block_delay_adjustment;
3270 StorePlayer[jx][jy] = player->element_nr;
3271 player->jx = player->last_jx = jx;
3272 player->jy = player->last_jy = jy;
3282 /* when playing a tape, eliminate all players who do not participate */
3284 for (i = 0; i < MAX_PLAYERS; i++)
3286 if (stored_player[i].active && !tape.player_participates[i])
3288 struct PlayerInfo *player = &stored_player[i];
3289 int jx = player->jx, jy = player->jy;
3291 player->active = FALSE;
3292 StorePlayer[jx][jy] = 0;
3293 Feld[jx][jy] = EL_EMPTY;
3297 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3299 /* when in single player mode, eliminate all but the first active player */
3301 for (i = 0; i < MAX_PLAYERS; i++)
3303 if (stored_player[i].active)
3305 for (j = i + 1; j < MAX_PLAYERS; j++)
3307 if (stored_player[j].active)
3309 struct PlayerInfo *player = &stored_player[j];
3310 int jx = player->jx, jy = player->jy;
3312 player->active = FALSE;
3313 player->present = FALSE;
3315 StorePlayer[jx][jy] = 0;
3316 Feld[jx][jy] = EL_EMPTY;
3323 /* when recording the game, store which players take part in the game */
3326 for (i = 0; i < MAX_PLAYERS; i++)
3327 if (stored_player[i].active)
3328 tape.player_participates[i] = TRUE;
3333 for (i = 0; i < MAX_PLAYERS; i++)
3335 struct PlayerInfo *player = &stored_player[i];
3337 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3342 if (local_player == player)
3343 printf("Player %d is local player.\n", i+1);
3347 if (BorderElement == EL_EMPTY)
3350 SBX_Right = lev_fieldx - SCR_FIELDX;
3352 SBY_Lower = lev_fieldy - SCR_FIELDY;
3357 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3359 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3362 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3363 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3365 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3366 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3368 /* if local player not found, look for custom element that might create
3369 the player (make some assumptions about the right custom element) */
3370 if (!local_player->present)
3372 int start_x = 0, start_y = 0;
3373 int found_rating = 0;
3374 int found_element = EL_UNDEFINED;
3375 int player_nr = local_player->index_nr;
3377 SCAN_PLAYFIELD(x, y)
3379 int element = Feld[x][y];
3384 if (level.use_start_element[player_nr] &&
3385 level.start_element[player_nr] == element &&
3392 found_element = element;
3395 if (!IS_CUSTOM_ELEMENT(element))
3398 if (CAN_CHANGE(element))
3400 for (i = 0; i < element_info[element].num_change_pages; i++)
3402 /* check for player created from custom element as single target */
3403 content = element_info[element].change_page[i].target_element;
3404 is_player = ELEM_IS_PLAYER(content);
3406 if (is_player && (found_rating < 3 ||
3407 (found_rating == 3 && element < found_element)))
3413 found_element = element;
3418 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3420 /* check for player created from custom element as explosion content */
3421 content = element_info[element].content.e[xx][yy];
3422 is_player = ELEM_IS_PLAYER(content);
3424 if (is_player && (found_rating < 2 ||
3425 (found_rating == 2 && element < found_element)))
3427 start_x = x + xx - 1;
3428 start_y = y + yy - 1;
3431 found_element = element;
3434 if (!CAN_CHANGE(element))
3437 for (i = 0; i < element_info[element].num_change_pages; i++)
3439 /* check for player created from custom element as extended target */
3441 element_info[element].change_page[i].target_content.e[xx][yy];
3443 is_player = ELEM_IS_PLAYER(content);
3445 if (is_player && (found_rating < 1 ||
3446 (found_rating == 1 && element < found_element)))
3448 start_x = x + xx - 1;
3449 start_y = y + yy - 1;
3452 found_element = element;
3458 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3459 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3462 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3463 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3468 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3469 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3470 local_player->jx - MIDPOSX);
3472 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3473 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3474 local_player->jy - MIDPOSY);
3479 if (!game.restart_level)
3480 CloseDoor(DOOR_CLOSE_1);
3483 if (level_editor_test_game)
3484 FadeSkipNextFadeIn();
3488 if (level_editor_test_game)
3489 fading = fading_none;
3491 fading = menu.destination;
3495 FadeOut(REDRAW_FIELD);
3498 FadeOut(REDRAW_FIELD);
3501 /* !!! FIX THIS (START) !!! */
3502 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3504 InitGameEngine_EM();
3506 /* blit playfield from scroll buffer to normal back buffer for fading in */
3507 BlitScreenToBitmap_EM(backbuffer);
3514 /* after drawing the level, correct some elements */
3515 if (game.timegate_time_left == 0)
3516 CloseAllOpenTimegates();
3518 /* blit playfield from scroll buffer to normal back buffer for fading in */
3519 if (setup.soft_scrolling)
3520 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3522 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3524 /* !!! FIX THIS (END) !!! */
3527 FadeIn(REDRAW_FIELD);
3530 FadeIn(REDRAW_FIELD);
3535 if (!game.restart_level)
3537 /* copy default game door content to main double buffer */
3538 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3539 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3542 SetPanelBackground();
3543 SetDrawBackgroundMask(REDRAW_DOOR_1);
3545 DrawGameDoorValues();
3547 if (!game.restart_level)
3551 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3552 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3553 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3557 /* copy actual game door content to door double buffer for OpenDoor() */
3558 BlitBitmap(drawto, bitmap_db_door,
3559 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3561 OpenDoor(DOOR_OPEN_ALL);
3563 PlaySound(SND_GAME_STARTING);
3565 if (setup.sound_music)
3568 KeyboardAutoRepeatOffUnlessAutoplay();
3572 for (i = 0; i < MAX_PLAYERS; i++)
3573 printf("Player %d %sactive.\n",
3574 i + 1, (stored_player[i].active ? "" : "not "));
3585 game.restart_level = FALSE;
3588 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3590 /* this is used for non-R'n'D game engines to update certain engine values */
3592 /* needed to determine if sounds are played within the visible screen area */
3593 scroll_x = actual_scroll_x;
3594 scroll_y = actual_scroll_y;
3597 void InitMovDir(int x, int y)
3599 int i, element = Feld[x][y];
3600 static int xy[4][2] =
3607 static int direction[3][4] =
3609 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3610 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3611 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3620 Feld[x][y] = EL_BUG;
3621 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3624 case EL_SPACESHIP_RIGHT:
3625 case EL_SPACESHIP_UP:
3626 case EL_SPACESHIP_LEFT:
3627 case EL_SPACESHIP_DOWN:
3628 Feld[x][y] = EL_SPACESHIP;
3629 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3632 case EL_BD_BUTTERFLY_RIGHT:
3633 case EL_BD_BUTTERFLY_UP:
3634 case EL_BD_BUTTERFLY_LEFT:
3635 case EL_BD_BUTTERFLY_DOWN:
3636 Feld[x][y] = EL_BD_BUTTERFLY;
3637 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3640 case EL_BD_FIREFLY_RIGHT:
3641 case EL_BD_FIREFLY_UP:
3642 case EL_BD_FIREFLY_LEFT:
3643 case EL_BD_FIREFLY_DOWN:
3644 Feld[x][y] = EL_BD_FIREFLY;
3645 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3648 case EL_PACMAN_RIGHT:
3650 case EL_PACMAN_LEFT:
3651 case EL_PACMAN_DOWN:
3652 Feld[x][y] = EL_PACMAN;
3653 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3656 case EL_YAMYAM_LEFT:
3657 case EL_YAMYAM_RIGHT:
3659 case EL_YAMYAM_DOWN:
3660 Feld[x][y] = EL_YAMYAM;
3661 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3664 case EL_SP_SNIKSNAK:
3665 MovDir[x][y] = MV_UP;
3668 case EL_SP_ELECTRON:
3669 MovDir[x][y] = MV_LEFT;
3676 Feld[x][y] = EL_MOLE;
3677 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3681 if (IS_CUSTOM_ELEMENT(element))
3683 struct ElementInfo *ei = &element_info[element];
3684 int move_direction_initial = ei->move_direction_initial;
3685 int move_pattern = ei->move_pattern;
3687 if (move_direction_initial == MV_START_PREVIOUS)
3689 if (MovDir[x][y] != MV_NONE)
3692 move_direction_initial = MV_START_AUTOMATIC;
3695 if (move_direction_initial == MV_START_RANDOM)
3696 MovDir[x][y] = 1 << RND(4);
3697 else if (move_direction_initial & MV_ANY_DIRECTION)
3698 MovDir[x][y] = move_direction_initial;
3699 else if (move_pattern == MV_ALL_DIRECTIONS ||
3700 move_pattern == MV_TURNING_LEFT ||
3701 move_pattern == MV_TURNING_RIGHT ||
3702 move_pattern == MV_TURNING_LEFT_RIGHT ||
3703 move_pattern == MV_TURNING_RIGHT_LEFT ||
3704 move_pattern == MV_TURNING_RANDOM)
3705 MovDir[x][y] = 1 << RND(4);
3706 else if (move_pattern == MV_HORIZONTAL)
3707 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3708 else if (move_pattern == MV_VERTICAL)
3709 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3710 else if (move_pattern & MV_ANY_DIRECTION)
3711 MovDir[x][y] = element_info[element].move_pattern;
3712 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3713 move_pattern == MV_ALONG_RIGHT_SIDE)
3715 /* use random direction as default start direction */
3716 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3717 MovDir[x][y] = 1 << RND(4);
3719 for (i = 0; i < NUM_DIRECTIONS; i++)
3721 int x1 = x + xy[i][0];
3722 int y1 = y + xy[i][1];
3724 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3726 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3727 MovDir[x][y] = direction[0][i];
3729 MovDir[x][y] = direction[1][i];
3738 MovDir[x][y] = 1 << RND(4);
3740 if (element != EL_BUG &&
3741 element != EL_SPACESHIP &&
3742 element != EL_BD_BUTTERFLY &&
3743 element != EL_BD_FIREFLY)
3746 for (i = 0; i < NUM_DIRECTIONS; i++)
3748 int x1 = x + xy[i][0];
3749 int y1 = y + xy[i][1];
3751 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3753 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3755 MovDir[x][y] = direction[0][i];
3758 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3759 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3761 MovDir[x][y] = direction[1][i];
3770 GfxDir[x][y] = MovDir[x][y];
3773 void InitAmoebaNr(int x, int y)
3776 int group_nr = AmoebeNachbarNr(x, y);
3780 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3782 if (AmoebaCnt[i] == 0)
3790 AmoebaNr[x][y] = group_nr;
3791 AmoebaCnt[group_nr]++;
3792 AmoebaCnt2[group_nr]++;
3795 static void PlayerWins(struct PlayerInfo *player)
3797 player->LevelSolved = TRUE;
3798 player->GameOver = TRUE;
3800 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3801 level.native_em_level->lev->score : player->score);
3806 static int time, time_final;
3807 static int score, score_final;
3808 static int game_over_delay_1 = 0;
3809 static int game_over_delay_2 = 0;
3810 int game_over_delay_value_1 = 50;
3811 int game_over_delay_value_2 = 50;
3813 if (!local_player->LevelSolved_GameWon)
3817 /* do not start end game actions before the player stops moving (to exit) */
3818 if (local_player->MovPos)
3821 local_player->LevelSolved_GameWon = TRUE;
3822 local_player->LevelSolved_SaveTape = tape.recording;
3823 local_player->LevelSolved_SaveScore = !tape.playing;
3825 if (tape.auto_play) /* tape might already be stopped here */
3826 tape.auto_play_level_solved = TRUE;
3832 game_over_delay_1 = game_over_delay_value_1;
3833 game_over_delay_2 = game_over_delay_value_2;
3835 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3836 score = score_final = local_player->score_final;
3841 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3843 else if (level.time == 0 && TimePlayed < 999)
3846 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3849 local_player->score_final = score_final;
3851 if (level_editor_test_game)
3854 score = score_final;
3857 game_control_value[GAME_CONTROL_TIME] = time;
3858 game_control_value[GAME_CONTROL_SCORE] = score;
3860 DisplayGameControlValues();
3862 DrawGameValue_Time(time);
3863 DrawGameValue_Score(score);
3867 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3869 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3871 /* close exit door after last player */
3872 if ((AllPlayersGone &&
3873 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3874 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3875 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3876 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3877 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3879 int element = Feld[ExitX][ExitY];
3882 if (element == EL_EM_EXIT_OPEN ||
3883 element == EL_EM_STEEL_EXIT_OPEN)
3890 Feld[ExitX][ExitY] =
3891 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3892 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3893 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3894 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3895 EL_EM_STEEL_EXIT_CLOSING);
3897 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3901 /* player disappears */
3902 DrawLevelField(ExitX, ExitY);
3905 for (i = 0; i < MAX_PLAYERS; i++)
3907 struct PlayerInfo *player = &stored_player[i];
3909 if (player->present)
3911 RemovePlayer(player);
3913 /* player disappears */
3914 DrawLevelField(player->jx, player->jy);
3919 PlaySound(SND_GAME_WINNING);
3922 if (game_over_delay_1 > 0)
3924 game_over_delay_1--;
3929 if (time != time_final)
3931 int time_to_go = ABS(time_final - time);
3932 int time_count_dir = (time < time_final ? +1 : -1);
3933 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3935 time += time_count_steps * time_count_dir;
3936 score += time_count_steps * level.score[SC_TIME_BONUS];
3939 game_control_value[GAME_CONTROL_TIME] = time;
3940 game_control_value[GAME_CONTROL_SCORE] = score;
3942 DisplayGameControlValues();
3944 DrawGameValue_Time(time);
3945 DrawGameValue_Score(score);
3948 if (time == time_final)
3949 StopSound(SND_GAME_LEVELTIME_BONUS);
3950 else if (setup.sound_loops)
3951 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3953 PlaySound(SND_GAME_LEVELTIME_BONUS);
3958 local_player->LevelSolved_PanelOff = TRUE;
3960 if (game_over_delay_2 > 0)
3962 game_over_delay_2--;
3975 boolean raise_level = FALSE;
3977 local_player->LevelSolved_GameEnd = TRUE;
3979 CloseDoor(DOOR_CLOSE_1);
3981 if (local_player->LevelSolved_SaveTape)
3988 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3990 SaveTape(tape.level_nr); /* ask to save tape */
3994 if (level_editor_test_game)
3996 game_status = GAME_MODE_MAIN;
3999 DrawAndFadeInMainMenu(REDRAW_FIELD);
4007 if (!local_player->LevelSolved_SaveScore)
4010 FadeOut(REDRAW_FIELD);
4013 game_status = GAME_MODE_MAIN;
4015 DrawAndFadeInMainMenu(REDRAW_FIELD);
4020 if (level_nr == leveldir_current->handicap_level)
4022 leveldir_current->handicap_level++;
4023 SaveLevelSetup_SeriesInfo();
4026 if (level_nr < leveldir_current->last_level)
4027 raise_level = TRUE; /* advance to next level */
4029 if ((hi_pos = NewHiScore()) >= 0)
4031 game_status = GAME_MODE_SCORES;
4033 DrawHallOfFame(hi_pos);
4044 FadeOut(REDRAW_FIELD);
4047 game_status = GAME_MODE_MAIN;
4055 DrawAndFadeInMainMenu(REDRAW_FIELD);
4064 LoadScore(level_nr);
4066 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4067 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4070 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4072 if (local_player->score_final > highscore[k].Score)
4074 /* player has made it to the hall of fame */
4076 if (k < MAX_SCORE_ENTRIES - 1)
4078 int m = MAX_SCORE_ENTRIES - 1;
4081 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4082 if (strEqual(setup.player_name, highscore[l].Name))
4084 if (m == k) /* player's new highscore overwrites his old one */
4088 for (l = m; l > k; l--)
4090 strcpy(highscore[l].Name, highscore[l - 1].Name);
4091 highscore[l].Score = highscore[l - 1].Score;
4098 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4099 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4100 highscore[k].Score = local_player->score_final;
4106 else if (!strncmp(setup.player_name, highscore[k].Name,
4107 MAX_PLAYER_NAME_LEN))
4108 break; /* player already there with a higher score */
4114 SaveScore(level_nr);
4119 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4121 int element = Feld[x][y];
4122 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4123 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4124 int horiz_move = (dx != 0);
4125 int sign = (horiz_move ? dx : dy);
4126 int step = sign * element_info[element].move_stepsize;
4128 /* special values for move stepsize for spring and things on conveyor belt */
4131 if (CAN_FALL(element) &&
4132 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4133 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4134 else if (element == EL_SPRING)
4135 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4141 inline static int getElementMoveStepsize(int x, int y)
4143 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4146 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4148 if (player->GfxAction != action || player->GfxDir != dir)
4151 printf("Player frame reset! (%d => %d, %d => %d)\n",
4152 player->GfxAction, action, player->GfxDir, dir);
4155 player->GfxAction = action;
4156 player->GfxDir = dir;
4158 player->StepFrame = 0;
4162 #if USE_GFX_RESET_GFX_ANIMATION
4163 static void ResetGfxFrame(int x, int y, boolean redraw)
4165 int element = Feld[x][y];
4166 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4167 int last_gfx_frame = GfxFrame[x][y];
4169 if (graphic_info[graphic].anim_global_sync)
4170 GfxFrame[x][y] = FrameCounter;
4171 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4172 GfxFrame[x][y] = CustomValue[x][y];
4173 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4174 GfxFrame[x][y] = element_info[element].collect_score;
4175 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4176 GfxFrame[x][y] = ChangeDelay[x][y];
4178 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4179 DrawLevelGraphicAnimation(x, y, graphic);
4183 static void ResetGfxAnimation(int x, int y)
4185 GfxAction[x][y] = ACTION_DEFAULT;
4186 GfxDir[x][y] = MovDir[x][y];
4189 #if USE_GFX_RESET_GFX_ANIMATION
4190 ResetGfxFrame(x, y, FALSE);
4194 static void ResetRandomAnimationValue(int x, int y)
4196 GfxRandom[x][y] = INIT_GFX_RANDOM();
4199 void InitMovingField(int x, int y, int direction)
4201 int element = Feld[x][y];
4202 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4203 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4206 boolean is_moving_before, is_moving_after;
4208 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4211 /* check if element was/is moving or being moved before/after mode change */
4214 is_moving_before = (WasJustMoving[x][y] != 0);
4216 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4217 is_moving_before = WasJustMoving[x][y];
4220 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4222 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4224 /* reset animation only for moving elements which change direction of moving
4225 or which just started or stopped moving
4226 (else CEs with property "can move" / "not moving" are reset each frame) */
4227 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4229 if (is_moving_before != is_moving_after ||
4230 direction != MovDir[x][y])
4231 ResetGfxAnimation(x, y);
4233 if ((is_moving_before || is_moving_after) && !continues_moving)
4234 ResetGfxAnimation(x, y);
4237 if (!continues_moving)
4238 ResetGfxAnimation(x, y);
4241 MovDir[x][y] = direction;
4242 GfxDir[x][y] = direction;
4244 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4245 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4246 direction == MV_DOWN && CAN_FALL(element) ?
4247 ACTION_FALLING : ACTION_MOVING);
4249 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4250 ACTION_FALLING : ACTION_MOVING);
4253 /* this is needed for CEs with property "can move" / "not moving" */
4255 if (is_moving_after)
4257 if (Feld[newx][newy] == EL_EMPTY)
4258 Feld[newx][newy] = EL_BLOCKED;
4260 MovDir[newx][newy] = MovDir[x][y];
4262 #if USE_NEW_CUSTOM_VALUE
4263 CustomValue[newx][newy] = CustomValue[x][y];
4266 GfxFrame[newx][newy] = GfxFrame[x][y];
4267 GfxRandom[newx][newy] = GfxRandom[x][y];
4268 GfxAction[newx][newy] = GfxAction[x][y];
4269 GfxDir[newx][newy] = GfxDir[x][y];
4273 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4275 int direction = MovDir[x][y];
4276 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4277 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4283 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4285 int oldx = x, oldy = y;
4286 int direction = MovDir[x][y];
4288 if (direction == MV_LEFT)
4290 else if (direction == MV_RIGHT)
4292 else if (direction == MV_UP)
4294 else if (direction == MV_DOWN)
4297 *comes_from_x = oldx;
4298 *comes_from_y = oldy;
4301 int MovingOrBlocked2Element(int x, int y)
4303 int element = Feld[x][y];
4305 if (element == EL_BLOCKED)
4309 Blocked2Moving(x, y, &oldx, &oldy);
4310 return Feld[oldx][oldy];
4316 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4318 /* like MovingOrBlocked2Element(), but if element is moving
4319 and (x,y) is the field the moving element is just leaving,
4320 return EL_BLOCKED instead of the element value */
4321 int element = Feld[x][y];
4323 if (IS_MOVING(x, y))
4325 if (element == EL_BLOCKED)
4329 Blocked2Moving(x, y, &oldx, &oldy);
4330 return Feld[oldx][oldy];
4339 static void RemoveField(int x, int y)
4341 Feld[x][y] = EL_EMPTY;
4347 #if USE_NEW_CUSTOM_VALUE
4348 CustomValue[x][y] = 0;
4352 ChangeDelay[x][y] = 0;
4353 ChangePage[x][y] = -1;
4354 Pushed[x][y] = FALSE;
4357 ExplodeField[x][y] = EX_TYPE_NONE;
4360 GfxElement[x][y] = EL_UNDEFINED;
4361 GfxAction[x][y] = ACTION_DEFAULT;
4362 GfxDir[x][y] = MV_NONE;
4365 void RemoveMovingField(int x, int y)
4367 int oldx = x, oldy = y, newx = x, newy = y;
4368 int element = Feld[x][y];
4369 int next_element = EL_UNDEFINED;
4371 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4374 if (IS_MOVING(x, y))
4376 Moving2Blocked(x, y, &newx, &newy);
4378 if (Feld[newx][newy] != EL_BLOCKED)
4380 /* element is moving, but target field is not free (blocked), but
4381 already occupied by something different (example: acid pool);
4382 in this case, only remove the moving field, but not the target */
4384 RemoveField(oldx, oldy);
4386 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4388 DrawLevelField(oldx, oldy);
4393 else if (element == EL_BLOCKED)
4395 Blocked2Moving(x, y, &oldx, &oldy);
4396 if (!IS_MOVING(oldx, oldy))
4400 if (element == EL_BLOCKED &&
4401 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4402 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4403 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4404 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4405 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4406 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4407 next_element = get_next_element(Feld[oldx][oldy]);
4409 RemoveField(oldx, oldy);
4410 RemoveField(newx, newy);
4412 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4414 if (next_element != EL_UNDEFINED)
4415 Feld[oldx][oldy] = next_element;
4417 DrawLevelField(oldx, oldy);
4418 DrawLevelField(newx, newy);
4421 void DrawDynamite(int x, int y)
4423 int sx = SCREENX(x), sy = SCREENY(y);
4424 int graphic = el2img(Feld[x][y]);
4427 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4430 if (IS_WALKABLE_INSIDE(Back[x][y]))
4434 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4435 else if (Store[x][y])
4436 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4438 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4440 if (Back[x][y] || Store[x][y])
4441 DrawGraphicThruMask(sx, sy, graphic, frame);
4443 DrawGraphic(sx, sy, graphic, frame);
4446 void CheckDynamite(int x, int y)
4448 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4452 if (MovDelay[x][y] != 0)
4455 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4461 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4466 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4468 boolean num_checked_players = 0;
4471 for (i = 0; i < MAX_PLAYERS; i++)
4473 if (stored_player[i].active)
4475 int sx = stored_player[i].jx;
4476 int sy = stored_player[i].jy;
4478 if (num_checked_players == 0)
4485 *sx1 = MIN(*sx1, sx);
4486 *sy1 = MIN(*sy1, sy);
4487 *sx2 = MAX(*sx2, sx);
4488 *sy2 = MAX(*sy2, sy);
4491 num_checked_players++;
4496 static boolean checkIfAllPlayersFitToScreen_RND()
4498 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4500 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4502 return (sx2 - sx1 < SCR_FIELDX &&
4503 sy2 - sy1 < SCR_FIELDY);
4506 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4508 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4510 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4512 *sx = (sx1 + sx2) / 2;
4513 *sy = (sy1 + sy2) / 2;
4516 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4517 boolean center_screen, boolean quick_relocation)
4519 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4520 boolean no_delay = (tape.warp_forward);
4521 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4522 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4524 if (quick_relocation)
4526 int offset = (setup.scroll_delay ? setup.scroll_delay_value : 0);
4528 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4530 if (!level.shifted_relocation || center_screen)
4532 /* quick relocation (without scrolling), with centering of screen */
4534 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4535 x > SBX_Right + MIDPOSX ? SBX_Right :
4538 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4539 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4544 /* quick relocation (without scrolling), but do not center screen */
4546 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4547 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4550 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4551 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4554 int offset_x = x + (scroll_x - center_scroll_x);
4555 int offset_y = y + (scroll_y - center_scroll_y);
4557 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4558 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4559 offset_x - MIDPOSX);
4561 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4562 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4563 offset_y - MIDPOSY);
4568 /* quick relocation (without scrolling), inside visible screen area */
4570 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4571 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4572 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4574 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4575 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4576 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4578 /* don't scroll over playfield boundaries */
4579 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4580 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4582 /* don't scroll over playfield boundaries */
4583 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4584 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4587 RedrawPlayfield(TRUE, 0,0,0,0);
4592 int scroll_xx, scroll_yy;
4594 if (!level.shifted_relocation || center_screen)
4596 /* visible relocation (with scrolling), with centering of screen */
4598 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4599 x > SBX_Right + MIDPOSX ? SBX_Right :
4602 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4603 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4608 /* visible relocation (with scrolling), but do not center screen */
4610 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4611 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4614 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4615 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4618 int offset_x = x + (scroll_x - center_scroll_x);
4619 int offset_y = y + (scroll_y - center_scroll_y);
4621 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4622 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4623 offset_x - MIDPOSX);
4625 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4626 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4627 offset_y - MIDPOSY);
4632 /* visible relocation (with scrolling), with centering of screen */
4634 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4635 x > SBX_Right + MIDPOSX ? SBX_Right :
4638 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4639 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4643 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4645 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4648 int fx = FX, fy = FY;
4650 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4651 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4653 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4659 fx += dx * TILEX / 2;
4660 fy += dy * TILEY / 2;
4662 ScrollLevel(dx, dy);
4665 /* scroll in two steps of half tile size to make things smoother */
4666 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4668 Delay(wait_delay_value);
4670 /* scroll second step to align at full tile size */
4672 Delay(wait_delay_value);
4677 Delay(wait_delay_value);
4681 void RelocatePlayer(int jx, int jy, int el_player_raw)
4683 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4684 int player_nr = GET_PLAYER_NR(el_player);
4685 struct PlayerInfo *player = &stored_player[player_nr];
4686 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4687 boolean no_delay = (tape.warp_forward);
4688 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4689 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4690 int old_jx = player->jx;
4691 int old_jy = player->jy;
4692 int old_element = Feld[old_jx][old_jy];
4693 int element = Feld[jx][jy];
4694 boolean player_relocated = (old_jx != jx || old_jy != jy);
4696 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4697 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4698 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4699 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4700 int leave_side_horiz = move_dir_horiz;
4701 int leave_side_vert = move_dir_vert;
4702 int enter_side = enter_side_horiz | enter_side_vert;
4703 int leave_side = leave_side_horiz | leave_side_vert;
4705 if (player->GameOver) /* do not reanimate dead player */
4708 if (!player_relocated) /* no need to relocate the player */
4711 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4713 RemoveField(jx, jy); /* temporarily remove newly placed player */
4714 DrawLevelField(jx, jy);
4717 if (player->present)
4719 while (player->MovPos)
4721 ScrollPlayer(player, SCROLL_GO_ON);
4722 ScrollScreen(NULL, SCROLL_GO_ON);
4724 AdvanceFrameAndPlayerCounters(player->index_nr);
4729 Delay(wait_delay_value);
4732 DrawPlayer(player); /* needed here only to cleanup last field */
4733 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4735 player->is_moving = FALSE;
4738 if (IS_CUSTOM_ELEMENT(old_element))
4739 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4741 player->index_bit, leave_side);
4743 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4745 player->index_bit, leave_side);
4747 Feld[jx][jy] = el_player;
4748 InitPlayerField(jx, jy, el_player, TRUE);
4750 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4752 Feld[jx][jy] = element;
4753 InitField(jx, jy, FALSE);
4756 /* only visually relocate centered player */
4757 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4758 FALSE, level.instant_relocation);
4760 TestIfPlayerTouchesBadThing(jx, jy);
4761 TestIfPlayerTouchesCustomElement(jx, jy);
4763 if (IS_CUSTOM_ELEMENT(element))
4764 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4765 player->index_bit, enter_side);
4767 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4768 player->index_bit, enter_side);
4771 void Explode(int ex, int ey, int phase, int mode)
4777 /* !!! eliminate this variable !!! */
4778 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4780 if (game.explosions_delayed)
4782 ExplodeField[ex][ey] = mode;
4786 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4788 int center_element = Feld[ex][ey];
4789 int artwork_element, explosion_element; /* set these values later */
4792 /* --- This is only really needed (and now handled) in "Impact()". --- */
4793 /* do not explode moving elements that left the explode field in time */
4794 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4795 center_element == EL_EMPTY &&
4796 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4801 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4802 if (mode == EX_TYPE_NORMAL ||
4803 mode == EX_TYPE_CENTER ||
4804 mode == EX_TYPE_CROSS)
4805 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4808 /* remove things displayed in background while burning dynamite */
4809 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4812 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4814 /* put moving element to center field (and let it explode there) */
4815 center_element = MovingOrBlocked2Element(ex, ey);
4816 RemoveMovingField(ex, ey);
4817 Feld[ex][ey] = center_element;
4820 /* now "center_element" is finally determined -- set related values now */
4821 artwork_element = center_element; /* for custom player artwork */
4822 explosion_element = center_element; /* for custom player artwork */
4824 if (IS_PLAYER(ex, ey))
4826 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4828 artwork_element = stored_player[player_nr].artwork_element;
4830 if (level.use_explosion_element[player_nr])
4832 explosion_element = level.explosion_element[player_nr];
4833 artwork_element = explosion_element;
4838 if (mode == EX_TYPE_NORMAL ||
4839 mode == EX_TYPE_CENTER ||
4840 mode == EX_TYPE_CROSS)
4841 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4844 last_phase = element_info[explosion_element].explosion_delay + 1;
4846 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4848 int xx = x - ex + 1;
4849 int yy = y - ey + 1;
4852 if (!IN_LEV_FIELD(x, y) ||
4853 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4854 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4857 element = Feld[x][y];
4859 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4861 element = MovingOrBlocked2Element(x, y);
4863 if (!IS_EXPLOSION_PROOF(element))
4864 RemoveMovingField(x, y);
4867 /* indestructible elements can only explode in center (but not flames) */
4868 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4869 mode == EX_TYPE_BORDER)) ||
4870 element == EL_FLAMES)
4873 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4874 behaviour, for example when touching a yamyam that explodes to rocks
4875 with active deadly shield, a rock is created under the player !!! */
4876 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4878 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4879 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4880 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4882 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4885 if (IS_ACTIVE_BOMB(element))
4887 /* re-activate things under the bomb like gate or penguin */
4888 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4895 /* save walkable background elements while explosion on same tile */
4896 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4897 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4898 Back[x][y] = element;
4900 /* ignite explodable elements reached by other explosion */
4901 if (element == EL_EXPLOSION)
4902 element = Store2[x][y];
4904 if (AmoebaNr[x][y] &&
4905 (element == EL_AMOEBA_FULL ||
4906 element == EL_BD_AMOEBA ||
4907 element == EL_AMOEBA_GROWING))
4909 AmoebaCnt[AmoebaNr[x][y]]--;
4910 AmoebaCnt2[AmoebaNr[x][y]]--;
4915 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4917 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4919 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4921 if (PLAYERINFO(ex, ey)->use_murphy)
4922 Store[x][y] = EL_EMPTY;
4925 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4926 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4927 else if (ELEM_IS_PLAYER(center_element))
4928 Store[x][y] = EL_EMPTY;
4929 else if (center_element == EL_YAMYAM)
4930 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4931 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4932 Store[x][y] = element_info[center_element].content.e[xx][yy];
4934 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4935 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4936 otherwise) -- FIX THIS !!! */
4937 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4938 Store[x][y] = element_info[element].content.e[1][1];
4940 else if (!CAN_EXPLODE(element))
4941 Store[x][y] = element_info[element].content.e[1][1];
4944 Store[x][y] = EL_EMPTY;
4946 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4947 center_element == EL_AMOEBA_TO_DIAMOND)
4948 Store2[x][y] = element;
4950 Feld[x][y] = EL_EXPLOSION;
4951 GfxElement[x][y] = artwork_element;
4953 ExplodePhase[x][y] = 1;
4954 ExplodeDelay[x][y] = last_phase;
4959 if (center_element == EL_YAMYAM)
4960 game.yamyam_content_nr =
4961 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4973 GfxFrame[x][y] = 0; /* restart explosion animation */
4975 last_phase = ExplodeDelay[x][y];
4977 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4981 /* activate this even in non-DEBUG version until cause for crash in
4982 getGraphicAnimationFrame() (see below) is found and eliminated */
4988 /* this can happen if the player leaves an explosion just in time */
4989 if (GfxElement[x][y] == EL_UNDEFINED)
4990 GfxElement[x][y] = EL_EMPTY;
4992 if (GfxElement[x][y] == EL_UNDEFINED)
4995 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4996 printf("Explode(): This should never happen!\n");
4999 GfxElement[x][y] = EL_EMPTY;
5005 border_element = Store2[x][y];
5006 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5007 border_element = StorePlayer[x][y];
5009 if (phase == element_info[border_element].ignition_delay ||
5010 phase == last_phase)
5012 boolean border_explosion = FALSE;
5014 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5015 !PLAYER_EXPLOSION_PROTECTED(x, y))
5017 KillPlayerUnlessExplosionProtected(x, y);
5018 border_explosion = TRUE;
5020 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5022 Feld[x][y] = Store2[x][y];
5025 border_explosion = TRUE;
5027 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5029 AmoebeUmwandeln(x, y);
5031 border_explosion = TRUE;
5034 /* if an element just explodes due to another explosion (chain-reaction),
5035 do not immediately end the new explosion when it was the last frame of
5036 the explosion (as it would be done in the following "if"-statement!) */
5037 if (border_explosion && phase == last_phase)
5041 if (phase == last_phase)
5045 element = Feld[x][y] = Store[x][y];
5046 Store[x][y] = Store2[x][y] = 0;
5047 GfxElement[x][y] = EL_UNDEFINED;
5049 /* player can escape from explosions and might therefore be still alive */
5050 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5051 element <= EL_PLAYER_IS_EXPLODING_4)
5053 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5054 int explosion_element = EL_PLAYER_1 + player_nr;
5055 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5056 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5058 if (level.use_explosion_element[player_nr])
5059 explosion_element = level.explosion_element[player_nr];
5061 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5062 element_info[explosion_element].content.e[xx][yy]);
5065 /* restore probably existing indestructible background element */
5066 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5067 element = Feld[x][y] = Back[x][y];
5070 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5071 GfxDir[x][y] = MV_NONE;
5072 ChangeDelay[x][y] = 0;
5073 ChangePage[x][y] = -1;
5075 #if USE_NEW_CUSTOM_VALUE
5076 CustomValue[x][y] = 0;
5079 InitField_WithBug2(x, y, FALSE);
5081 DrawLevelField(x, y);
5083 TestIfElementTouchesCustomElement(x, y);
5085 if (GFX_CRUMBLED(element))
5086 DrawLevelFieldCrumbledSandNeighbours(x, y);
5088 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5089 StorePlayer[x][y] = 0;
5091 if (ELEM_IS_PLAYER(element))
5092 RelocatePlayer(x, y, element);
5094 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5096 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5097 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5100 DrawLevelFieldCrumbledSand(x, y);
5102 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5104 DrawLevelElement(x, y, Back[x][y]);
5105 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5107 else if (IS_WALKABLE_UNDER(Back[x][y]))
5109 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5110 DrawLevelElementThruMask(x, y, Back[x][y]);
5112 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5113 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5117 void DynaExplode(int ex, int ey)
5120 int dynabomb_element = Feld[ex][ey];
5121 int dynabomb_size = 1;
5122 boolean dynabomb_xl = FALSE;
5123 struct PlayerInfo *player;
5124 static int xy[4][2] =
5132 if (IS_ACTIVE_BOMB(dynabomb_element))
5134 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5135 dynabomb_size = player->dynabomb_size;
5136 dynabomb_xl = player->dynabomb_xl;
5137 player->dynabombs_left++;
5140 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5142 for (i = 0; i < NUM_DIRECTIONS; i++)
5144 for (j = 1; j <= dynabomb_size; j++)
5146 int x = ex + j * xy[i][0];
5147 int y = ey + j * xy[i][1];
5150 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5153 element = Feld[x][y];
5155 /* do not restart explosions of fields with active bombs */
5156 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5159 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5161 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5162 !IS_DIGGABLE(element) && !dynabomb_xl)
5168 void Bang(int x, int y)
5170 int element = MovingOrBlocked2Element(x, y);
5171 int explosion_type = EX_TYPE_NORMAL;
5173 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5175 struct PlayerInfo *player = PLAYERINFO(x, y);
5177 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5178 player->element_nr);
5180 if (level.use_explosion_element[player->index_nr])
5182 int explosion_element = level.explosion_element[player->index_nr];
5184 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5185 explosion_type = EX_TYPE_CROSS;
5186 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5187 explosion_type = EX_TYPE_CENTER;
5195 case EL_BD_BUTTERFLY:
5198 case EL_DARK_YAMYAM:
5202 RaiseScoreElement(element);
5205 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5206 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5207 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5208 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5209 case EL_DYNABOMB_INCREASE_NUMBER:
5210 case EL_DYNABOMB_INCREASE_SIZE:
5211 case EL_DYNABOMB_INCREASE_POWER:
5212 explosion_type = EX_TYPE_DYNA;
5215 case EL_DC_LANDMINE:
5217 case EL_EM_EXIT_OPEN:
5218 case EL_EM_STEEL_EXIT_OPEN:
5220 explosion_type = EX_TYPE_CENTER;
5225 case EL_LAMP_ACTIVE:
5226 case EL_AMOEBA_TO_DIAMOND:
5227 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5228 explosion_type = EX_TYPE_CENTER;
5232 if (element_info[element].explosion_type == EXPLODES_CROSS)
5233 explosion_type = EX_TYPE_CROSS;
5234 else if (element_info[element].explosion_type == EXPLODES_1X1)
5235 explosion_type = EX_TYPE_CENTER;
5239 if (explosion_type == EX_TYPE_DYNA)
5242 Explode(x, y, EX_PHASE_START, explosion_type);
5244 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5247 void SplashAcid(int x, int y)
5249 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5250 (!IN_LEV_FIELD(x - 1, y - 2) ||
5251 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5252 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5254 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5255 (!IN_LEV_FIELD(x + 1, y - 2) ||
5256 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5257 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5259 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5262 static void InitBeltMovement()
5264 static int belt_base_element[4] =
5266 EL_CONVEYOR_BELT_1_LEFT,
5267 EL_CONVEYOR_BELT_2_LEFT,
5268 EL_CONVEYOR_BELT_3_LEFT,
5269 EL_CONVEYOR_BELT_4_LEFT
5271 static int belt_base_active_element[4] =
5273 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5274 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5275 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5276 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5281 /* set frame order for belt animation graphic according to belt direction */
5282 for (i = 0; i < NUM_BELTS; i++)
5286 for (j = 0; j < NUM_BELT_PARTS; j++)
5288 int element = belt_base_active_element[belt_nr] + j;
5289 int graphic = el2img(element);
5291 if (game.belt_dir[i] == MV_LEFT)
5292 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5294 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5298 SCAN_PLAYFIELD(x, y)
5300 int element = Feld[x][y];
5302 for (i = 0; i < NUM_BELTS; i++)
5304 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5306 int e_belt_nr = getBeltNrFromBeltElement(element);
5309 if (e_belt_nr == belt_nr)
5311 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5313 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5320 static void ToggleBeltSwitch(int x, int y)
5322 static int belt_base_element[4] =
5324 EL_CONVEYOR_BELT_1_LEFT,
5325 EL_CONVEYOR_BELT_2_LEFT,
5326 EL_CONVEYOR_BELT_3_LEFT,
5327 EL_CONVEYOR_BELT_4_LEFT
5329 static int belt_base_active_element[4] =
5331 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5332 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5333 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5334 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5336 static int belt_base_switch_element[4] =
5338 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5339 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5340 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5341 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5343 static int belt_move_dir[4] =
5351 int element = Feld[x][y];
5352 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5353 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5354 int belt_dir = belt_move_dir[belt_dir_nr];
5357 if (!IS_BELT_SWITCH(element))
5360 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5361 game.belt_dir[belt_nr] = belt_dir;
5363 if (belt_dir_nr == 3)
5366 /* set frame order for belt animation graphic according to belt direction */
5367 for (i = 0; i < NUM_BELT_PARTS; i++)
5369 int element = belt_base_active_element[belt_nr] + i;
5370 int graphic = el2img(element);
5372 if (belt_dir == MV_LEFT)
5373 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5375 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5378 SCAN_PLAYFIELD(xx, yy)
5380 int element = Feld[xx][yy];
5382 if (IS_BELT_SWITCH(element))
5384 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5386 if (e_belt_nr == belt_nr)
5388 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5389 DrawLevelField(xx, yy);
5392 else if (IS_BELT(element) && belt_dir != MV_NONE)
5394 int e_belt_nr = getBeltNrFromBeltElement(element);
5396 if (e_belt_nr == belt_nr)
5398 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5400 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5401 DrawLevelField(xx, yy);
5404 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5406 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5408 if (e_belt_nr == belt_nr)
5410 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5412 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5413 DrawLevelField(xx, yy);
5419 static void ToggleSwitchgateSwitch(int x, int y)
5423 game.switchgate_pos = !game.switchgate_pos;
5425 SCAN_PLAYFIELD(xx, yy)
5427 int element = Feld[xx][yy];
5429 #if !USE_BOTH_SWITCHGATE_SWITCHES
5430 if (element == EL_SWITCHGATE_SWITCH_UP ||
5431 element == EL_SWITCHGATE_SWITCH_DOWN)
5433 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5434 DrawLevelField(xx, yy);
5436 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5437 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5439 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5440 DrawLevelField(xx, yy);
5443 if (element == EL_SWITCHGATE_SWITCH_UP)
5445 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5446 DrawLevelField(xx, yy);
5448 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5450 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5451 DrawLevelField(xx, yy);
5453 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5455 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5456 DrawLevelField(xx, yy);
5458 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5460 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5461 DrawLevelField(xx, yy);
5464 else if (element == EL_SWITCHGATE_OPEN ||
5465 element == EL_SWITCHGATE_OPENING)
5467 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5469 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5471 else if (element == EL_SWITCHGATE_CLOSED ||
5472 element == EL_SWITCHGATE_CLOSING)
5474 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5476 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5481 static int getInvisibleActiveFromInvisibleElement(int element)
5483 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5484 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5485 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5489 static int getInvisibleFromInvisibleActiveElement(int element)
5491 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5492 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5493 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5497 static void RedrawAllLightSwitchesAndInvisibleElements()
5501 SCAN_PLAYFIELD(x, y)
5503 int element = Feld[x][y];
5505 if (element == EL_LIGHT_SWITCH &&
5506 game.light_time_left > 0)
5508 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5509 DrawLevelField(x, y);
5511 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5512 game.light_time_left == 0)
5514 Feld[x][y] = EL_LIGHT_SWITCH;
5515 DrawLevelField(x, y);
5517 else if (element == EL_EMC_DRIPPER &&
5518 game.light_time_left > 0)
5520 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5521 DrawLevelField(x, y);
5523 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5524 game.light_time_left == 0)
5526 Feld[x][y] = EL_EMC_DRIPPER;
5527 DrawLevelField(x, y);
5529 else if (element == EL_INVISIBLE_STEELWALL ||
5530 element == EL_INVISIBLE_WALL ||
5531 element == EL_INVISIBLE_SAND)
5533 if (game.light_time_left > 0)
5534 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5536 DrawLevelField(x, y);
5538 /* uncrumble neighbour fields, if needed */
5539 if (element == EL_INVISIBLE_SAND)
5540 DrawLevelFieldCrumbledSandNeighbours(x, y);
5542 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5543 element == EL_INVISIBLE_WALL_ACTIVE ||
5544 element == EL_INVISIBLE_SAND_ACTIVE)
5546 if (game.light_time_left == 0)
5547 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5549 DrawLevelField(x, y);
5551 /* re-crumble neighbour fields, if needed */
5552 if (element == EL_INVISIBLE_SAND)
5553 DrawLevelFieldCrumbledSandNeighbours(x, y);
5558 static void RedrawAllInvisibleElementsForLenses()
5562 SCAN_PLAYFIELD(x, y)
5564 int element = Feld[x][y];
5566 if (element == EL_EMC_DRIPPER &&
5567 game.lenses_time_left > 0)
5569 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5570 DrawLevelField(x, y);
5572 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5573 game.lenses_time_left == 0)
5575 Feld[x][y] = EL_EMC_DRIPPER;
5576 DrawLevelField(x, y);
5578 else if (element == EL_INVISIBLE_STEELWALL ||
5579 element == EL_INVISIBLE_WALL ||
5580 element == EL_INVISIBLE_SAND)
5582 if (game.lenses_time_left > 0)
5583 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5585 DrawLevelField(x, y);
5587 /* uncrumble neighbour fields, if needed */
5588 if (element == EL_INVISIBLE_SAND)
5589 DrawLevelFieldCrumbledSandNeighbours(x, y);
5591 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5592 element == EL_INVISIBLE_WALL_ACTIVE ||
5593 element == EL_INVISIBLE_SAND_ACTIVE)
5595 if (game.lenses_time_left == 0)
5596 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5598 DrawLevelField(x, y);
5600 /* re-crumble neighbour fields, if needed */
5601 if (element == EL_INVISIBLE_SAND)
5602 DrawLevelFieldCrumbledSandNeighbours(x, y);
5607 static void RedrawAllInvisibleElementsForMagnifier()
5611 SCAN_PLAYFIELD(x, y)
5613 int element = Feld[x][y];
5615 if (element == EL_EMC_FAKE_GRASS &&
5616 game.magnify_time_left > 0)
5618 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5619 DrawLevelField(x, y);
5621 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5622 game.magnify_time_left == 0)
5624 Feld[x][y] = EL_EMC_FAKE_GRASS;
5625 DrawLevelField(x, y);
5627 else if (IS_GATE_GRAY(element) &&
5628 game.magnify_time_left > 0)
5630 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5631 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5632 IS_EM_GATE_GRAY(element) ?
5633 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5634 IS_EMC_GATE_GRAY(element) ?
5635 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5637 DrawLevelField(x, y);
5639 else if (IS_GATE_GRAY_ACTIVE(element) &&
5640 game.magnify_time_left == 0)
5642 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5643 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5644 IS_EM_GATE_GRAY_ACTIVE(element) ?
5645 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5646 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5647 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5649 DrawLevelField(x, y);
5654 static void ToggleLightSwitch(int x, int y)
5656 int element = Feld[x][y];
5658 game.light_time_left =
5659 (element == EL_LIGHT_SWITCH ?
5660 level.time_light * FRAMES_PER_SECOND : 0);
5662 RedrawAllLightSwitchesAndInvisibleElements();
5665 static void ActivateTimegateSwitch(int x, int y)
5669 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5671 SCAN_PLAYFIELD(xx, yy)
5673 int element = Feld[xx][yy];
5675 if (element == EL_TIMEGATE_CLOSED ||
5676 element == EL_TIMEGATE_CLOSING)
5678 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5679 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5683 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5685 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5686 DrawLevelField(xx, yy);
5693 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5694 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5696 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5700 void Impact(int x, int y)
5702 boolean last_line = (y == lev_fieldy - 1);
5703 boolean object_hit = FALSE;
5704 boolean impact = (last_line || object_hit);
5705 int element = Feld[x][y];
5706 int smashed = EL_STEELWALL;
5708 if (!last_line) /* check if element below was hit */
5710 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5713 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5714 MovDir[x][y + 1] != MV_DOWN ||
5715 MovPos[x][y + 1] <= TILEY / 2));
5717 /* do not smash moving elements that left the smashed field in time */
5718 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5719 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5722 #if USE_QUICKSAND_IMPACT_BUGFIX
5723 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5725 RemoveMovingField(x, y + 1);
5726 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5727 Feld[x][y + 2] = EL_ROCK;
5728 DrawLevelField(x, y + 2);
5733 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5735 RemoveMovingField(x, y + 1);
5736 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5737 Feld[x][y + 2] = EL_ROCK;
5738 DrawLevelField(x, y + 2);
5745 smashed = MovingOrBlocked2Element(x, y + 1);
5747 impact = (last_line || object_hit);
5750 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5752 SplashAcid(x, y + 1);
5756 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5757 /* only reset graphic animation if graphic really changes after impact */
5759 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5761 ResetGfxAnimation(x, y);
5762 DrawLevelField(x, y);
5765 if (impact && CAN_EXPLODE_IMPACT(element))
5770 else if (impact && element == EL_PEARL &&
5771 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5773 ResetGfxAnimation(x, y);
5775 Feld[x][y] = EL_PEARL_BREAKING;
5776 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5779 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5781 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5786 if (impact && element == EL_AMOEBA_DROP)
5788 if (object_hit && IS_PLAYER(x, y + 1))
5789 KillPlayerUnlessEnemyProtected(x, y + 1);
5790 else if (object_hit && smashed == EL_PENGUIN)
5794 Feld[x][y] = EL_AMOEBA_GROWING;
5795 Store[x][y] = EL_AMOEBA_WET;
5797 ResetRandomAnimationValue(x, y);
5802 if (object_hit) /* check which object was hit */
5804 if ((CAN_PASS_MAGIC_WALL(element) &&
5805 (smashed == EL_MAGIC_WALL ||
5806 smashed == EL_BD_MAGIC_WALL)) ||
5807 (CAN_PASS_DC_MAGIC_WALL(element) &&
5808 smashed == EL_DC_MAGIC_WALL))
5811 int activated_magic_wall =
5812 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5813 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5814 EL_DC_MAGIC_WALL_ACTIVE);
5816 /* activate magic wall / mill */
5817 SCAN_PLAYFIELD(xx, yy)
5819 if (Feld[xx][yy] == smashed)
5820 Feld[xx][yy] = activated_magic_wall;
5823 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5824 game.magic_wall_active = TRUE;
5826 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5827 SND_MAGIC_WALL_ACTIVATING :
5828 smashed == EL_BD_MAGIC_WALL ?
5829 SND_BD_MAGIC_WALL_ACTIVATING :
5830 SND_DC_MAGIC_WALL_ACTIVATING));
5833 if (IS_PLAYER(x, y + 1))
5835 if (CAN_SMASH_PLAYER(element))
5837 KillPlayerUnlessEnemyProtected(x, y + 1);
5841 else if (smashed == EL_PENGUIN)
5843 if (CAN_SMASH_PLAYER(element))
5849 else if (element == EL_BD_DIAMOND)
5851 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5857 else if (((element == EL_SP_INFOTRON ||
5858 element == EL_SP_ZONK) &&
5859 (smashed == EL_SP_SNIKSNAK ||
5860 smashed == EL_SP_ELECTRON ||
5861 smashed == EL_SP_DISK_ORANGE)) ||
5862 (element == EL_SP_INFOTRON &&
5863 smashed == EL_SP_DISK_YELLOW))
5868 else if (CAN_SMASH_EVERYTHING(element))
5870 if (IS_CLASSIC_ENEMY(smashed) ||
5871 CAN_EXPLODE_SMASHED(smashed))
5876 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5878 if (smashed == EL_LAMP ||
5879 smashed == EL_LAMP_ACTIVE)
5884 else if (smashed == EL_NUT)
5886 Feld[x][y + 1] = EL_NUT_BREAKING;
5887 PlayLevelSound(x, y, SND_NUT_BREAKING);
5888 RaiseScoreElement(EL_NUT);
5891 else if (smashed == EL_PEARL)
5893 ResetGfxAnimation(x, y);
5895 Feld[x][y + 1] = EL_PEARL_BREAKING;
5896 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5899 else if (smashed == EL_DIAMOND)
5901 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5902 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5905 else if (IS_BELT_SWITCH(smashed))
5907 ToggleBeltSwitch(x, y + 1);
5909 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5910 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5911 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5912 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5914 ToggleSwitchgateSwitch(x, y + 1);
5916 else if (smashed == EL_LIGHT_SWITCH ||
5917 smashed == EL_LIGHT_SWITCH_ACTIVE)
5919 ToggleLightSwitch(x, y + 1);
5924 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5927 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5929 CheckElementChangeBySide(x, y + 1, smashed, element,
5930 CE_SWITCHED, CH_SIDE_TOP);
5931 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5937 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5942 /* play sound of magic wall / mill */
5944 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5945 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5946 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5948 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5949 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5950 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5951 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5952 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5953 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5958 /* play sound of object that hits the ground */
5959 if (last_line || object_hit)
5960 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5963 inline static void TurnRoundExt(int x, int y)
5975 { 0, 0 }, { 0, 0 }, { 0, 0 },
5980 int left, right, back;
5984 { MV_DOWN, MV_UP, MV_RIGHT },
5985 { MV_UP, MV_DOWN, MV_LEFT },
5987 { MV_LEFT, MV_RIGHT, MV_DOWN },
5991 { MV_RIGHT, MV_LEFT, MV_UP }
5994 int element = Feld[x][y];
5995 int move_pattern = element_info[element].move_pattern;
5997 int old_move_dir = MovDir[x][y];
5998 int left_dir = turn[old_move_dir].left;
5999 int right_dir = turn[old_move_dir].right;
6000 int back_dir = turn[old_move_dir].back;
6002 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6003 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6004 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6005 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6007 int left_x = x + left_dx, left_y = y + left_dy;
6008 int right_x = x + right_dx, right_y = y + right_dy;
6009 int move_x = x + move_dx, move_y = y + move_dy;
6013 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6015 TestIfBadThingTouchesOtherBadThing(x, y);
6017 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6018 MovDir[x][y] = right_dir;
6019 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6020 MovDir[x][y] = left_dir;
6022 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6024 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6027 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6029 TestIfBadThingTouchesOtherBadThing(x, y);
6031 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6032 MovDir[x][y] = left_dir;
6033 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6034 MovDir[x][y] = right_dir;
6036 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6038 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6041 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6043 TestIfBadThingTouchesOtherBadThing(x, y);
6045 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6046 MovDir[x][y] = left_dir;
6047 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6048 MovDir[x][y] = right_dir;
6050 if (MovDir[x][y] != old_move_dir)
6053 else if (element == EL_YAMYAM)
6055 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6056 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6058 if (can_turn_left && can_turn_right)
6059 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6060 else if (can_turn_left)
6061 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6062 else if (can_turn_right)
6063 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6065 MovDir[x][y] = back_dir;
6067 MovDelay[x][y] = 16 + 16 * RND(3);
6069 else if (element == EL_DARK_YAMYAM)
6071 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6073 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6076 if (can_turn_left && can_turn_right)
6077 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6078 else if (can_turn_left)
6079 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6080 else if (can_turn_right)
6081 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6083 MovDir[x][y] = back_dir;
6085 MovDelay[x][y] = 16 + 16 * RND(3);
6087 else if (element == EL_PACMAN)
6089 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6090 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6092 if (can_turn_left && can_turn_right)
6093 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6094 else if (can_turn_left)
6095 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6096 else if (can_turn_right)
6097 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6099 MovDir[x][y] = back_dir;
6101 MovDelay[x][y] = 6 + RND(40);
6103 else if (element == EL_PIG)
6105 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6106 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6107 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6108 boolean should_turn_left, should_turn_right, should_move_on;
6110 int rnd = RND(rnd_value);
6112 should_turn_left = (can_turn_left &&
6114 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6115 y + back_dy + left_dy)));
6116 should_turn_right = (can_turn_right &&
6118 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6119 y + back_dy + right_dy)));
6120 should_move_on = (can_move_on &&
6123 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6124 y + move_dy + left_dy) ||
6125 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6126 y + move_dy + right_dy)));
6128 if (should_turn_left || should_turn_right || should_move_on)
6130 if (should_turn_left && should_turn_right && should_move_on)
6131 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6132 rnd < 2 * rnd_value / 3 ? right_dir :
6134 else if (should_turn_left && should_turn_right)
6135 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6136 else if (should_turn_left && should_move_on)
6137 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6138 else if (should_turn_right && should_move_on)
6139 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6140 else if (should_turn_left)
6141 MovDir[x][y] = left_dir;
6142 else if (should_turn_right)
6143 MovDir[x][y] = right_dir;
6144 else if (should_move_on)
6145 MovDir[x][y] = old_move_dir;
6147 else if (can_move_on && rnd > rnd_value / 8)
6148 MovDir[x][y] = old_move_dir;
6149 else if (can_turn_left && can_turn_right)
6150 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6151 else if (can_turn_left && rnd > rnd_value / 8)
6152 MovDir[x][y] = left_dir;
6153 else if (can_turn_right && rnd > rnd_value/8)
6154 MovDir[x][y] = right_dir;
6156 MovDir[x][y] = back_dir;
6158 xx = x + move_xy[MovDir[x][y]].dx;
6159 yy = y + move_xy[MovDir[x][y]].dy;
6161 if (!IN_LEV_FIELD(xx, yy) ||
6162 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6163 MovDir[x][y] = old_move_dir;
6167 else if (element == EL_DRAGON)
6169 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6170 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6171 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6173 int rnd = RND(rnd_value);
6175 if (can_move_on && rnd > rnd_value / 8)
6176 MovDir[x][y] = old_move_dir;
6177 else if (can_turn_left && can_turn_right)
6178 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6179 else if (can_turn_left && rnd > rnd_value / 8)
6180 MovDir[x][y] = left_dir;
6181 else if (can_turn_right && rnd > rnd_value / 8)
6182 MovDir[x][y] = right_dir;
6184 MovDir[x][y] = back_dir;
6186 xx = x + move_xy[MovDir[x][y]].dx;
6187 yy = y + move_xy[MovDir[x][y]].dy;
6189 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6190 MovDir[x][y] = old_move_dir;
6194 else if (element == EL_MOLE)
6196 boolean can_move_on =
6197 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6198 IS_AMOEBOID(Feld[move_x][move_y]) ||
6199 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6202 boolean can_turn_left =
6203 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6204 IS_AMOEBOID(Feld[left_x][left_y])));
6206 boolean can_turn_right =
6207 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6208 IS_AMOEBOID(Feld[right_x][right_y])));
6210 if (can_turn_left && can_turn_right)
6211 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6212 else if (can_turn_left)
6213 MovDir[x][y] = left_dir;
6215 MovDir[x][y] = right_dir;
6218 if (MovDir[x][y] != old_move_dir)
6221 else if (element == EL_BALLOON)
6223 MovDir[x][y] = game.wind_direction;
6226 else if (element == EL_SPRING)
6228 #if USE_NEW_SPRING_BUMPER
6229 if (MovDir[x][y] & MV_HORIZONTAL)
6231 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6232 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6234 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6235 ResetGfxAnimation(move_x, move_y);
6236 DrawLevelField(move_x, move_y);
6238 MovDir[x][y] = back_dir;
6240 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6241 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6242 MovDir[x][y] = MV_NONE;
6245 if (MovDir[x][y] & MV_HORIZONTAL &&
6246 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6247 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6248 MovDir[x][y] = MV_NONE;
6253 else if (element == EL_ROBOT ||
6254 element == EL_SATELLITE ||
6255 element == EL_PENGUIN ||
6256 element == EL_EMC_ANDROID)
6258 int attr_x = -1, attr_y = -1;
6269 for (i = 0; i < MAX_PLAYERS; i++)
6271 struct PlayerInfo *player = &stored_player[i];
6272 int jx = player->jx, jy = player->jy;
6274 if (!player->active)
6278 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6286 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6287 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6288 game.engine_version < VERSION_IDENT(3,1,0,0)))
6294 if (element == EL_PENGUIN)
6297 static int xy[4][2] =
6305 for (i = 0; i < NUM_DIRECTIONS; i++)
6307 int ex = x + xy[i][0];
6308 int ey = y + xy[i][1];
6310 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6311 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6312 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6313 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6322 MovDir[x][y] = MV_NONE;
6324 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6325 else if (attr_x > x)
6326 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6328 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6329 else if (attr_y > y)
6330 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6332 if (element == EL_ROBOT)
6336 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6337 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6338 Moving2Blocked(x, y, &newx, &newy);
6340 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6341 MovDelay[x][y] = 8 + 8 * !RND(3);
6343 MovDelay[x][y] = 16;
6345 else if (element == EL_PENGUIN)
6351 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6353 boolean first_horiz = RND(2);
6354 int new_move_dir = MovDir[x][y];
6357 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6358 Moving2Blocked(x, y, &newx, &newy);
6360 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6364 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6365 Moving2Blocked(x, y, &newx, &newy);
6367 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6370 MovDir[x][y] = old_move_dir;
6374 else if (element == EL_SATELLITE)
6380 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6382 boolean first_horiz = RND(2);
6383 int new_move_dir = MovDir[x][y];
6386 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6387 Moving2Blocked(x, y, &newx, &newy);
6389 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6393 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6394 Moving2Blocked(x, y, &newx, &newy);
6396 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6399 MovDir[x][y] = old_move_dir;
6403 else if (element == EL_EMC_ANDROID)
6405 static int check_pos[16] =
6407 -1, /* 0 => (invalid) */
6408 7, /* 1 => MV_LEFT */
6409 3, /* 2 => MV_RIGHT */
6410 -1, /* 3 => (invalid) */
6412 0, /* 5 => MV_LEFT | MV_UP */
6413 2, /* 6 => MV_RIGHT | MV_UP */
6414 -1, /* 7 => (invalid) */
6415 5, /* 8 => MV_DOWN */
6416 6, /* 9 => MV_LEFT | MV_DOWN */
6417 4, /* 10 => MV_RIGHT | MV_DOWN */
6418 -1, /* 11 => (invalid) */
6419 -1, /* 12 => (invalid) */
6420 -1, /* 13 => (invalid) */
6421 -1, /* 14 => (invalid) */
6422 -1, /* 15 => (invalid) */
6430 { -1, -1, MV_LEFT | MV_UP },
6432 { +1, -1, MV_RIGHT | MV_UP },
6433 { +1, 0, MV_RIGHT },
6434 { +1, +1, MV_RIGHT | MV_DOWN },
6436 { -1, +1, MV_LEFT | MV_DOWN },
6439 int start_pos, check_order;
6440 boolean can_clone = FALSE;
6443 /* check if there is any free field around current position */
6444 for (i = 0; i < 8; i++)
6446 int newx = x + check_xy[i].dx;
6447 int newy = y + check_xy[i].dy;
6449 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6457 if (can_clone) /* randomly find an element to clone */
6461 start_pos = check_pos[RND(8)];
6462 check_order = (RND(2) ? -1 : +1);
6464 for (i = 0; i < 8; i++)
6466 int pos_raw = start_pos + i * check_order;
6467 int pos = (pos_raw + 8) % 8;
6468 int newx = x + check_xy[pos].dx;
6469 int newy = y + check_xy[pos].dy;
6471 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6473 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6474 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6476 Store[x][y] = Feld[newx][newy];
6485 if (can_clone) /* randomly find a direction to move */
6489 start_pos = check_pos[RND(8)];
6490 check_order = (RND(2) ? -1 : +1);
6492 for (i = 0; i < 8; i++)
6494 int pos_raw = start_pos + i * check_order;
6495 int pos = (pos_raw + 8) % 8;
6496 int newx = x + check_xy[pos].dx;
6497 int newy = y + check_xy[pos].dy;
6498 int new_move_dir = check_xy[pos].dir;
6500 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6502 MovDir[x][y] = new_move_dir;
6503 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6512 if (can_clone) /* cloning and moving successful */
6515 /* cannot clone -- try to move towards player */
6517 start_pos = check_pos[MovDir[x][y] & 0x0f];
6518 check_order = (RND(2) ? -1 : +1);
6520 for (i = 0; i < 3; i++)
6522 /* first check start_pos, then previous/next or (next/previous) pos */
6523 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6524 int pos = (pos_raw + 8) % 8;
6525 int newx = x + check_xy[pos].dx;
6526 int newy = y + check_xy[pos].dy;
6527 int new_move_dir = check_xy[pos].dir;
6529 if (IS_PLAYER(newx, newy))
6532 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6534 MovDir[x][y] = new_move_dir;
6535 MovDelay[x][y] = level.android_move_time * 8 + 1;
6542 else if (move_pattern == MV_TURNING_LEFT ||
6543 move_pattern == MV_TURNING_RIGHT ||
6544 move_pattern == MV_TURNING_LEFT_RIGHT ||
6545 move_pattern == MV_TURNING_RIGHT_LEFT ||
6546 move_pattern == MV_TURNING_RANDOM ||
6547 move_pattern == MV_ALL_DIRECTIONS)
6549 boolean can_turn_left =
6550 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6551 boolean can_turn_right =
6552 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6554 if (element_info[element].move_stepsize == 0) /* "not moving" */
6557 if (move_pattern == MV_TURNING_LEFT)
6558 MovDir[x][y] = left_dir;
6559 else if (move_pattern == MV_TURNING_RIGHT)
6560 MovDir[x][y] = right_dir;
6561 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6562 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6563 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6564 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6565 else if (move_pattern == MV_TURNING_RANDOM)
6566 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6567 can_turn_right && !can_turn_left ? right_dir :
6568 RND(2) ? left_dir : right_dir);
6569 else if (can_turn_left && can_turn_right)
6570 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6571 else if (can_turn_left)
6572 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6573 else if (can_turn_right)
6574 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6576 MovDir[x][y] = back_dir;
6578 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6580 else if (move_pattern == MV_HORIZONTAL ||
6581 move_pattern == MV_VERTICAL)
6583 if (move_pattern & old_move_dir)
6584 MovDir[x][y] = back_dir;
6585 else if (move_pattern == MV_HORIZONTAL)
6586 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6587 else if (move_pattern == MV_VERTICAL)
6588 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6590 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6592 else if (move_pattern & MV_ANY_DIRECTION)
6594 MovDir[x][y] = move_pattern;
6595 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6597 else if (move_pattern & MV_WIND_DIRECTION)
6599 MovDir[x][y] = game.wind_direction;
6600 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6602 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6604 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6605 MovDir[x][y] = left_dir;
6606 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6607 MovDir[x][y] = right_dir;
6609 if (MovDir[x][y] != old_move_dir)
6610 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6612 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6614 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6615 MovDir[x][y] = right_dir;
6616 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6617 MovDir[x][y] = left_dir;
6619 if (MovDir[x][y] != old_move_dir)
6620 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6622 else if (move_pattern == MV_TOWARDS_PLAYER ||
6623 move_pattern == MV_AWAY_FROM_PLAYER)
6625 int attr_x = -1, attr_y = -1;
6627 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6638 for (i = 0; i < MAX_PLAYERS; i++)
6640 struct PlayerInfo *player = &stored_player[i];
6641 int jx = player->jx, jy = player->jy;
6643 if (!player->active)
6647 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6655 MovDir[x][y] = MV_NONE;
6657 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6658 else if (attr_x > x)
6659 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6661 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6662 else if (attr_y > y)
6663 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6665 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6667 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6669 boolean first_horiz = RND(2);
6670 int new_move_dir = MovDir[x][y];
6672 if (element_info[element].move_stepsize == 0) /* "not moving" */
6674 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6675 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6681 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6682 Moving2Blocked(x, y, &newx, &newy);
6684 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6688 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6689 Moving2Blocked(x, y, &newx, &newy);
6691 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6694 MovDir[x][y] = old_move_dir;
6697 else if (move_pattern == MV_WHEN_PUSHED ||
6698 move_pattern == MV_WHEN_DROPPED)
6700 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6701 MovDir[x][y] = MV_NONE;
6705 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6707 static int test_xy[7][2] =
6717 static int test_dir[7] =
6727 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6728 int move_preference = -1000000; /* start with very low preference */
6729 int new_move_dir = MV_NONE;
6730 int start_test = RND(4);
6733 for (i = 0; i < NUM_DIRECTIONS; i++)
6735 int move_dir = test_dir[start_test + i];
6736 int move_dir_preference;
6738 xx = x + test_xy[start_test + i][0];
6739 yy = y + test_xy[start_test + i][1];
6741 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6742 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6744 new_move_dir = move_dir;
6749 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6752 move_dir_preference = -1 * RunnerVisit[xx][yy];
6753 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6754 move_dir_preference = PlayerVisit[xx][yy];
6756 if (move_dir_preference > move_preference)
6758 /* prefer field that has not been visited for the longest time */
6759 move_preference = move_dir_preference;
6760 new_move_dir = move_dir;
6762 else if (move_dir_preference == move_preference &&
6763 move_dir == old_move_dir)
6765 /* prefer last direction when all directions are preferred equally */
6766 move_preference = move_dir_preference;
6767 new_move_dir = move_dir;
6771 MovDir[x][y] = new_move_dir;
6772 if (old_move_dir != new_move_dir)
6773 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6777 static void TurnRound(int x, int y)
6779 int direction = MovDir[x][y];
6783 GfxDir[x][y] = MovDir[x][y];
6785 if (direction != MovDir[x][y])
6789 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6791 ResetGfxFrame(x, y, FALSE);
6794 static boolean JustBeingPushed(int x, int y)
6798 for (i = 0; i < MAX_PLAYERS; i++)
6800 struct PlayerInfo *player = &stored_player[i];
6802 if (player->active && player->is_pushing && player->MovPos)
6804 int next_jx = player->jx + (player->jx - player->last_jx);
6805 int next_jy = player->jy + (player->jy - player->last_jy);
6807 if (x == next_jx && y == next_jy)
6815 void StartMoving(int x, int y)
6817 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6818 int element = Feld[x][y];
6823 if (MovDelay[x][y] == 0)
6824 GfxAction[x][y] = ACTION_DEFAULT;
6826 if (CAN_FALL(element) && y < lev_fieldy - 1)
6828 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6829 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6830 if (JustBeingPushed(x, y))
6833 if (element == EL_QUICKSAND_FULL)
6835 if (IS_FREE(x, y + 1))
6837 InitMovingField(x, y, MV_DOWN);
6838 started_moving = TRUE;
6840 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6841 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6842 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6843 Store[x][y] = EL_ROCK;
6845 Store[x][y] = EL_ROCK;
6848 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6850 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6852 if (!MovDelay[x][y])
6853 MovDelay[x][y] = TILEY + 1;
6862 Feld[x][y] = EL_QUICKSAND_EMPTY;
6863 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6864 Store[x][y + 1] = Store[x][y];
6867 PlayLevelSoundAction(x, y, ACTION_FILLING);
6870 else if (element == EL_QUICKSAND_FAST_FULL)
6872 if (IS_FREE(x, y + 1))
6874 InitMovingField(x, y, MV_DOWN);
6875 started_moving = TRUE;
6877 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6878 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6879 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6880 Store[x][y] = EL_ROCK;
6882 Store[x][y] = EL_ROCK;
6885 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6887 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6889 if (!MovDelay[x][y])
6890 MovDelay[x][y] = TILEY + 1;
6899 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6900 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6901 Store[x][y + 1] = Store[x][y];
6904 PlayLevelSoundAction(x, y, ACTION_FILLING);
6907 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6908 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6910 InitMovingField(x, y, MV_DOWN);
6911 started_moving = TRUE;
6913 Feld[x][y] = EL_QUICKSAND_FILLING;
6914 Store[x][y] = element;
6916 PlayLevelSoundAction(x, y, ACTION_FILLING);
6918 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6919 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6921 InitMovingField(x, y, MV_DOWN);
6922 started_moving = TRUE;
6924 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6925 Store[x][y] = element;
6927 PlayLevelSoundAction(x, y, ACTION_FILLING);
6929 else if (element == EL_MAGIC_WALL_FULL)
6931 if (IS_FREE(x, y + 1))
6933 InitMovingField(x, y, MV_DOWN);
6934 started_moving = TRUE;
6936 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6937 Store[x][y] = EL_CHANGED(Store[x][y]);
6939 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6941 if (!MovDelay[x][y])
6942 MovDelay[x][y] = TILEY/4 + 1;
6951 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6952 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6953 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6957 else if (element == EL_BD_MAGIC_WALL_FULL)
6959 if (IS_FREE(x, y + 1))
6961 InitMovingField(x, y, MV_DOWN);
6962 started_moving = TRUE;
6964 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6965 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6967 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6969 if (!MovDelay[x][y])
6970 MovDelay[x][y] = TILEY/4 + 1;
6979 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6980 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6981 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6985 else if (element == EL_DC_MAGIC_WALL_FULL)
6987 if (IS_FREE(x, y + 1))
6989 InitMovingField(x, y, MV_DOWN);
6990 started_moving = TRUE;
6992 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6993 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6995 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6997 if (!MovDelay[x][y])
6998 MovDelay[x][y] = TILEY/4 + 1;
7007 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7008 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7009 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7013 else if ((CAN_PASS_MAGIC_WALL(element) &&
7014 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7015 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7016 (CAN_PASS_DC_MAGIC_WALL(element) &&
7017 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7020 InitMovingField(x, y, MV_DOWN);
7021 started_moving = TRUE;
7024 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7025 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7026 EL_DC_MAGIC_WALL_FILLING);
7027 Store[x][y] = element;
7029 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7031 SplashAcid(x, y + 1);
7033 InitMovingField(x, y, MV_DOWN);
7034 started_moving = TRUE;
7036 Store[x][y] = EL_ACID;
7039 #if USE_FIX_IMPACT_COLLISION
7040 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7041 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7043 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7044 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7046 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7047 CAN_FALL(element) && WasJustFalling[x][y] &&
7048 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7050 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7051 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7052 (Feld[x][y + 1] == EL_BLOCKED)))
7054 /* this is needed for a special case not covered by calling "Impact()"
7055 from "ContinueMoving()": if an element moves to a tile directly below
7056 another element which was just falling on that tile (which was empty
7057 in the previous frame), the falling element above would just stop
7058 instead of smashing the element below (in previous version, the above
7059 element was just checked for "moving" instead of "falling", resulting
7060 in incorrect smashes caused by horizontal movement of the above
7061 element; also, the case of the player being the element to smash was
7062 simply not covered here... :-/ ) */
7064 CheckCollision[x][y] = 0;
7065 CheckImpact[x][y] = 0;
7069 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7071 if (MovDir[x][y] == MV_NONE)
7073 InitMovingField(x, y, MV_DOWN);
7074 started_moving = TRUE;
7077 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7079 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7080 MovDir[x][y] = MV_DOWN;
7082 InitMovingField(x, y, MV_DOWN);
7083 started_moving = TRUE;
7085 else if (element == EL_AMOEBA_DROP)
7087 Feld[x][y] = EL_AMOEBA_GROWING;
7088 Store[x][y] = EL_AMOEBA_WET;
7090 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7091 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7092 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7093 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7095 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7096 (IS_FREE(x - 1, y + 1) ||
7097 Feld[x - 1][y + 1] == EL_ACID));
7098 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7099 (IS_FREE(x + 1, y + 1) ||
7100 Feld[x + 1][y + 1] == EL_ACID));
7101 boolean can_fall_any = (can_fall_left || can_fall_right);
7102 boolean can_fall_both = (can_fall_left && can_fall_right);
7103 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7105 #if USE_NEW_ALL_SLIPPERY
7106 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7108 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7109 can_fall_right = FALSE;
7110 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7111 can_fall_left = FALSE;
7112 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7113 can_fall_right = FALSE;
7114 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7115 can_fall_left = FALSE;
7117 can_fall_any = (can_fall_left || can_fall_right);
7118 can_fall_both = FALSE;
7121 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7123 if (slippery_type == SLIPPERY_ONLY_LEFT)
7124 can_fall_right = FALSE;
7125 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7126 can_fall_left = FALSE;
7127 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7128 can_fall_right = FALSE;
7129 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7130 can_fall_left = FALSE;
7132 can_fall_any = (can_fall_left || can_fall_right);
7133 can_fall_both = (can_fall_left && can_fall_right);
7137 #if USE_NEW_ALL_SLIPPERY
7139 #if USE_NEW_SP_SLIPPERY
7140 /* !!! better use the same properties as for custom elements here !!! */
7141 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7142 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7144 can_fall_right = FALSE; /* slip down on left side */
7145 can_fall_both = FALSE;
7150 #if USE_NEW_ALL_SLIPPERY
7153 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7154 can_fall_right = FALSE; /* slip down on left side */
7156 can_fall_left = !(can_fall_right = RND(2));
7158 can_fall_both = FALSE;
7163 if (game.emulation == EMU_BOULDERDASH ||
7164 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7165 can_fall_right = FALSE; /* slip down on left side */
7167 can_fall_left = !(can_fall_right = RND(2));
7169 can_fall_both = FALSE;
7175 /* if not determined otherwise, prefer left side for slipping down */
7176 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7177 started_moving = TRUE;
7181 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7183 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7186 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7187 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7188 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7189 int belt_dir = game.belt_dir[belt_nr];
7191 if ((belt_dir == MV_LEFT && left_is_free) ||
7192 (belt_dir == MV_RIGHT && right_is_free))
7194 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7196 InitMovingField(x, y, belt_dir);
7197 started_moving = TRUE;
7199 Pushed[x][y] = TRUE;
7200 Pushed[nextx][y] = TRUE;
7202 GfxAction[x][y] = ACTION_DEFAULT;
7206 MovDir[x][y] = 0; /* if element was moving, stop it */
7211 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7213 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7215 if (CAN_MOVE(element) && !started_moving)
7218 int move_pattern = element_info[element].move_pattern;
7223 if (MovDir[x][y] == MV_NONE)
7225 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7226 x, y, element, element_info[element].token_name);
7227 printf("StartMoving(): This should never happen!\n");
7232 Moving2Blocked(x, y, &newx, &newy);
7234 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7237 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7238 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7240 WasJustMoving[x][y] = 0;
7241 CheckCollision[x][y] = 0;
7243 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7245 if (Feld[x][y] != element) /* element has changed */
7249 if (!MovDelay[x][y]) /* start new movement phase */
7251 /* all objects that can change their move direction after each step
7252 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7254 if (element != EL_YAMYAM &&
7255 element != EL_DARK_YAMYAM &&
7256 element != EL_PACMAN &&
7257 !(move_pattern & MV_ANY_DIRECTION) &&
7258 move_pattern != MV_TURNING_LEFT &&
7259 move_pattern != MV_TURNING_RIGHT &&
7260 move_pattern != MV_TURNING_LEFT_RIGHT &&
7261 move_pattern != MV_TURNING_RIGHT_LEFT &&
7262 move_pattern != MV_TURNING_RANDOM)
7266 if (MovDelay[x][y] && (element == EL_BUG ||
7267 element == EL_SPACESHIP ||
7268 element == EL_SP_SNIKSNAK ||
7269 element == EL_SP_ELECTRON ||
7270 element == EL_MOLE))
7271 DrawLevelField(x, y);
7275 if (MovDelay[x][y]) /* wait some time before next movement */
7279 if (element == EL_ROBOT ||
7280 element == EL_YAMYAM ||
7281 element == EL_DARK_YAMYAM)
7283 DrawLevelElementAnimationIfNeeded(x, y, element);
7284 PlayLevelSoundAction(x, y, ACTION_WAITING);
7286 else if (element == EL_SP_ELECTRON)
7287 DrawLevelElementAnimationIfNeeded(x, y, element);
7288 else if (element == EL_DRAGON)
7291 int dir = MovDir[x][y];
7292 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7293 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7294 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7295 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7296 dir == MV_UP ? IMG_FLAMES_1_UP :
7297 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7298 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7300 GfxAction[x][y] = ACTION_ATTACKING;
7302 if (IS_PLAYER(x, y))
7303 DrawPlayerField(x, y);
7305 DrawLevelField(x, y);
7307 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7309 for (i = 1; i <= 3; i++)
7311 int xx = x + i * dx;
7312 int yy = y + i * dy;
7313 int sx = SCREENX(xx);
7314 int sy = SCREENY(yy);
7315 int flame_graphic = graphic + (i - 1);
7317 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7322 int flamed = MovingOrBlocked2Element(xx, yy);
7326 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7328 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7329 RemoveMovingField(xx, yy);
7331 RemoveField(xx, yy);
7333 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7336 RemoveMovingField(xx, yy);
7339 ChangeDelay[xx][yy] = 0;
7341 Feld[xx][yy] = EL_FLAMES;
7343 if (IN_SCR_FIELD(sx, sy))
7345 DrawLevelFieldCrumbledSand(xx, yy);
7346 DrawGraphic(sx, sy, flame_graphic, frame);
7351 if (Feld[xx][yy] == EL_FLAMES)
7352 Feld[xx][yy] = EL_EMPTY;
7353 DrawLevelField(xx, yy);
7358 if (MovDelay[x][y]) /* element still has to wait some time */
7360 PlayLevelSoundAction(x, y, ACTION_WAITING);
7366 /* now make next step */
7368 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7370 if (DONT_COLLIDE_WITH(element) &&
7371 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7372 !PLAYER_ENEMY_PROTECTED(newx, newy))
7374 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7379 else if (CAN_MOVE_INTO_ACID(element) &&
7380 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7381 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7382 (MovDir[x][y] == MV_DOWN ||
7383 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7385 SplashAcid(newx, newy);
7386 Store[x][y] = EL_ACID;
7388 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7390 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7391 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7392 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7393 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7396 DrawLevelField(x, y);
7398 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7399 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7400 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7402 local_player->friends_still_needed--;
7403 if (!local_player->friends_still_needed &&
7404 !local_player->GameOver && AllPlayersGone)
7405 PlayerWins(local_player);
7409 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7411 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7412 DrawLevelField(newx, newy);
7414 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7416 else if (!IS_FREE(newx, newy))
7418 GfxAction[x][y] = ACTION_WAITING;
7420 if (IS_PLAYER(x, y))
7421 DrawPlayerField(x, y);
7423 DrawLevelField(x, y);
7428 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7430 if (IS_FOOD_PIG(Feld[newx][newy]))
7432 if (IS_MOVING(newx, newy))
7433 RemoveMovingField(newx, newy);
7436 Feld[newx][newy] = EL_EMPTY;
7437 DrawLevelField(newx, newy);
7440 PlayLevelSound(x, y, SND_PIG_DIGGING);
7442 else if (!IS_FREE(newx, newy))
7444 if (IS_PLAYER(x, y))
7445 DrawPlayerField(x, y);
7447 DrawLevelField(x, y);
7452 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7454 if (Store[x][y] != EL_EMPTY)
7456 boolean can_clone = FALSE;
7459 /* check if element to clone is still there */
7460 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7462 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7470 /* cannot clone or target field not free anymore -- do not clone */
7471 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7472 Store[x][y] = EL_EMPTY;
7475 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7477 if (IS_MV_DIAGONAL(MovDir[x][y]))
7479 int diagonal_move_dir = MovDir[x][y];
7480 int stored = Store[x][y];
7481 int change_delay = 8;
7484 /* android is moving diagonally */
7486 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7488 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7489 GfxElement[x][y] = EL_EMC_ANDROID;
7490 GfxAction[x][y] = ACTION_SHRINKING;
7491 GfxDir[x][y] = diagonal_move_dir;
7492 ChangeDelay[x][y] = change_delay;
7494 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7497 DrawLevelGraphicAnimation(x, y, graphic);
7498 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7500 if (Feld[newx][newy] == EL_ACID)
7502 SplashAcid(newx, newy);
7507 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7509 Store[newx][newy] = EL_EMC_ANDROID;
7510 GfxElement[newx][newy] = EL_EMC_ANDROID;
7511 GfxAction[newx][newy] = ACTION_GROWING;
7512 GfxDir[newx][newy] = diagonal_move_dir;
7513 ChangeDelay[newx][newy] = change_delay;
7515 graphic = el_act_dir2img(GfxElement[newx][newy],
7516 GfxAction[newx][newy], GfxDir[newx][newy]);
7518 DrawLevelGraphicAnimation(newx, newy, graphic);
7519 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7525 Feld[newx][newy] = EL_EMPTY;
7526 DrawLevelField(newx, newy);
7528 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7531 else if (!IS_FREE(newx, newy))
7534 if (IS_PLAYER(x, y))
7535 DrawPlayerField(x, y);
7537 DrawLevelField(x, y);
7543 else if (IS_CUSTOM_ELEMENT(element) &&
7544 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7546 int new_element = Feld[newx][newy];
7548 if (!IS_FREE(newx, newy))
7550 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7551 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7554 /* no element can dig solid indestructible elements */
7555 if (IS_INDESTRUCTIBLE(new_element) &&
7556 !IS_DIGGABLE(new_element) &&
7557 !IS_COLLECTIBLE(new_element))
7560 if (AmoebaNr[newx][newy] &&
7561 (new_element == EL_AMOEBA_FULL ||
7562 new_element == EL_BD_AMOEBA ||
7563 new_element == EL_AMOEBA_GROWING))
7565 AmoebaCnt[AmoebaNr[newx][newy]]--;
7566 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7569 if (IS_MOVING(newx, newy))
7570 RemoveMovingField(newx, newy);
7573 RemoveField(newx, newy);
7574 DrawLevelField(newx, newy);
7577 /* if digged element was about to explode, prevent the explosion */
7578 ExplodeField[newx][newy] = EX_TYPE_NONE;
7580 PlayLevelSoundAction(x, y, action);
7583 Store[newx][newy] = EL_EMPTY;
7585 /* this makes it possible to leave the removed element again */
7586 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7587 Store[newx][newy] = new_element;
7589 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7591 int move_leave_element = element_info[element].move_leave_element;
7593 /* this makes it possible to leave the removed element again */
7594 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7595 new_element : move_leave_element);
7599 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7601 RunnerVisit[x][y] = FrameCounter;
7602 PlayerVisit[x][y] /= 8; /* expire player visit path */
7605 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7607 if (!IS_FREE(newx, newy))
7609 if (IS_PLAYER(x, y))
7610 DrawPlayerField(x, y);
7612 DrawLevelField(x, y);
7618 boolean wanna_flame = !RND(10);
7619 int dx = newx - x, dy = newy - y;
7620 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7621 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7622 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7623 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7624 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7625 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7628 IS_CLASSIC_ENEMY(element1) ||
7629 IS_CLASSIC_ENEMY(element2)) &&
7630 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7631 element1 != EL_FLAMES && element2 != EL_FLAMES)
7633 ResetGfxAnimation(x, y);
7634 GfxAction[x][y] = ACTION_ATTACKING;
7636 if (IS_PLAYER(x, y))
7637 DrawPlayerField(x, y);
7639 DrawLevelField(x, y);
7641 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7643 MovDelay[x][y] = 50;
7647 RemoveField(newx, newy);
7649 Feld[newx][newy] = EL_FLAMES;
7650 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7653 RemoveField(newx1, newy1);
7655 Feld[newx1][newy1] = EL_FLAMES;
7657 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7660 RemoveField(newx2, newy2);
7662 Feld[newx2][newy2] = EL_FLAMES;
7669 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7670 Feld[newx][newy] == EL_DIAMOND)
7672 if (IS_MOVING(newx, newy))
7673 RemoveMovingField(newx, newy);
7676 Feld[newx][newy] = EL_EMPTY;
7677 DrawLevelField(newx, newy);
7680 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7682 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7683 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7685 if (AmoebaNr[newx][newy])
7687 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7688 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7689 Feld[newx][newy] == EL_BD_AMOEBA)
7690 AmoebaCnt[AmoebaNr[newx][newy]]--;
7695 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7697 RemoveMovingField(newx, newy);
7700 if (IS_MOVING(newx, newy))
7702 RemoveMovingField(newx, newy);
7707 Feld[newx][newy] = EL_EMPTY;
7708 DrawLevelField(newx, newy);
7711 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7713 else if ((element == EL_PACMAN || element == EL_MOLE)
7714 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7716 if (AmoebaNr[newx][newy])
7718 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7719 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7720 Feld[newx][newy] == EL_BD_AMOEBA)
7721 AmoebaCnt[AmoebaNr[newx][newy]]--;
7724 if (element == EL_MOLE)
7726 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7727 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7729 ResetGfxAnimation(x, y);
7730 GfxAction[x][y] = ACTION_DIGGING;
7731 DrawLevelField(x, y);
7733 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7735 return; /* wait for shrinking amoeba */
7737 else /* element == EL_PACMAN */
7739 Feld[newx][newy] = EL_EMPTY;
7740 DrawLevelField(newx, newy);
7741 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7744 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7745 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7746 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7748 /* wait for shrinking amoeba to completely disappear */
7751 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7753 /* object was running against a wall */
7758 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7759 if (move_pattern & MV_ANY_DIRECTION &&
7760 move_pattern == MovDir[x][y])
7762 int blocking_element =
7763 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7765 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7768 element = Feld[x][y]; /* element might have changed */
7772 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7773 DrawLevelElementAnimation(x, y, element);
7775 if (DONT_TOUCH(element))
7776 TestIfBadThingTouchesPlayer(x, y);
7781 InitMovingField(x, y, MovDir[x][y]);
7783 PlayLevelSoundAction(x, y, ACTION_MOVING);
7787 ContinueMoving(x, y);
7790 void ContinueMoving(int x, int y)
7792 int element = Feld[x][y];
7793 struct ElementInfo *ei = &element_info[element];
7794 int direction = MovDir[x][y];
7795 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7796 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7797 int newx = x + dx, newy = y + dy;
7798 int stored = Store[x][y];
7799 int stored_new = Store[newx][newy];
7800 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7801 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7802 boolean last_line = (newy == lev_fieldy - 1);
7804 MovPos[x][y] += getElementMoveStepsize(x, y);
7806 if (pushed_by_player) /* special case: moving object pushed by player */
7807 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7809 if (ABS(MovPos[x][y]) < TILEX)
7812 int ee = Feld[x][y];
7813 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7814 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7816 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7817 x, y, ABS(MovPos[x][y]),
7819 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7822 DrawLevelField(x, y);
7824 return; /* element is still moving */
7827 /* element reached destination field */
7829 Feld[x][y] = EL_EMPTY;
7830 Feld[newx][newy] = element;
7831 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7833 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7835 element = Feld[newx][newy] = EL_ACID;
7837 else if (element == EL_MOLE)
7839 Feld[x][y] = EL_SAND;
7841 DrawLevelFieldCrumbledSandNeighbours(x, y);
7843 else if (element == EL_QUICKSAND_FILLING)
7845 element = Feld[newx][newy] = get_next_element(element);
7846 Store[newx][newy] = Store[x][y];
7848 else if (element == EL_QUICKSAND_EMPTYING)
7850 Feld[x][y] = get_next_element(element);
7851 element = Feld[newx][newy] = Store[x][y];
7853 else if (element == EL_QUICKSAND_FAST_FILLING)
7855 element = Feld[newx][newy] = get_next_element(element);
7856 Store[newx][newy] = Store[x][y];
7858 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7860 Feld[x][y] = get_next_element(element);
7861 element = Feld[newx][newy] = Store[x][y];
7863 else if (element == EL_MAGIC_WALL_FILLING)
7865 element = Feld[newx][newy] = get_next_element(element);
7866 if (!game.magic_wall_active)
7867 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7868 Store[newx][newy] = Store[x][y];
7870 else if (element == EL_MAGIC_WALL_EMPTYING)
7872 Feld[x][y] = get_next_element(element);
7873 if (!game.magic_wall_active)
7874 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7875 element = Feld[newx][newy] = Store[x][y];
7877 #if USE_NEW_CUSTOM_VALUE
7878 InitField(newx, newy, FALSE);
7881 else if (element == EL_BD_MAGIC_WALL_FILLING)
7883 element = Feld[newx][newy] = get_next_element(element);
7884 if (!game.magic_wall_active)
7885 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7886 Store[newx][newy] = Store[x][y];
7888 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7890 Feld[x][y] = get_next_element(element);
7891 if (!game.magic_wall_active)
7892 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7893 element = Feld[newx][newy] = Store[x][y];
7895 #if USE_NEW_CUSTOM_VALUE
7896 InitField(newx, newy, FALSE);
7899 else if (element == EL_DC_MAGIC_WALL_FILLING)
7901 element = Feld[newx][newy] = get_next_element(element);
7902 if (!game.magic_wall_active)
7903 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7904 Store[newx][newy] = Store[x][y];
7906 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7908 Feld[x][y] = get_next_element(element);
7909 if (!game.magic_wall_active)
7910 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7911 element = Feld[newx][newy] = Store[x][y];
7913 #if USE_NEW_CUSTOM_VALUE
7914 InitField(newx, newy, FALSE);
7917 else if (element == EL_AMOEBA_DROPPING)
7919 Feld[x][y] = get_next_element(element);
7920 element = Feld[newx][newy] = Store[x][y];
7922 else if (element == EL_SOKOBAN_OBJECT)
7925 Feld[x][y] = Back[x][y];
7927 if (Back[newx][newy])
7928 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7930 Back[x][y] = Back[newx][newy] = 0;
7933 Store[x][y] = EL_EMPTY;
7938 MovDelay[newx][newy] = 0;
7940 if (CAN_CHANGE_OR_HAS_ACTION(element))
7942 /* copy element change control values to new field */
7943 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7944 ChangePage[newx][newy] = ChangePage[x][y];
7945 ChangeCount[newx][newy] = ChangeCount[x][y];
7946 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7949 #if USE_NEW_CUSTOM_VALUE
7950 CustomValue[newx][newy] = CustomValue[x][y];
7953 ChangeDelay[x][y] = 0;
7954 ChangePage[x][y] = -1;
7955 ChangeCount[x][y] = 0;
7956 ChangeEvent[x][y] = -1;
7958 #if USE_NEW_CUSTOM_VALUE
7959 CustomValue[x][y] = 0;
7962 /* copy animation control values to new field */
7963 GfxFrame[newx][newy] = GfxFrame[x][y];
7964 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7965 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7966 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7968 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7970 /* some elements can leave other elements behind after moving */
7972 if (ei->move_leave_element != EL_EMPTY &&
7973 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7974 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7976 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7977 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7978 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7981 int move_leave_element = ei->move_leave_element;
7985 /* this makes it possible to leave the removed element again */
7986 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7987 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7989 /* this makes it possible to leave the removed element again */
7990 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7991 move_leave_element = stored;
7994 /* this makes it possible to leave the removed element again */
7995 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7996 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7997 move_leave_element = stored;
8000 Feld[x][y] = move_leave_element;
8002 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8003 MovDir[x][y] = direction;
8005 InitField(x, y, FALSE);
8007 if (GFX_CRUMBLED(Feld[x][y]))
8008 DrawLevelFieldCrumbledSandNeighbours(x, y);
8010 if (ELEM_IS_PLAYER(move_leave_element))
8011 RelocatePlayer(x, y, move_leave_element);
8014 /* do this after checking for left-behind element */
8015 ResetGfxAnimation(x, y); /* reset animation values for old field */
8017 if (!CAN_MOVE(element) ||
8018 (CAN_FALL(element) && direction == MV_DOWN &&
8019 (element == EL_SPRING ||
8020 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8021 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8022 GfxDir[x][y] = MovDir[newx][newy] = 0;
8024 DrawLevelField(x, y);
8025 DrawLevelField(newx, newy);
8027 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8029 /* prevent pushed element from moving on in pushed direction */
8030 if (pushed_by_player && CAN_MOVE(element) &&
8031 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8032 !(element_info[element].move_pattern & direction))
8033 TurnRound(newx, newy);
8035 /* prevent elements on conveyor belt from moving on in last direction */
8036 if (pushed_by_conveyor && CAN_FALL(element) &&
8037 direction & MV_HORIZONTAL)
8038 MovDir[newx][newy] = 0;
8040 if (!pushed_by_player)
8042 int nextx = newx + dx, nexty = newy + dy;
8043 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8045 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8047 if (CAN_FALL(element) && direction == MV_DOWN)
8048 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8050 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8051 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8053 #if USE_FIX_IMPACT_COLLISION
8054 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8055 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8059 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8061 TestIfBadThingTouchesPlayer(newx, newy);
8062 TestIfBadThingTouchesFriend(newx, newy);
8064 if (!IS_CUSTOM_ELEMENT(element))
8065 TestIfBadThingTouchesOtherBadThing(newx, newy);
8067 else if (element == EL_PENGUIN)
8068 TestIfFriendTouchesBadThing(newx, newy);
8070 /* give the player one last chance (one more frame) to move away */
8071 if (CAN_FALL(element) && direction == MV_DOWN &&
8072 (last_line || (!IS_FREE(x, newy + 1) &&
8073 (!IS_PLAYER(x, newy + 1) ||
8074 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8077 if (pushed_by_player && !game.use_change_when_pushing_bug)
8079 int push_side = MV_DIR_OPPOSITE(direction);
8080 struct PlayerInfo *player = PLAYERINFO(x, y);
8082 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8083 player->index_bit, push_side);
8084 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8085 player->index_bit, push_side);
8088 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8089 MovDelay[newx][newy] = 1;
8091 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8093 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8096 if (ChangePage[newx][newy] != -1) /* delayed change */
8098 int page = ChangePage[newx][newy];
8099 struct ElementChangeInfo *change = &ei->change_page[page];
8101 ChangePage[newx][newy] = -1;
8103 if (change->can_change)
8105 if (ChangeElement(newx, newy, element, page))
8107 if (change->post_change_function)
8108 change->post_change_function(newx, newy);
8112 if (change->has_action)
8113 ExecuteCustomElementAction(newx, newy, element, page);
8117 TestIfElementHitsCustomElement(newx, newy, direction);
8118 TestIfPlayerTouchesCustomElement(newx, newy);
8119 TestIfElementTouchesCustomElement(newx, newy);
8121 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8122 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8123 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8124 MV_DIR_OPPOSITE(direction));
8127 int AmoebeNachbarNr(int ax, int ay)
8130 int element = Feld[ax][ay];
8132 static int xy[4][2] =
8140 for (i = 0; i < NUM_DIRECTIONS; i++)
8142 int x = ax + xy[i][0];
8143 int y = ay + xy[i][1];
8145 if (!IN_LEV_FIELD(x, y))
8148 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8149 group_nr = AmoebaNr[x][y];
8155 void AmoebenVereinigen(int ax, int ay)
8157 int i, x, y, xx, yy;
8158 int new_group_nr = AmoebaNr[ax][ay];
8159 static int xy[4][2] =
8167 if (new_group_nr == 0)
8170 for (i = 0; i < NUM_DIRECTIONS; i++)
8175 if (!IN_LEV_FIELD(x, y))
8178 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8179 Feld[x][y] == EL_BD_AMOEBA ||
8180 Feld[x][y] == EL_AMOEBA_DEAD) &&
8181 AmoebaNr[x][y] != new_group_nr)
8183 int old_group_nr = AmoebaNr[x][y];
8185 if (old_group_nr == 0)
8188 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8189 AmoebaCnt[old_group_nr] = 0;
8190 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8191 AmoebaCnt2[old_group_nr] = 0;
8193 SCAN_PLAYFIELD(xx, yy)
8195 if (AmoebaNr[xx][yy] == old_group_nr)
8196 AmoebaNr[xx][yy] = new_group_nr;
8202 void AmoebeUmwandeln(int ax, int ay)
8206 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8208 int group_nr = AmoebaNr[ax][ay];
8213 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8214 printf("AmoebeUmwandeln(): This should never happen!\n");
8219 SCAN_PLAYFIELD(x, y)
8221 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8224 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8228 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8229 SND_AMOEBA_TURNING_TO_GEM :
8230 SND_AMOEBA_TURNING_TO_ROCK));
8235 static int xy[4][2] =
8243 for (i = 0; i < NUM_DIRECTIONS; i++)
8248 if (!IN_LEV_FIELD(x, y))
8251 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8253 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8254 SND_AMOEBA_TURNING_TO_GEM :
8255 SND_AMOEBA_TURNING_TO_ROCK));
8262 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8265 int group_nr = AmoebaNr[ax][ay];
8266 boolean done = FALSE;
8271 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8272 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8277 SCAN_PLAYFIELD(x, y)
8279 if (AmoebaNr[x][y] == group_nr &&
8280 (Feld[x][y] == EL_AMOEBA_DEAD ||
8281 Feld[x][y] == EL_BD_AMOEBA ||
8282 Feld[x][y] == EL_AMOEBA_GROWING))
8285 Feld[x][y] = new_element;
8286 InitField(x, y, FALSE);
8287 DrawLevelField(x, y);
8293 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8294 SND_BD_AMOEBA_TURNING_TO_ROCK :
8295 SND_BD_AMOEBA_TURNING_TO_GEM));
8298 void AmoebeWaechst(int x, int y)
8300 static unsigned long sound_delay = 0;
8301 static unsigned long sound_delay_value = 0;
8303 if (!MovDelay[x][y]) /* start new growing cycle */
8307 if (DelayReached(&sound_delay, sound_delay_value))
8309 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8310 sound_delay_value = 30;
8314 if (MovDelay[x][y]) /* wait some time before growing bigger */
8317 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8319 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8320 6 - MovDelay[x][y]);
8322 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8325 if (!MovDelay[x][y])
8327 Feld[x][y] = Store[x][y];
8329 DrawLevelField(x, y);
8334 void AmoebaDisappearing(int x, int y)
8336 static unsigned long sound_delay = 0;
8337 static unsigned long sound_delay_value = 0;
8339 if (!MovDelay[x][y]) /* start new shrinking cycle */
8343 if (DelayReached(&sound_delay, sound_delay_value))
8344 sound_delay_value = 30;
8347 if (MovDelay[x][y]) /* wait some time before shrinking */
8350 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8352 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8353 6 - MovDelay[x][y]);
8355 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8358 if (!MovDelay[x][y])
8360 Feld[x][y] = EL_EMPTY;
8361 DrawLevelField(x, y);
8363 /* don't let mole enter this field in this cycle;
8364 (give priority to objects falling to this field from above) */
8370 void AmoebeAbleger(int ax, int ay)
8373 int element = Feld[ax][ay];
8374 int graphic = el2img(element);
8375 int newax = ax, neway = ay;
8376 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8377 static int xy[4][2] =
8385 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8387 Feld[ax][ay] = EL_AMOEBA_DEAD;
8388 DrawLevelField(ax, ay);
8392 if (IS_ANIMATED(graphic))
8393 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8395 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8396 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8398 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8401 if (MovDelay[ax][ay])
8405 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8408 int x = ax + xy[start][0];
8409 int y = ay + xy[start][1];
8411 if (!IN_LEV_FIELD(x, y))
8414 if (IS_FREE(x, y) ||
8415 CAN_GROW_INTO(Feld[x][y]) ||
8416 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8417 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8423 if (newax == ax && neway == ay)
8426 else /* normal or "filled" (BD style) amoeba */
8429 boolean waiting_for_player = FALSE;
8431 for (i = 0; i < NUM_DIRECTIONS; i++)
8433 int j = (start + i) % 4;
8434 int x = ax + xy[j][0];
8435 int y = ay + xy[j][1];
8437 if (!IN_LEV_FIELD(x, y))
8440 if (IS_FREE(x, y) ||
8441 CAN_GROW_INTO(Feld[x][y]) ||
8442 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8443 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8449 else if (IS_PLAYER(x, y))
8450 waiting_for_player = TRUE;
8453 if (newax == ax && neway == ay) /* amoeba cannot grow */
8455 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8457 Feld[ax][ay] = EL_AMOEBA_DEAD;
8458 DrawLevelField(ax, ay);
8459 AmoebaCnt[AmoebaNr[ax][ay]]--;
8461 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8463 if (element == EL_AMOEBA_FULL)
8464 AmoebeUmwandeln(ax, ay);
8465 else if (element == EL_BD_AMOEBA)
8466 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8471 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8473 /* amoeba gets larger by growing in some direction */
8475 int new_group_nr = AmoebaNr[ax][ay];
8478 if (new_group_nr == 0)
8480 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8481 printf("AmoebeAbleger(): This should never happen!\n");
8486 AmoebaNr[newax][neway] = new_group_nr;
8487 AmoebaCnt[new_group_nr]++;
8488 AmoebaCnt2[new_group_nr]++;
8490 /* if amoeba touches other amoeba(s) after growing, unify them */
8491 AmoebenVereinigen(newax, neway);
8493 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8495 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8501 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8502 (neway == lev_fieldy - 1 && newax != ax))
8504 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8505 Store[newax][neway] = element;
8507 else if (neway == ay || element == EL_EMC_DRIPPER)
8509 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8511 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8515 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8516 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8517 Store[ax][ay] = EL_AMOEBA_DROP;
8518 ContinueMoving(ax, ay);
8522 DrawLevelField(newax, neway);
8525 void Life(int ax, int ay)
8529 int element = Feld[ax][ay];
8530 int graphic = el2img(element);
8531 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8533 boolean changed = FALSE;
8535 if (IS_ANIMATED(graphic))
8536 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8541 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8542 MovDelay[ax][ay] = life_time;
8544 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8547 if (MovDelay[ax][ay])
8551 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8553 int xx = ax+x1, yy = ay+y1;
8556 if (!IN_LEV_FIELD(xx, yy))
8559 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8561 int x = xx+x2, y = yy+y2;
8563 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8566 if (((Feld[x][y] == element ||
8567 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8569 (IS_FREE(x, y) && Stop[x][y]))
8573 if (xx == ax && yy == ay) /* field in the middle */
8575 if (nachbarn < life_parameter[0] ||
8576 nachbarn > life_parameter[1])
8578 Feld[xx][yy] = EL_EMPTY;
8580 DrawLevelField(xx, yy);
8581 Stop[xx][yy] = TRUE;
8585 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8586 { /* free border field */
8587 if (nachbarn >= life_parameter[2] &&
8588 nachbarn <= life_parameter[3])
8590 Feld[xx][yy] = element;
8591 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8593 DrawLevelField(xx, yy);
8594 Stop[xx][yy] = TRUE;
8601 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8602 SND_GAME_OF_LIFE_GROWING);
8605 static void InitRobotWheel(int x, int y)
8607 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8610 static void RunRobotWheel(int x, int y)
8612 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8615 static void StopRobotWheel(int x, int y)
8617 if (ZX == x && ZY == y)
8621 static void InitTimegateWheel(int x, int y)
8623 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8626 static void RunTimegateWheel(int x, int y)
8628 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8631 static void InitMagicBallDelay(int x, int y)
8634 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8636 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8640 static void ActivateMagicBall(int bx, int by)
8644 if (level.ball_random)
8646 int pos_border = RND(8); /* select one of the eight border elements */
8647 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8648 int xx = pos_content % 3;
8649 int yy = pos_content / 3;
8654 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8655 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8659 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8661 int xx = x - bx + 1;
8662 int yy = y - by + 1;
8664 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8665 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8669 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8672 void CheckExit(int x, int y)
8674 if (local_player->gems_still_needed > 0 ||
8675 local_player->sokobanfields_still_needed > 0 ||
8676 local_player->lights_still_needed > 0)
8678 int element = Feld[x][y];
8679 int graphic = el2img(element);
8681 if (IS_ANIMATED(graphic))
8682 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8687 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8690 Feld[x][y] = EL_EXIT_OPENING;
8692 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8695 void CheckExitEM(int x, int y)
8697 if (local_player->gems_still_needed > 0 ||
8698 local_player->sokobanfields_still_needed > 0 ||
8699 local_player->lights_still_needed > 0)
8701 int element = Feld[x][y];
8702 int graphic = el2img(element);
8704 if (IS_ANIMATED(graphic))
8705 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8710 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8713 Feld[x][y] = EL_EM_EXIT_OPENING;
8715 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8718 void CheckExitSteel(int x, int y)
8720 if (local_player->gems_still_needed > 0 ||
8721 local_player->sokobanfields_still_needed > 0 ||
8722 local_player->lights_still_needed > 0)
8724 int element = Feld[x][y];
8725 int graphic = el2img(element);
8727 if (IS_ANIMATED(graphic))
8728 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8733 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8736 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8738 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8741 void CheckExitSteelEM(int x, int y)
8743 if (local_player->gems_still_needed > 0 ||
8744 local_player->sokobanfields_still_needed > 0 ||
8745 local_player->lights_still_needed > 0)
8747 int element = Feld[x][y];
8748 int graphic = el2img(element);
8750 if (IS_ANIMATED(graphic))
8751 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8756 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8759 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8761 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8764 void CheckExitSP(int x, int y)
8766 if (local_player->gems_still_needed > 0)
8768 int element = Feld[x][y];
8769 int graphic = el2img(element);
8771 if (IS_ANIMATED(graphic))
8772 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8777 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8780 Feld[x][y] = EL_SP_EXIT_OPENING;
8782 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8785 static void CloseAllOpenTimegates()
8789 SCAN_PLAYFIELD(x, y)
8791 int element = Feld[x][y];
8793 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8795 Feld[x][y] = EL_TIMEGATE_CLOSING;
8797 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8802 void DrawTwinkleOnField(int x, int y)
8804 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8807 if (Feld[x][y] == EL_BD_DIAMOND)
8810 if (MovDelay[x][y] == 0) /* next animation frame */
8811 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8813 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8817 if (setup.direct_draw && MovDelay[x][y])
8818 SetDrawtoField(DRAW_BUFFERED);
8820 DrawLevelElementAnimation(x, y, Feld[x][y]);
8822 if (MovDelay[x][y] != 0)
8824 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8825 10 - MovDelay[x][y]);
8827 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8829 if (setup.direct_draw)
8833 dest_x = FX + SCREENX(x) * TILEX;
8834 dest_y = FY + SCREENY(y) * TILEY;
8836 BlitBitmap(drawto_field, window,
8837 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8838 SetDrawtoField(DRAW_DIRECT);
8844 void MauerWaechst(int x, int y)
8848 if (!MovDelay[x][y]) /* next animation frame */
8849 MovDelay[x][y] = 3 * delay;
8851 if (MovDelay[x][y]) /* wait some time before next frame */
8855 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8857 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8858 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8860 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8863 if (!MovDelay[x][y])
8865 if (MovDir[x][y] == MV_LEFT)
8867 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8868 DrawLevelField(x - 1, y);
8870 else if (MovDir[x][y] == MV_RIGHT)
8872 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8873 DrawLevelField(x + 1, y);
8875 else if (MovDir[x][y] == MV_UP)
8877 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8878 DrawLevelField(x, y - 1);
8882 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8883 DrawLevelField(x, y + 1);
8886 Feld[x][y] = Store[x][y];
8888 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8889 DrawLevelField(x, y);
8894 void MauerAbleger(int ax, int ay)
8896 int element = Feld[ax][ay];
8897 int graphic = el2img(element);
8898 boolean oben_frei = FALSE, unten_frei = FALSE;
8899 boolean links_frei = FALSE, rechts_frei = FALSE;
8900 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8901 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8902 boolean new_wall = FALSE;
8904 if (IS_ANIMATED(graphic))
8905 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8907 if (!MovDelay[ax][ay]) /* start building new wall */
8908 MovDelay[ax][ay] = 6;
8910 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8913 if (MovDelay[ax][ay])
8917 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8919 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8921 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8923 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8926 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8927 element == EL_EXPANDABLE_WALL_ANY)
8931 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8932 Store[ax][ay-1] = element;
8933 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8934 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8935 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8936 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8941 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8942 Store[ax][ay+1] = element;
8943 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8944 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8945 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8946 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8951 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8952 element == EL_EXPANDABLE_WALL_ANY ||
8953 element == EL_EXPANDABLE_WALL ||
8954 element == EL_BD_EXPANDABLE_WALL)
8958 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8959 Store[ax-1][ay] = element;
8960 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8961 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8962 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8963 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8969 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8970 Store[ax+1][ay] = element;
8971 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8972 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8973 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8974 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8979 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8980 DrawLevelField(ax, ay);
8982 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8984 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8985 unten_massiv = TRUE;
8986 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8987 links_massiv = TRUE;
8988 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8989 rechts_massiv = TRUE;
8991 if (((oben_massiv && unten_massiv) ||
8992 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8993 element == EL_EXPANDABLE_WALL) &&
8994 ((links_massiv && rechts_massiv) ||
8995 element == EL_EXPANDABLE_WALL_VERTICAL))
8996 Feld[ax][ay] = EL_WALL;
8999 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9002 void MauerAblegerStahl(int ax, int ay)
9004 int element = Feld[ax][ay];
9005 int graphic = el2img(element);
9006 boolean oben_frei = FALSE, unten_frei = FALSE;
9007 boolean links_frei = FALSE, rechts_frei = FALSE;
9008 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9009 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9010 boolean new_wall = FALSE;
9012 if (IS_ANIMATED(graphic))
9013 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9015 if (!MovDelay[ax][ay]) /* start building new wall */
9016 MovDelay[ax][ay] = 6;
9018 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9021 if (MovDelay[ax][ay])
9025 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9027 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9029 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9031 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9034 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9035 element == EL_EXPANDABLE_STEELWALL_ANY)
9039 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9040 Store[ax][ay-1] = element;
9041 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9042 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9043 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9044 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9049 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9050 Store[ax][ay+1] = element;
9051 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9052 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9053 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9054 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9059 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9060 element == EL_EXPANDABLE_STEELWALL_ANY)
9064 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9065 Store[ax-1][ay] = element;
9066 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9067 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9068 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9069 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9075 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9076 Store[ax+1][ay] = element;
9077 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9078 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9079 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9080 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9085 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9087 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9088 unten_massiv = TRUE;
9089 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9090 links_massiv = TRUE;
9091 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9092 rechts_massiv = TRUE;
9094 if (((oben_massiv && unten_massiv) ||
9095 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9096 ((links_massiv && rechts_massiv) ||
9097 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9098 Feld[ax][ay] = EL_WALL;
9101 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9104 void CheckForDragon(int x, int y)
9107 boolean dragon_found = FALSE;
9108 static int xy[4][2] =
9116 for (i = 0; i < NUM_DIRECTIONS; i++)
9118 for (j = 0; j < 4; j++)
9120 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9122 if (IN_LEV_FIELD(xx, yy) &&
9123 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9125 if (Feld[xx][yy] == EL_DRAGON)
9126 dragon_found = TRUE;
9135 for (i = 0; i < NUM_DIRECTIONS; i++)
9137 for (j = 0; j < 3; j++)
9139 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9141 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9143 Feld[xx][yy] = EL_EMPTY;
9144 DrawLevelField(xx, yy);
9153 static void InitBuggyBase(int x, int y)
9155 int element = Feld[x][y];
9156 int activating_delay = FRAMES_PER_SECOND / 4;
9159 (element == EL_SP_BUGGY_BASE ?
9160 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9161 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9163 element == EL_SP_BUGGY_BASE_ACTIVE ?
9164 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9167 static void WarnBuggyBase(int x, int y)
9170 static int xy[4][2] =
9178 for (i = 0; i < NUM_DIRECTIONS; i++)
9180 int xx = x + xy[i][0];
9181 int yy = y + xy[i][1];
9183 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9185 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9192 static void InitTrap(int x, int y)
9194 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9197 static void ActivateTrap(int x, int y)
9199 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9202 static void ChangeActiveTrap(int x, int y)
9204 int graphic = IMG_TRAP_ACTIVE;
9206 /* if new animation frame was drawn, correct crumbled sand border */
9207 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9208 DrawLevelFieldCrumbledSand(x, y);
9211 static int getSpecialActionElement(int element, int number, int base_element)
9213 return (element != EL_EMPTY ? element :
9214 number != -1 ? base_element + number - 1 :
9218 static int getModifiedActionNumber(int value_old, int operator, int operand,
9219 int value_min, int value_max)
9221 int value_new = (operator == CA_MODE_SET ? operand :
9222 operator == CA_MODE_ADD ? value_old + operand :
9223 operator == CA_MODE_SUBTRACT ? value_old - operand :
9224 operator == CA_MODE_MULTIPLY ? value_old * operand :
9225 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9226 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9229 return (value_new < value_min ? value_min :
9230 value_new > value_max ? value_max :
9234 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9236 struct ElementInfo *ei = &element_info[element];
9237 struct ElementChangeInfo *change = &ei->change_page[page];
9238 int target_element = change->target_element;
9239 int action_type = change->action_type;
9240 int action_mode = change->action_mode;
9241 int action_arg = change->action_arg;
9244 if (!change->has_action)
9247 /* ---------- determine action paramater values -------------------------- */
9249 int level_time_value =
9250 (level.time > 0 ? TimeLeft :
9253 int action_arg_element =
9254 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9255 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9256 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9259 int action_arg_direction =
9260 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9261 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9262 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9263 change->actual_trigger_side :
9264 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9265 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9268 int action_arg_number_min =
9269 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9272 int action_arg_number_max =
9273 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9274 action_type == CA_SET_LEVEL_GEMS ? 999 :
9275 action_type == CA_SET_LEVEL_TIME ? 9999 :
9276 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9277 action_type == CA_SET_CE_VALUE ? 9999 :
9278 action_type == CA_SET_CE_SCORE ? 9999 :
9281 int action_arg_number_reset =
9282 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9283 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9284 action_type == CA_SET_LEVEL_TIME ? level.time :
9285 action_type == CA_SET_LEVEL_SCORE ? 0 :
9286 #if USE_NEW_CUSTOM_VALUE
9287 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9289 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9291 action_type == CA_SET_CE_SCORE ? 0 :
9294 int action_arg_number =
9295 (action_arg <= CA_ARG_MAX ? action_arg :
9296 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9297 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9298 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9299 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9300 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9301 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9302 #if USE_NEW_CUSTOM_VALUE
9303 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9305 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9307 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9308 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9309 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9310 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9311 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9312 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9313 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9314 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9315 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9316 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9317 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9320 int action_arg_number_old =
9321 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9322 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9323 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9324 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9325 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9328 int action_arg_number_new =
9329 getModifiedActionNumber(action_arg_number_old,
9330 action_mode, action_arg_number,
9331 action_arg_number_min, action_arg_number_max);
9333 int trigger_player_bits =
9334 (change->actual_trigger_player >= EL_PLAYER_1 &&
9335 change->actual_trigger_player <= EL_PLAYER_4 ?
9336 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9339 int action_arg_player_bits =
9340 (action_arg >= CA_ARG_PLAYER_1 &&
9341 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9342 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9345 /* ---------- execute action -------------------------------------------- */
9347 switch (action_type)
9354 /* ---------- level actions ------------------------------------------- */
9356 case CA_RESTART_LEVEL:
9358 game.restart_level = TRUE;
9363 case CA_SHOW_ENVELOPE:
9365 int element = getSpecialActionElement(action_arg_element,
9366 action_arg_number, EL_ENVELOPE_1);
9368 if (IS_ENVELOPE(element))
9369 local_player->show_envelope = element;
9374 case CA_SET_LEVEL_TIME:
9376 if (level.time > 0) /* only modify limited time value */
9378 TimeLeft = action_arg_number_new;
9381 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9383 DisplayGameControlValues();
9385 DrawGameValue_Time(TimeLeft);
9388 if (!TimeLeft && setup.time_limit)
9389 for (i = 0; i < MAX_PLAYERS; i++)
9390 KillPlayer(&stored_player[i]);
9396 case CA_SET_LEVEL_SCORE:
9398 local_player->score = action_arg_number_new;
9401 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9403 DisplayGameControlValues();
9405 DrawGameValue_Score(local_player->score);
9411 case CA_SET_LEVEL_GEMS:
9413 local_player->gems_still_needed = action_arg_number_new;
9416 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9418 DisplayGameControlValues();
9420 DrawGameValue_Emeralds(local_player->gems_still_needed);
9426 #if !USE_PLAYER_GRAVITY
9427 case CA_SET_LEVEL_GRAVITY:
9429 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9430 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9431 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9437 case CA_SET_LEVEL_WIND:
9439 game.wind_direction = action_arg_direction;
9444 /* ---------- player actions ------------------------------------------ */
9446 case CA_MOVE_PLAYER:
9448 /* automatically move to the next field in specified direction */
9449 for (i = 0; i < MAX_PLAYERS; i++)
9450 if (trigger_player_bits & (1 << i))
9451 stored_player[i].programmed_action = action_arg_direction;
9456 case CA_EXIT_PLAYER:
9458 for (i = 0; i < MAX_PLAYERS; i++)
9459 if (action_arg_player_bits & (1 << i))
9460 PlayerWins(&stored_player[i]);
9465 case CA_KILL_PLAYER:
9467 for (i = 0; i < MAX_PLAYERS; i++)
9468 if (action_arg_player_bits & (1 << i))
9469 KillPlayer(&stored_player[i]);
9474 case CA_SET_PLAYER_KEYS:
9476 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9477 int element = getSpecialActionElement(action_arg_element,
9478 action_arg_number, EL_KEY_1);
9480 if (IS_KEY(element))
9482 for (i = 0; i < MAX_PLAYERS; i++)
9484 if (trigger_player_bits & (1 << i))
9486 stored_player[i].key[KEY_NR(element)] = key_state;
9488 DrawGameDoorValues();
9496 case CA_SET_PLAYER_SPEED:
9498 for (i = 0; i < MAX_PLAYERS; i++)
9500 if (trigger_player_bits & (1 << i))
9502 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9504 if (action_arg == CA_ARG_SPEED_FASTER &&
9505 stored_player[i].cannot_move)
9507 action_arg_number = STEPSIZE_VERY_SLOW;
9509 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9510 action_arg == CA_ARG_SPEED_FASTER)
9512 action_arg_number = 2;
9513 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9516 else if (action_arg == CA_ARG_NUMBER_RESET)
9518 action_arg_number = level.initial_player_stepsize[i];
9522 getModifiedActionNumber(move_stepsize,
9525 action_arg_number_min,
9526 action_arg_number_max);
9528 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9535 case CA_SET_PLAYER_SHIELD:
9537 for (i = 0; i < MAX_PLAYERS; i++)
9539 if (trigger_player_bits & (1 << i))
9541 if (action_arg == CA_ARG_SHIELD_OFF)
9543 stored_player[i].shield_normal_time_left = 0;
9544 stored_player[i].shield_deadly_time_left = 0;
9546 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9548 stored_player[i].shield_normal_time_left = 999999;
9550 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9552 stored_player[i].shield_normal_time_left = 999999;
9553 stored_player[i].shield_deadly_time_left = 999999;
9561 #if USE_PLAYER_GRAVITY
9562 case CA_SET_PLAYER_GRAVITY:
9564 for (i = 0; i < MAX_PLAYERS; i++)
9566 if (trigger_player_bits & (1 << i))
9568 stored_player[i].gravity =
9569 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9570 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9571 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9572 stored_player[i].gravity);
9580 case CA_SET_PLAYER_ARTWORK:
9582 for (i = 0; i < MAX_PLAYERS; i++)
9584 if (trigger_player_bits & (1 << i))
9586 int artwork_element = action_arg_element;
9588 if (action_arg == CA_ARG_ELEMENT_RESET)
9590 (level.use_artwork_element[i] ? level.artwork_element[i] :
9591 stored_player[i].element_nr);
9593 #if USE_GFX_RESET_PLAYER_ARTWORK
9594 if (stored_player[i].artwork_element != artwork_element)
9595 stored_player[i].Frame = 0;
9598 stored_player[i].artwork_element = artwork_element;
9600 SetPlayerWaiting(&stored_player[i], FALSE);
9602 /* set number of special actions for bored and sleeping animation */
9603 stored_player[i].num_special_action_bored =
9604 get_num_special_action(artwork_element,
9605 ACTION_BORING_1, ACTION_BORING_LAST);
9606 stored_player[i].num_special_action_sleeping =
9607 get_num_special_action(artwork_element,
9608 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9615 /* ---------- CE actions ---------------------------------------------- */
9617 case CA_SET_CE_VALUE:
9619 #if USE_NEW_CUSTOM_VALUE
9620 int last_ce_value = CustomValue[x][y];
9622 CustomValue[x][y] = action_arg_number_new;
9624 if (CustomValue[x][y] != last_ce_value)
9626 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9627 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9629 if (CustomValue[x][y] == 0)
9631 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9632 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9640 case CA_SET_CE_SCORE:
9642 #if USE_NEW_CUSTOM_VALUE
9643 int last_ce_score = ei->collect_score;
9645 ei->collect_score = action_arg_number_new;
9647 if (ei->collect_score != last_ce_score)
9649 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9650 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9652 if (ei->collect_score == 0)
9656 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9657 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9660 This is a very special case that seems to be a mixture between
9661 CheckElementChange() and CheckTriggeredElementChange(): while
9662 the first one only affects single elements that are triggered
9663 directly, the second one affects multiple elements in the playfield
9664 that are triggered indirectly by another element. This is a third
9665 case: Changing the CE score always affects multiple identical CEs,
9666 so every affected CE must be checked, not only the single CE for
9667 which the CE score was changed in the first place (as every instance
9668 of that CE shares the same CE score, and therefore also can change)!
9670 SCAN_PLAYFIELD(xx, yy)
9672 if (Feld[xx][yy] == element)
9673 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9674 CE_SCORE_GETS_ZERO);
9683 /* ---------- engine actions ------------------------------------------ */
9685 case CA_SET_ENGINE_SCAN_MODE:
9687 InitPlayfieldScanMode(action_arg);
9697 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9699 int old_element = Feld[x][y];
9700 int new_element = GetElementFromGroupElement(element);
9701 int previous_move_direction = MovDir[x][y];
9702 #if USE_NEW_CUSTOM_VALUE
9703 int last_ce_value = CustomValue[x][y];
9705 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9706 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9707 boolean add_player_onto_element = (new_element_is_player &&
9708 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9709 /* this breaks SnakeBite when a snake is
9710 halfway through a door that closes */
9711 /* NOW FIXED AT LEVEL INIT IN files.c */
9712 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9714 IS_WALKABLE(old_element));
9717 /* check if element under the player changes from accessible to unaccessible
9718 (needed for special case of dropping element which then changes) */
9719 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9720 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9728 if (!add_player_onto_element)
9730 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9731 RemoveMovingField(x, y);
9735 Feld[x][y] = new_element;
9737 #if !USE_GFX_RESET_GFX_ANIMATION
9738 ResetGfxAnimation(x, y);
9739 ResetRandomAnimationValue(x, y);
9742 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9743 MovDir[x][y] = previous_move_direction;
9745 #if USE_NEW_CUSTOM_VALUE
9746 if (element_info[new_element].use_last_ce_value)
9747 CustomValue[x][y] = last_ce_value;
9750 InitField_WithBug1(x, y, FALSE);
9752 new_element = Feld[x][y]; /* element may have changed */
9754 #if USE_GFX_RESET_GFX_ANIMATION
9755 ResetGfxAnimation(x, y);
9756 ResetRandomAnimationValue(x, y);
9759 DrawLevelField(x, y);
9761 if (GFX_CRUMBLED(new_element))
9762 DrawLevelFieldCrumbledSandNeighbours(x, y);
9766 /* check if element under the player changes from accessible to unaccessible
9767 (needed for special case of dropping element which then changes) */
9768 /* (must be checked after creating new element for walkable group elements) */
9769 #if USE_FIX_KILLED_BY_NON_WALKABLE
9770 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9771 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9778 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9779 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9788 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9789 if (new_element_is_player)
9790 RelocatePlayer(x, y, new_element);
9793 ChangeCount[x][y]++; /* count number of changes in the same frame */
9795 TestIfBadThingTouchesPlayer(x, y);
9796 TestIfPlayerTouchesCustomElement(x, y);
9797 TestIfElementTouchesCustomElement(x, y);
9800 static void CreateField(int x, int y, int element)
9802 CreateFieldExt(x, y, element, FALSE);
9805 static void CreateElementFromChange(int x, int y, int element)
9807 element = GET_VALID_RUNTIME_ELEMENT(element);
9809 #if USE_STOP_CHANGED_ELEMENTS
9810 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9812 int old_element = Feld[x][y];
9814 /* prevent changed element from moving in same engine frame
9815 unless both old and new element can either fall or move */
9816 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9817 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9822 CreateFieldExt(x, y, element, TRUE);
9825 static boolean ChangeElement(int x, int y, int element, int page)
9827 struct ElementInfo *ei = &element_info[element];
9828 struct ElementChangeInfo *change = &ei->change_page[page];
9829 int ce_value = CustomValue[x][y];
9830 int ce_score = ei->collect_score;
9832 int old_element = Feld[x][y];
9834 /* always use default change event to prevent running into a loop */
9835 if (ChangeEvent[x][y] == -1)
9836 ChangeEvent[x][y] = CE_DELAY;
9838 if (ChangeEvent[x][y] == CE_DELAY)
9840 /* reset actual trigger element, trigger player and action element */
9841 change->actual_trigger_element = EL_EMPTY;
9842 change->actual_trigger_player = EL_PLAYER_1;
9843 change->actual_trigger_side = CH_SIDE_NONE;
9844 change->actual_trigger_ce_value = 0;
9845 change->actual_trigger_ce_score = 0;
9848 /* do not change elements more than a specified maximum number of changes */
9849 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9852 ChangeCount[x][y]++; /* count number of changes in the same frame */
9854 if (change->explode)
9861 if (change->use_target_content)
9863 boolean complete_replace = TRUE;
9864 boolean can_replace[3][3];
9867 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9870 boolean is_walkable;
9871 boolean is_diggable;
9872 boolean is_collectible;
9873 boolean is_removable;
9874 boolean is_destructible;
9875 int ex = x + xx - 1;
9876 int ey = y + yy - 1;
9877 int content_element = change->target_content.e[xx][yy];
9880 can_replace[xx][yy] = TRUE;
9882 if (ex == x && ey == y) /* do not check changing element itself */
9885 if (content_element == EL_EMPTY_SPACE)
9887 can_replace[xx][yy] = FALSE; /* do not replace border with space */
9892 if (!IN_LEV_FIELD(ex, ey))
9894 can_replace[xx][yy] = FALSE;
9895 complete_replace = FALSE;
9902 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9903 e = MovingOrBlocked2Element(ex, ey);
9905 is_empty = (IS_FREE(ex, ey) ||
9906 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9908 is_walkable = (is_empty || IS_WALKABLE(e));
9909 is_diggable = (is_empty || IS_DIGGABLE(e));
9910 is_collectible = (is_empty || IS_COLLECTIBLE(e));
9911 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9912 is_removable = (is_diggable || is_collectible);
9914 can_replace[xx][yy] =
9915 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9916 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9917 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9918 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9919 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9920 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9921 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9923 if (!can_replace[xx][yy])
9924 complete_replace = FALSE;
9927 if (!change->only_if_complete || complete_replace)
9929 boolean something_has_changed = FALSE;
9931 if (change->only_if_complete && change->use_random_replace &&
9932 RND(100) < change->random_percentage)
9935 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9937 int ex = x + xx - 1;
9938 int ey = y + yy - 1;
9939 int content_element;
9941 if (can_replace[xx][yy] && (!change->use_random_replace ||
9942 RND(100) < change->random_percentage))
9944 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9945 RemoveMovingField(ex, ey);
9947 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9949 content_element = change->target_content.e[xx][yy];
9950 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9951 ce_value, ce_score);
9953 CreateElementFromChange(ex, ey, target_element);
9955 something_has_changed = TRUE;
9957 /* for symmetry reasons, freeze newly created border elements */
9958 if (ex != x || ey != y)
9959 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9963 if (something_has_changed)
9965 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9966 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9972 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9973 ce_value, ce_score);
9975 if (element == EL_DIAGONAL_GROWING ||
9976 element == EL_DIAGONAL_SHRINKING)
9978 target_element = Store[x][y];
9980 Store[x][y] = EL_EMPTY;
9983 CreateElementFromChange(x, y, target_element);
9985 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9986 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9989 /* this uses direct change before indirect change */
9990 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9995 #if USE_NEW_DELAYED_ACTION
9997 static void HandleElementChange(int x, int y, int page)
9999 int element = MovingOrBlocked2Element(x, y);
10000 struct ElementInfo *ei = &element_info[element];
10001 struct ElementChangeInfo *change = &ei->change_page[page];
10004 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10005 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10008 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10009 x, y, element, element_info[element].token_name);
10010 printf("HandleElementChange(): This should never happen!\n");
10015 /* this can happen with classic bombs on walkable, changing elements */
10016 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10019 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10020 ChangeDelay[x][y] = 0;
10026 if (ChangeDelay[x][y] == 0) /* initialize element change */
10028 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10030 if (change->can_change)
10033 /* !!! not clear why graphic animation should be reset at all here !!! */
10034 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10035 #if USE_GFX_RESET_WHEN_NOT_MOVING
10036 /* when a custom element is about to change (for example by change delay),
10037 do not reset graphic animation when the custom element is moving */
10038 if (!IS_MOVING(x, y))
10041 ResetGfxAnimation(x, y);
10042 ResetRandomAnimationValue(x, y);
10046 if (change->pre_change_function)
10047 change->pre_change_function(x, y);
10051 ChangeDelay[x][y]--;
10053 if (ChangeDelay[x][y] != 0) /* continue element change */
10055 if (change->can_change)
10057 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10059 if (IS_ANIMATED(graphic))
10060 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10062 if (change->change_function)
10063 change->change_function(x, y);
10066 else /* finish element change */
10068 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10070 page = ChangePage[x][y];
10071 ChangePage[x][y] = -1;
10073 change = &ei->change_page[page];
10076 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10078 ChangeDelay[x][y] = 1; /* try change after next move step */
10079 ChangePage[x][y] = page; /* remember page to use for change */
10084 if (change->can_change)
10086 if (ChangeElement(x, y, element, page))
10088 if (change->post_change_function)
10089 change->post_change_function(x, y);
10093 if (change->has_action)
10094 ExecuteCustomElementAction(x, y, element, page);
10100 static void HandleElementChange(int x, int y, int page)
10102 int element = MovingOrBlocked2Element(x, y);
10103 struct ElementInfo *ei = &element_info[element];
10104 struct ElementChangeInfo *change = &ei->change_page[page];
10107 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10110 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10111 x, y, element, element_info[element].token_name);
10112 printf("HandleElementChange(): This should never happen!\n");
10117 /* this can happen with classic bombs on walkable, changing elements */
10118 if (!CAN_CHANGE(element))
10121 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10122 ChangeDelay[x][y] = 0;
10128 if (ChangeDelay[x][y] == 0) /* initialize element change */
10130 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10132 ResetGfxAnimation(x, y);
10133 ResetRandomAnimationValue(x, y);
10135 if (change->pre_change_function)
10136 change->pre_change_function(x, y);
10139 ChangeDelay[x][y]--;
10141 if (ChangeDelay[x][y] != 0) /* continue element change */
10143 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10145 if (IS_ANIMATED(graphic))
10146 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10148 if (change->change_function)
10149 change->change_function(x, y);
10151 else /* finish element change */
10153 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10155 page = ChangePage[x][y];
10156 ChangePage[x][y] = -1;
10158 change = &ei->change_page[page];
10161 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10163 ChangeDelay[x][y] = 1; /* try change after next move step */
10164 ChangePage[x][y] = page; /* remember page to use for change */
10169 if (ChangeElement(x, y, element, page))
10171 if (change->post_change_function)
10172 change->post_change_function(x, y);
10179 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10180 int trigger_element,
10182 int trigger_player,
10186 boolean change_done_any = FALSE;
10187 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10190 if (!(trigger_events[trigger_element][trigger_event]))
10194 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10195 trigger_event, recursion_loop_depth, recursion_loop_detected,
10196 recursion_loop_element, EL_NAME(recursion_loop_element));
10199 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10201 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10203 int element = EL_CUSTOM_START + i;
10204 boolean change_done = FALSE;
10207 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10208 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10211 for (p = 0; p < element_info[element].num_change_pages; p++)
10213 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10215 if (change->can_change_or_has_action &&
10216 change->has_event[trigger_event] &&
10217 change->trigger_side & trigger_side &&
10218 change->trigger_player & trigger_player &&
10219 change->trigger_page & trigger_page_bits &&
10220 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10222 change->actual_trigger_element = trigger_element;
10223 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10224 change->actual_trigger_side = trigger_side;
10225 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10226 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10228 if ((change->can_change && !change_done) || change->has_action)
10232 SCAN_PLAYFIELD(x, y)
10234 if (Feld[x][y] == element)
10236 if (change->can_change && !change_done)
10238 ChangeDelay[x][y] = 1;
10239 ChangeEvent[x][y] = trigger_event;
10241 HandleElementChange(x, y, p);
10243 #if USE_NEW_DELAYED_ACTION
10244 else if (change->has_action)
10246 ExecuteCustomElementAction(x, y, element, p);
10247 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10250 if (change->has_action)
10252 ExecuteCustomElementAction(x, y, element, p);
10253 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10259 if (change->can_change)
10261 change_done = TRUE;
10262 change_done_any = TRUE;
10269 RECURSION_LOOP_DETECTION_END();
10271 return change_done_any;
10274 static boolean CheckElementChangeExt(int x, int y,
10276 int trigger_element,
10278 int trigger_player,
10281 boolean change_done = FALSE;
10284 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10285 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10288 if (Feld[x][y] == EL_BLOCKED)
10290 Blocked2Moving(x, y, &x, &y);
10291 element = Feld[x][y];
10295 /* check if element has already changed */
10296 if (Feld[x][y] != element)
10299 /* check if element has already changed or is about to change after moving */
10300 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10301 Feld[x][y] != element) ||
10303 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10304 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10305 ChangePage[x][y] != -1)))
10310 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10311 trigger_event, recursion_loop_depth, recursion_loop_detected,
10312 recursion_loop_element, EL_NAME(recursion_loop_element));
10315 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10317 for (p = 0; p < element_info[element].num_change_pages; p++)
10319 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10321 /* check trigger element for all events where the element that is checked
10322 for changing interacts with a directly adjacent element -- this is
10323 different to element changes that affect other elements to change on the
10324 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10325 boolean check_trigger_element =
10326 (trigger_event == CE_TOUCHING_X ||
10327 trigger_event == CE_HITTING_X ||
10328 trigger_event == CE_HIT_BY_X ||
10330 /* this one was forgotten until 3.2.3 */
10331 trigger_event == CE_DIGGING_X);
10334 if (change->can_change_or_has_action &&
10335 change->has_event[trigger_event] &&
10336 change->trigger_side & trigger_side &&
10337 change->trigger_player & trigger_player &&
10338 (!check_trigger_element ||
10339 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10341 change->actual_trigger_element = trigger_element;
10342 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10343 change->actual_trigger_side = trigger_side;
10344 change->actual_trigger_ce_value = CustomValue[x][y];
10345 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10347 /* special case: trigger element not at (x,y) position for some events */
10348 if (check_trigger_element)
10360 { 0, 0 }, { 0, 0 }, { 0, 0 },
10364 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10365 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10367 change->actual_trigger_ce_value = CustomValue[xx][yy];
10368 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10371 if (change->can_change && !change_done)
10373 ChangeDelay[x][y] = 1;
10374 ChangeEvent[x][y] = trigger_event;
10376 HandleElementChange(x, y, p);
10378 change_done = TRUE;
10380 #if USE_NEW_DELAYED_ACTION
10381 else if (change->has_action)
10383 ExecuteCustomElementAction(x, y, element, p);
10384 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10387 if (change->has_action)
10389 ExecuteCustomElementAction(x, y, element, p);
10390 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10396 RECURSION_LOOP_DETECTION_END();
10398 return change_done;
10401 static void PlayPlayerSound(struct PlayerInfo *player)
10403 int jx = player->jx, jy = player->jy;
10404 int sound_element = player->artwork_element;
10405 int last_action = player->last_action_waiting;
10406 int action = player->action_waiting;
10408 if (player->is_waiting)
10410 if (action != last_action)
10411 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10413 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10417 if (action != last_action)
10418 StopSound(element_info[sound_element].sound[last_action]);
10420 if (last_action == ACTION_SLEEPING)
10421 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10425 static void PlayAllPlayersSound()
10429 for (i = 0; i < MAX_PLAYERS; i++)
10430 if (stored_player[i].active)
10431 PlayPlayerSound(&stored_player[i]);
10434 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10436 boolean last_waiting = player->is_waiting;
10437 int move_dir = player->MovDir;
10439 player->dir_waiting = move_dir;
10440 player->last_action_waiting = player->action_waiting;
10444 if (!last_waiting) /* not waiting -> waiting */
10446 player->is_waiting = TRUE;
10448 player->frame_counter_bored =
10450 game.player_boring_delay_fixed +
10451 GetSimpleRandom(game.player_boring_delay_random);
10452 player->frame_counter_sleeping =
10454 game.player_sleeping_delay_fixed +
10455 GetSimpleRandom(game.player_sleeping_delay_random);
10457 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10460 if (game.player_sleeping_delay_fixed +
10461 game.player_sleeping_delay_random > 0 &&
10462 player->anim_delay_counter == 0 &&
10463 player->post_delay_counter == 0 &&
10464 FrameCounter >= player->frame_counter_sleeping)
10465 player->is_sleeping = TRUE;
10466 else if (game.player_boring_delay_fixed +
10467 game.player_boring_delay_random > 0 &&
10468 FrameCounter >= player->frame_counter_bored)
10469 player->is_bored = TRUE;
10471 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10472 player->is_bored ? ACTION_BORING :
10475 if (player->is_sleeping && player->use_murphy)
10477 /* special case for sleeping Murphy when leaning against non-free tile */
10479 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10480 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10481 !IS_MOVING(player->jx - 1, player->jy)))
10482 move_dir = MV_LEFT;
10483 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10484 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10485 !IS_MOVING(player->jx + 1, player->jy)))
10486 move_dir = MV_RIGHT;
10488 player->is_sleeping = FALSE;
10490 player->dir_waiting = move_dir;
10493 if (player->is_sleeping)
10495 if (player->num_special_action_sleeping > 0)
10497 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10499 int last_special_action = player->special_action_sleeping;
10500 int num_special_action = player->num_special_action_sleeping;
10501 int special_action =
10502 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10503 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10504 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10505 last_special_action + 1 : ACTION_SLEEPING);
10506 int special_graphic =
10507 el_act_dir2img(player->artwork_element, special_action, move_dir);
10509 player->anim_delay_counter =
10510 graphic_info[special_graphic].anim_delay_fixed +
10511 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10512 player->post_delay_counter =
10513 graphic_info[special_graphic].post_delay_fixed +
10514 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10516 player->special_action_sleeping = special_action;
10519 if (player->anim_delay_counter > 0)
10521 player->action_waiting = player->special_action_sleeping;
10522 player->anim_delay_counter--;
10524 else if (player->post_delay_counter > 0)
10526 player->post_delay_counter--;
10530 else if (player->is_bored)
10532 if (player->num_special_action_bored > 0)
10534 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10536 int special_action =
10537 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10538 int special_graphic =
10539 el_act_dir2img(player->artwork_element, special_action, move_dir);
10541 player->anim_delay_counter =
10542 graphic_info[special_graphic].anim_delay_fixed +
10543 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10544 player->post_delay_counter =
10545 graphic_info[special_graphic].post_delay_fixed +
10546 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10548 player->special_action_bored = special_action;
10551 if (player->anim_delay_counter > 0)
10553 player->action_waiting = player->special_action_bored;
10554 player->anim_delay_counter--;
10556 else if (player->post_delay_counter > 0)
10558 player->post_delay_counter--;
10563 else if (last_waiting) /* waiting -> not waiting */
10565 player->is_waiting = FALSE;
10566 player->is_bored = FALSE;
10567 player->is_sleeping = FALSE;
10569 player->frame_counter_bored = -1;
10570 player->frame_counter_sleeping = -1;
10572 player->anim_delay_counter = 0;
10573 player->post_delay_counter = 0;
10575 player->dir_waiting = player->MovDir;
10576 player->action_waiting = ACTION_DEFAULT;
10578 player->special_action_bored = ACTION_DEFAULT;
10579 player->special_action_sleeping = ACTION_DEFAULT;
10583 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10585 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10586 int left = player_action & JOY_LEFT;
10587 int right = player_action & JOY_RIGHT;
10588 int up = player_action & JOY_UP;
10589 int down = player_action & JOY_DOWN;
10590 int button1 = player_action & JOY_BUTTON_1;
10591 int button2 = player_action & JOY_BUTTON_2;
10592 int dx = (left ? -1 : right ? 1 : 0);
10593 int dy = (up ? -1 : down ? 1 : 0);
10595 if (!player->active || tape.pausing)
10601 snapped = SnapField(player, dx, dy);
10605 dropped = DropElement(player);
10607 moved = MovePlayer(player, dx, dy);
10610 if (tape.single_step && tape.recording && !tape.pausing)
10612 if (button1 || (dropped && !moved))
10614 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10615 SnapField(player, 0, 0); /* stop snapping */
10619 SetPlayerWaiting(player, FALSE);
10621 return player_action;
10625 /* no actions for this player (no input at player's configured device) */
10627 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10628 SnapField(player, 0, 0);
10629 CheckGravityMovementWhenNotMoving(player);
10631 if (player->MovPos == 0)
10632 SetPlayerWaiting(player, TRUE);
10634 if (player->MovPos == 0) /* needed for tape.playing */
10635 player->is_moving = FALSE;
10637 player->is_dropping = FALSE;
10638 player->is_dropping_pressed = FALSE;
10639 player->drop_pressed_delay = 0;
10645 static void CheckLevelTime()
10649 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10651 if (level.native_em_level->lev->home == 0) /* all players at home */
10653 PlayerWins(local_player);
10655 AllPlayersGone = TRUE;
10657 level.native_em_level->lev->home = -1;
10660 if (level.native_em_level->ply[0]->alive == 0 &&
10661 level.native_em_level->ply[1]->alive == 0 &&
10662 level.native_em_level->ply[2]->alive == 0 &&
10663 level.native_em_level->ply[3]->alive == 0) /* all dead */
10664 AllPlayersGone = TRUE;
10667 if (TimeFrames >= FRAMES_PER_SECOND)
10672 for (i = 0; i < MAX_PLAYERS; i++)
10674 struct PlayerInfo *player = &stored_player[i];
10676 if (SHIELD_ON(player))
10678 player->shield_normal_time_left--;
10680 if (player->shield_deadly_time_left > 0)
10681 player->shield_deadly_time_left--;
10685 if (!local_player->LevelSolved && !level.use_step_counter)
10693 if (TimeLeft <= 10 && setup.time_limit)
10694 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10697 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10699 DisplayGameControlValues();
10701 DrawGameValue_Time(TimeLeft);
10704 if (!TimeLeft && setup.time_limit)
10706 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10707 level.native_em_level->lev->killed_out_of_time = TRUE;
10709 for (i = 0; i < MAX_PLAYERS; i++)
10710 KillPlayer(&stored_player[i]);
10714 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10716 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10718 DisplayGameControlValues();
10721 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10722 DrawGameValue_Time(TimePlayed);
10725 level.native_em_level->lev->time =
10726 (level.time == 0 ? TimePlayed : TimeLeft);
10729 if (tape.recording || tape.playing)
10730 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10733 DrawGameDoorValues();
10736 void AdvanceFrameAndPlayerCounters(int player_nr)
10740 /* advance frame counters (global frame counter and time frame counter) */
10744 /* advance player counters (counters for move delay, move animation etc.) */
10745 for (i = 0; i < MAX_PLAYERS; i++)
10747 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10748 int move_delay_value = stored_player[i].move_delay_value;
10749 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10751 if (!advance_player_counters) /* not all players may be affected */
10754 #if USE_NEW_PLAYER_ANIM
10755 if (move_frames == 0) /* less than one move per game frame */
10757 int stepsize = TILEX / move_delay_value;
10758 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10759 int count = (stored_player[i].is_moving ?
10760 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10762 if (count % delay == 0)
10767 stored_player[i].Frame += move_frames;
10769 if (stored_player[i].MovPos != 0)
10770 stored_player[i].StepFrame += move_frames;
10772 if (stored_player[i].move_delay > 0)
10773 stored_player[i].move_delay--;
10775 /* due to bugs in previous versions, counter must count up, not down */
10776 if (stored_player[i].push_delay != -1)
10777 stored_player[i].push_delay++;
10779 if (stored_player[i].drop_delay > 0)
10780 stored_player[i].drop_delay--;
10782 if (stored_player[i].is_dropping_pressed)
10783 stored_player[i].drop_pressed_delay++;
10787 void StartGameActions(boolean init_network_game, boolean record_tape,
10790 unsigned long new_random_seed = InitRND(random_seed);
10793 TapeStartRecording(new_random_seed);
10795 #if defined(NETWORK_AVALIABLE)
10796 if (init_network_game)
10798 SendToServer_StartPlaying();
10809 static unsigned long game_frame_delay = 0;
10810 unsigned long game_frame_delay_value;
10811 byte *recorded_player_action;
10812 byte summarized_player_action = 0;
10813 byte tape_action[MAX_PLAYERS];
10816 /* detect endless loops, caused by custom element programming */
10817 if (recursion_loop_detected && recursion_loop_depth == 0)
10819 char *message = getStringCat3("Internal Error ! Element ",
10820 EL_NAME(recursion_loop_element),
10821 " caused endless loop ! Quit the game ?");
10823 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10824 EL_NAME(recursion_loop_element));
10826 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10828 recursion_loop_detected = FALSE; /* if game should be continued */
10835 if (game.restart_level)
10836 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10838 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10840 if (level.native_em_level->lev->home == 0) /* all players at home */
10842 PlayerWins(local_player);
10844 AllPlayersGone = TRUE;
10846 level.native_em_level->lev->home = -1;
10849 if (level.native_em_level->ply[0]->alive == 0 &&
10850 level.native_em_level->ply[1]->alive == 0 &&
10851 level.native_em_level->ply[2]->alive == 0 &&
10852 level.native_em_level->ply[3]->alive == 0) /* all dead */
10853 AllPlayersGone = TRUE;
10856 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10859 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10862 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10865 game_frame_delay_value =
10866 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10868 if (tape.playing && tape.warp_forward && !tape.pausing)
10869 game_frame_delay_value = 0;
10871 /* ---------- main game synchronization point ---------- */
10873 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10875 if (network_playing && !network_player_action_received)
10877 /* try to get network player actions in time */
10879 #if defined(NETWORK_AVALIABLE)
10880 /* last chance to get network player actions without main loop delay */
10881 HandleNetworking();
10884 /* game was quit by network peer */
10885 if (game_status != GAME_MODE_PLAYING)
10888 if (!network_player_action_received)
10889 return; /* failed to get network player actions in time */
10891 /* do not yet reset "network_player_action_received" (for tape.pausing) */
10897 /* at this point we know that we really continue executing the game */
10899 network_player_action_received = FALSE;
10901 /* when playing tape, read previously recorded player input from tape data */
10902 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10905 /* TapePlayAction() may return NULL when toggling to "pause before death" */
10910 if (tape.set_centered_player)
10912 game.centered_player_nr_next = tape.centered_player_nr_next;
10913 game.set_centered_player = TRUE;
10916 for (i = 0; i < MAX_PLAYERS; i++)
10918 summarized_player_action |= stored_player[i].action;
10920 if (!network_playing)
10921 stored_player[i].effective_action = stored_player[i].action;
10924 #if defined(NETWORK_AVALIABLE)
10925 if (network_playing)
10926 SendToServer_MovePlayer(summarized_player_action);
10929 if (!options.network && !setup.team_mode)
10930 local_player->effective_action = summarized_player_action;
10932 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10934 for (i = 0; i < MAX_PLAYERS; i++)
10935 stored_player[i].effective_action =
10936 (i == game.centered_player_nr ? summarized_player_action : 0);
10939 if (recorded_player_action != NULL)
10940 for (i = 0; i < MAX_PLAYERS; i++)
10941 stored_player[i].effective_action = recorded_player_action[i];
10943 for (i = 0; i < MAX_PLAYERS; i++)
10945 tape_action[i] = stored_player[i].effective_action;
10947 /* (this can only happen in the R'n'D game engine) */
10948 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10949 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10952 /* only record actions from input devices, but not programmed actions */
10953 if (tape.recording)
10954 TapeRecordAction(tape_action);
10956 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10958 GameActions_EM_Main();
10966 void GameActions_EM_Main()
10968 byte effective_action[MAX_PLAYERS];
10969 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10972 for (i = 0; i < MAX_PLAYERS; i++)
10973 effective_action[i] = stored_player[i].effective_action;
10975 GameActions_EM(effective_action, warp_mode);
10979 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10982 void GameActions_RND()
10984 int magic_wall_x = 0, magic_wall_y = 0;
10985 int i, x, y, element, graphic;
10987 InitPlayfieldScanModeVars();
10989 #if USE_ONE_MORE_CHANGE_PER_FRAME
10990 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10992 SCAN_PLAYFIELD(x, y)
10994 ChangeCount[x][y] = 0;
10995 ChangeEvent[x][y] = -1;
11000 if (game.set_centered_player)
11002 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11004 /* switching to "all players" only possible if all players fit to screen */
11005 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11007 game.centered_player_nr_next = game.centered_player_nr;
11008 game.set_centered_player = FALSE;
11011 /* do not switch focus to non-existing (or non-active) player */
11012 if (game.centered_player_nr_next >= 0 &&
11013 !stored_player[game.centered_player_nr_next].active)
11015 game.centered_player_nr_next = game.centered_player_nr;
11016 game.set_centered_player = FALSE;
11020 if (game.set_centered_player &&
11021 ScreenMovPos == 0) /* screen currently aligned at tile position */
11025 if (game.centered_player_nr_next == -1)
11027 setScreenCenteredToAllPlayers(&sx, &sy);
11031 sx = stored_player[game.centered_player_nr_next].jx;
11032 sy = stored_player[game.centered_player_nr_next].jy;
11035 game.centered_player_nr = game.centered_player_nr_next;
11036 game.set_centered_player = FALSE;
11038 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11039 DrawGameDoorValues();
11042 for (i = 0; i < MAX_PLAYERS; i++)
11044 int actual_player_action = stored_player[i].effective_action;
11047 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11048 - rnd_equinox_tetrachloride 048
11049 - rnd_equinox_tetrachloride_ii 096
11050 - rnd_emanuel_schmieg 002
11051 - doctor_sloan_ww 001, 020
11053 if (stored_player[i].MovPos == 0)
11054 CheckGravityMovement(&stored_player[i]);
11057 /* overwrite programmed action with tape action */
11058 if (stored_player[i].programmed_action)
11059 actual_player_action = stored_player[i].programmed_action;
11061 PlayerActions(&stored_player[i], actual_player_action);
11063 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11066 ScrollScreen(NULL, SCROLL_GO_ON);
11068 /* for backwards compatibility, the following code emulates a fixed bug that
11069 occured when pushing elements (causing elements that just made their last
11070 pushing step to already (if possible) make their first falling step in the
11071 same game frame, which is bad); this code is also needed to use the famous
11072 "spring push bug" which is used in older levels and might be wanted to be
11073 used also in newer levels, but in this case the buggy pushing code is only
11074 affecting the "spring" element and no other elements */
11076 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11078 for (i = 0; i < MAX_PLAYERS; i++)
11080 struct PlayerInfo *player = &stored_player[i];
11081 int x = player->jx;
11082 int y = player->jy;
11084 if (player->active && player->is_pushing && player->is_moving &&
11086 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11087 Feld[x][y] == EL_SPRING))
11089 ContinueMoving(x, y);
11091 /* continue moving after pushing (this is actually a bug) */
11092 if (!IS_MOVING(x, y))
11093 Stop[x][y] = FALSE;
11099 debug_print_timestamp(0, "start main loop profiling");
11102 SCAN_PLAYFIELD(x, y)
11104 ChangeCount[x][y] = 0;
11105 ChangeEvent[x][y] = -1;
11107 /* this must be handled before main playfield loop */
11108 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11111 if (MovDelay[x][y] <= 0)
11115 #if USE_NEW_SNAP_DELAY
11116 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11119 if (MovDelay[x][y] <= 0)
11122 DrawLevelField(x, y);
11124 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11130 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11132 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11133 printf("GameActions(): This should never happen!\n");
11135 ChangePage[x][y] = -1;
11139 Stop[x][y] = FALSE;
11140 if (WasJustMoving[x][y] > 0)
11141 WasJustMoving[x][y]--;
11142 if (WasJustFalling[x][y] > 0)
11143 WasJustFalling[x][y]--;
11144 if (CheckCollision[x][y] > 0)
11145 CheckCollision[x][y]--;
11146 if (CheckImpact[x][y] > 0)
11147 CheckImpact[x][y]--;
11151 /* reset finished pushing action (not done in ContinueMoving() to allow
11152 continuous pushing animation for elements with zero push delay) */
11153 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11155 ResetGfxAnimation(x, y);
11156 DrawLevelField(x, y);
11160 if (IS_BLOCKED(x, y))
11164 Blocked2Moving(x, y, &oldx, &oldy);
11165 if (!IS_MOVING(oldx, oldy))
11167 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11168 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11169 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11170 printf("GameActions(): This should never happen!\n");
11177 debug_print_timestamp(0, "- time for pre-main loop:");
11180 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11181 SCAN_PLAYFIELD(x, y)
11183 element = Feld[x][y];
11184 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11189 int element2 = element;
11190 int graphic2 = graphic;
11192 int element2 = Feld[x][y];
11193 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11195 int last_gfx_frame = GfxFrame[x][y];
11197 if (graphic_info[graphic2].anim_global_sync)
11198 GfxFrame[x][y] = FrameCounter;
11199 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11200 GfxFrame[x][y] = CustomValue[x][y];
11201 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11202 GfxFrame[x][y] = element_info[element2].collect_score;
11203 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11204 GfxFrame[x][y] = ChangeDelay[x][y];
11206 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11207 DrawLevelGraphicAnimation(x, y, graphic2);
11210 ResetGfxFrame(x, y, TRUE);
11214 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11215 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11216 ResetRandomAnimationValue(x, y);
11220 SetRandomAnimationValue(x, y);
11224 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11227 #endif // -------------------- !!! TEST ONLY !!! --------------------
11230 debug_print_timestamp(0, "- time for TEST loop: -->");
11233 SCAN_PLAYFIELD(x, y)
11235 element = Feld[x][y];
11236 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11238 ResetGfxFrame(x, y, TRUE);
11240 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11241 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11242 ResetRandomAnimationValue(x, y);
11244 SetRandomAnimationValue(x, y);
11246 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11248 if (IS_INACTIVE(element))
11250 if (IS_ANIMATED(graphic))
11251 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11256 /* this may take place after moving, so 'element' may have changed */
11257 if (IS_CHANGING(x, y) &&
11258 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11260 int page = element_info[element].event_page_nr[CE_DELAY];
11263 HandleElementChange(x, y, page);
11265 if (CAN_CHANGE(element))
11266 HandleElementChange(x, y, page);
11268 if (HAS_ACTION(element))
11269 ExecuteCustomElementAction(x, y, element, page);
11272 element = Feld[x][y];
11273 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11276 #if 0 // ---------------------------------------------------------------------
11278 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11282 element = Feld[x][y];
11283 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11285 if (IS_ANIMATED(graphic) &&
11286 !IS_MOVING(x, y) &&
11288 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11290 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11291 DrawTwinkleOnField(x, y);
11293 else if (IS_MOVING(x, y))
11294 ContinueMoving(x, y);
11301 case EL_EM_EXIT_OPEN:
11302 case EL_SP_EXIT_OPEN:
11303 case EL_STEEL_EXIT_OPEN:
11304 case EL_EM_STEEL_EXIT_OPEN:
11305 case EL_SP_TERMINAL:
11306 case EL_SP_TERMINAL_ACTIVE:
11307 case EL_EXTRA_TIME:
11308 case EL_SHIELD_NORMAL:
11309 case EL_SHIELD_DEADLY:
11310 if (IS_ANIMATED(graphic))
11311 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11314 case EL_DYNAMITE_ACTIVE:
11315 case EL_EM_DYNAMITE_ACTIVE:
11316 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11317 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11318 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11319 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11320 case EL_SP_DISK_RED_ACTIVE:
11321 CheckDynamite(x, y);
11324 case EL_AMOEBA_GROWING:
11325 AmoebeWaechst(x, y);
11328 case EL_AMOEBA_SHRINKING:
11329 AmoebaDisappearing(x, y);
11332 #if !USE_NEW_AMOEBA_CODE
11333 case EL_AMOEBA_WET:
11334 case EL_AMOEBA_DRY:
11335 case EL_AMOEBA_FULL:
11337 case EL_EMC_DRIPPER:
11338 AmoebeAbleger(x, y);
11342 case EL_GAME_OF_LIFE:
11347 case EL_EXIT_CLOSED:
11351 case EL_EM_EXIT_CLOSED:
11355 case EL_STEEL_EXIT_CLOSED:
11356 CheckExitSteel(x, y);
11359 case EL_EM_STEEL_EXIT_CLOSED:
11360 CheckExitSteelEM(x, y);
11363 case EL_SP_EXIT_CLOSED:
11367 case EL_EXPANDABLE_WALL_GROWING:
11368 case EL_EXPANDABLE_STEELWALL_GROWING:
11369 MauerWaechst(x, y);
11372 case EL_EXPANDABLE_WALL:
11373 case EL_EXPANDABLE_WALL_HORIZONTAL:
11374 case EL_EXPANDABLE_WALL_VERTICAL:
11375 case EL_EXPANDABLE_WALL_ANY:
11376 case EL_BD_EXPANDABLE_WALL:
11377 MauerAbleger(x, y);
11380 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11381 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11382 case EL_EXPANDABLE_STEELWALL_ANY:
11383 MauerAblegerStahl(x, y);
11387 CheckForDragon(x, y);
11393 case EL_ELEMENT_SNAPPING:
11394 case EL_DIAGONAL_SHRINKING:
11395 case EL_DIAGONAL_GROWING:
11398 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11400 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11405 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11406 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11411 #else // ---------------------------------------------------------------------
11413 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11417 element = Feld[x][y];
11418 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11420 if (IS_ANIMATED(graphic) &&
11421 !IS_MOVING(x, y) &&
11423 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11425 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11426 DrawTwinkleOnField(x, y);
11428 else if ((element == EL_ACID ||
11429 element == EL_EXIT_OPEN ||
11430 element == EL_EM_EXIT_OPEN ||
11431 element == EL_SP_EXIT_OPEN ||
11432 element == EL_STEEL_EXIT_OPEN ||
11433 element == EL_EM_STEEL_EXIT_OPEN ||
11434 element == EL_SP_TERMINAL ||
11435 element == EL_SP_TERMINAL_ACTIVE ||
11436 element == EL_EXTRA_TIME ||
11437 element == EL_SHIELD_NORMAL ||
11438 element == EL_SHIELD_DEADLY) &&
11439 IS_ANIMATED(graphic))
11440 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11441 else if (IS_MOVING(x, y))
11442 ContinueMoving(x, y);
11443 else if (IS_ACTIVE_BOMB(element))
11444 CheckDynamite(x, y);
11445 else if (element == EL_AMOEBA_GROWING)
11446 AmoebeWaechst(x, y);
11447 else if (element == EL_AMOEBA_SHRINKING)
11448 AmoebaDisappearing(x, y);
11450 #if !USE_NEW_AMOEBA_CODE
11451 else if (IS_AMOEBALIVE(element))
11452 AmoebeAbleger(x, y);
11455 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11457 else if (element == EL_EXIT_CLOSED)
11459 else if (element == EL_EM_EXIT_CLOSED)
11461 else if (element == EL_STEEL_EXIT_CLOSED)
11462 CheckExitSteel(x, y);
11463 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11464 CheckExitSteelEM(x, y);
11465 else if (element == EL_SP_EXIT_CLOSED)
11467 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11468 element == EL_EXPANDABLE_STEELWALL_GROWING)
11469 MauerWaechst(x, y);
11470 else if (element == EL_EXPANDABLE_WALL ||
11471 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11472 element == EL_EXPANDABLE_WALL_VERTICAL ||
11473 element == EL_EXPANDABLE_WALL_ANY ||
11474 element == EL_BD_EXPANDABLE_WALL)
11475 MauerAbleger(x, y);
11476 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11477 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11478 element == EL_EXPANDABLE_STEELWALL_ANY)
11479 MauerAblegerStahl(x, y);
11480 else if (element == EL_FLAMES)
11481 CheckForDragon(x, y);
11482 else if (element == EL_EXPLOSION)
11483 ; /* drawing of correct explosion animation is handled separately */
11484 else if (element == EL_ELEMENT_SNAPPING ||
11485 element == EL_DIAGONAL_SHRINKING ||
11486 element == EL_DIAGONAL_GROWING)
11488 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11490 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11492 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11493 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11495 #endif // ---------------------------------------------------------------------
11497 if (IS_BELT_ACTIVE(element))
11498 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11500 if (game.magic_wall_active)
11502 int jx = local_player->jx, jy = local_player->jy;
11504 /* play the element sound at the position nearest to the player */
11505 if ((element == EL_MAGIC_WALL_FULL ||
11506 element == EL_MAGIC_WALL_ACTIVE ||
11507 element == EL_MAGIC_WALL_EMPTYING ||
11508 element == EL_BD_MAGIC_WALL_FULL ||
11509 element == EL_BD_MAGIC_WALL_ACTIVE ||
11510 element == EL_BD_MAGIC_WALL_EMPTYING ||
11511 element == EL_DC_MAGIC_WALL_FULL ||
11512 element == EL_DC_MAGIC_WALL_ACTIVE ||
11513 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11514 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11523 debug_print_timestamp(0, "- time for MAIN loop: -->");
11526 #if USE_NEW_AMOEBA_CODE
11527 /* new experimental amoeba growth stuff */
11528 if (!(FrameCounter % 8))
11530 static unsigned long random = 1684108901;
11532 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11534 x = RND(lev_fieldx);
11535 y = RND(lev_fieldy);
11536 element = Feld[x][y];
11538 if (!IS_PLAYER(x,y) &&
11539 (element == EL_EMPTY ||
11540 CAN_GROW_INTO(element) ||
11541 element == EL_QUICKSAND_EMPTY ||
11542 element == EL_QUICKSAND_FAST_EMPTY ||
11543 element == EL_ACID_SPLASH_LEFT ||
11544 element == EL_ACID_SPLASH_RIGHT))
11546 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11547 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11548 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11549 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11550 Feld[x][y] = EL_AMOEBA_DROP;
11553 random = random * 129 + 1;
11559 if (game.explosions_delayed)
11562 game.explosions_delayed = FALSE;
11564 SCAN_PLAYFIELD(x, y)
11566 element = Feld[x][y];
11568 if (ExplodeField[x][y])
11569 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11570 else if (element == EL_EXPLOSION)
11571 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11573 ExplodeField[x][y] = EX_TYPE_NONE;
11576 game.explosions_delayed = TRUE;
11579 if (game.magic_wall_active)
11581 if (!(game.magic_wall_time_left % 4))
11583 int element = Feld[magic_wall_x][magic_wall_y];
11585 if (element == EL_BD_MAGIC_WALL_FULL ||
11586 element == EL_BD_MAGIC_WALL_ACTIVE ||
11587 element == EL_BD_MAGIC_WALL_EMPTYING)
11588 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11589 else if (element == EL_DC_MAGIC_WALL_FULL ||
11590 element == EL_DC_MAGIC_WALL_ACTIVE ||
11591 element == EL_DC_MAGIC_WALL_EMPTYING)
11592 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11594 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11597 if (game.magic_wall_time_left > 0)
11599 game.magic_wall_time_left--;
11600 if (!game.magic_wall_time_left)
11602 SCAN_PLAYFIELD(x, y)
11604 element = Feld[x][y];
11606 if (element == EL_MAGIC_WALL_ACTIVE ||
11607 element == EL_MAGIC_WALL_FULL)
11609 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11610 DrawLevelField(x, y);
11612 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11613 element == EL_BD_MAGIC_WALL_FULL)
11615 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11616 DrawLevelField(x, y);
11618 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11619 element == EL_DC_MAGIC_WALL_FULL)
11621 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11622 DrawLevelField(x, y);
11626 game.magic_wall_active = FALSE;
11631 if (game.light_time_left > 0)
11633 game.light_time_left--;
11635 if (game.light_time_left == 0)
11636 RedrawAllLightSwitchesAndInvisibleElements();
11639 if (game.timegate_time_left > 0)
11641 game.timegate_time_left--;
11643 if (game.timegate_time_left == 0)
11644 CloseAllOpenTimegates();
11647 if (game.lenses_time_left > 0)
11649 game.lenses_time_left--;
11651 if (game.lenses_time_left == 0)
11652 RedrawAllInvisibleElementsForLenses();
11655 if (game.magnify_time_left > 0)
11657 game.magnify_time_left--;
11659 if (game.magnify_time_left == 0)
11660 RedrawAllInvisibleElementsForMagnifier();
11663 for (i = 0; i < MAX_PLAYERS; i++)
11665 struct PlayerInfo *player = &stored_player[i];
11667 if (SHIELD_ON(player))
11669 if (player->shield_deadly_time_left)
11670 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11671 else if (player->shield_normal_time_left)
11672 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11679 PlayAllPlayersSound();
11681 if (options.debug) /* calculate frames per second */
11683 static unsigned long fps_counter = 0;
11684 static int fps_frames = 0;
11685 unsigned long fps_delay_ms = Counter() - fps_counter;
11689 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11691 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11694 fps_counter = Counter();
11697 redraw_mask |= REDRAW_FPS;
11700 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11702 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11704 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11706 local_player->show_envelope = 0;
11710 debug_print_timestamp(0, "stop main loop profiling ");
11711 printf("----------------------------------------------------------\n");
11714 /* use random number generator in every frame to make it less predictable */
11715 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11719 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11721 int min_x = x, min_y = y, max_x = x, max_y = y;
11724 for (i = 0; i < MAX_PLAYERS; i++)
11726 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11728 if (!stored_player[i].active || &stored_player[i] == player)
11731 min_x = MIN(min_x, jx);
11732 min_y = MIN(min_y, jy);
11733 max_x = MAX(max_x, jx);
11734 max_y = MAX(max_y, jy);
11737 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11740 static boolean AllPlayersInVisibleScreen()
11744 for (i = 0; i < MAX_PLAYERS; i++)
11746 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11748 if (!stored_player[i].active)
11751 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11758 void ScrollLevel(int dx, int dy)
11761 static Bitmap *bitmap_db_field2 = NULL;
11762 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11769 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11770 /* only horizontal XOR vertical scroll direction allowed */
11771 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11776 if (bitmap_db_field2 == NULL)
11777 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11779 /* needed when blitting directly to same bitmap -- should not be needed with
11780 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11781 BlitBitmap(drawto_field, bitmap_db_field2,
11782 FX + TILEX * (dx == -1) - softscroll_offset,
11783 FY + TILEY * (dy == -1) - softscroll_offset,
11784 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11785 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11786 FX + TILEX * (dx == 1) - softscroll_offset,
11787 FY + TILEY * (dy == 1) - softscroll_offset);
11788 BlitBitmap(bitmap_db_field2, drawto_field,
11789 FX + TILEX * (dx == 1) - softscroll_offset,
11790 FY + TILEY * (dy == 1) - softscroll_offset,
11791 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11792 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11793 FX + TILEX * (dx == 1) - softscroll_offset,
11794 FY + TILEY * (dy == 1) - softscroll_offset);
11799 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11800 int xsize = (BX2 - BX1 + 1);
11801 int ysize = (BY2 - BY1 + 1);
11802 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11803 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11804 int step = (start < end ? +1 : -1);
11806 for (i = start; i != end; i += step)
11808 BlitBitmap(drawto_field, drawto_field,
11809 FX + TILEX * (dx != 0 ? i + step : 0),
11810 FY + TILEY * (dy != 0 ? i + step : 0),
11811 TILEX * (dx != 0 ? 1 : xsize),
11812 TILEY * (dy != 0 ? 1 : ysize),
11813 FX + TILEX * (dx != 0 ? i : 0),
11814 FY + TILEY * (dy != 0 ? i : 0));
11819 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11821 BlitBitmap(drawto_field, drawto_field,
11822 FX + TILEX * (dx == -1) - softscroll_offset,
11823 FY + TILEY * (dy == -1) - softscroll_offset,
11824 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11825 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11826 FX + TILEX * (dx == 1) - softscroll_offset,
11827 FY + TILEY * (dy == 1) - softscroll_offset);
11833 x = (dx == 1 ? BX1 : BX2);
11834 for (y = BY1; y <= BY2; y++)
11835 DrawScreenField(x, y);
11840 y = (dy == 1 ? BY1 : BY2);
11841 for (x = BX1; x <= BX2; x++)
11842 DrawScreenField(x, y);
11845 redraw_mask |= REDRAW_FIELD;
11848 static boolean canFallDown(struct PlayerInfo *player)
11850 int jx = player->jx, jy = player->jy;
11852 return (IN_LEV_FIELD(jx, jy + 1) &&
11853 (IS_FREE(jx, jy + 1) ||
11854 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11855 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11856 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11859 static boolean canPassField(int x, int y, int move_dir)
11861 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11862 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11863 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11864 int nextx = x + dx;
11865 int nexty = y + dy;
11866 int element = Feld[x][y];
11868 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11869 !CAN_MOVE(element) &&
11870 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11871 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11872 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11875 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11877 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11878 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11879 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11883 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11884 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11885 (IS_DIGGABLE(Feld[newx][newy]) ||
11886 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11887 canPassField(newx, newy, move_dir)));
11890 static void CheckGravityMovement(struct PlayerInfo *player)
11892 #if USE_PLAYER_GRAVITY
11893 if (player->gravity && !player->programmed_action)
11895 if (game.gravity && !player->programmed_action)
11898 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11899 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11900 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11901 int jx = player->jx, jy = player->jy;
11902 boolean player_is_moving_to_valid_field =
11903 (!player_is_snapping &&
11904 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11905 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11906 boolean player_can_fall_down = canFallDown(player);
11908 if (player_can_fall_down &&
11909 !player_is_moving_to_valid_field)
11910 player->programmed_action = MV_DOWN;
11914 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11916 return CheckGravityMovement(player);
11918 #if USE_PLAYER_GRAVITY
11919 if (player->gravity && !player->programmed_action)
11921 if (game.gravity && !player->programmed_action)
11924 int jx = player->jx, jy = player->jy;
11925 boolean field_under_player_is_free =
11926 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11927 boolean player_is_standing_on_valid_field =
11928 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11929 (IS_WALKABLE(Feld[jx][jy]) &&
11930 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11932 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11933 player->programmed_action = MV_DOWN;
11938 MovePlayerOneStep()
11939 -----------------------------------------------------------------------------
11940 dx, dy: direction (non-diagonal) to try to move the player to
11941 real_dx, real_dy: direction as read from input device (can be diagonal)
11944 boolean MovePlayerOneStep(struct PlayerInfo *player,
11945 int dx, int dy, int real_dx, int real_dy)
11947 int jx = player->jx, jy = player->jy;
11948 int new_jx = jx + dx, new_jy = jy + dy;
11949 #if !USE_FIXED_DONT_RUN_INTO
11953 boolean player_can_move = !player->cannot_move;
11955 if (!player->active || (!dx && !dy))
11956 return MP_NO_ACTION;
11958 player->MovDir = (dx < 0 ? MV_LEFT :
11959 dx > 0 ? MV_RIGHT :
11961 dy > 0 ? MV_DOWN : MV_NONE);
11963 if (!IN_LEV_FIELD(new_jx, new_jy))
11964 return MP_NO_ACTION;
11966 if (!player_can_move)
11968 if (player->MovPos == 0)
11970 player->is_moving = FALSE;
11971 player->is_digging = FALSE;
11972 player->is_collecting = FALSE;
11973 player->is_snapping = FALSE;
11974 player->is_pushing = FALSE;
11979 if (!options.network && game.centered_player_nr == -1 &&
11980 !AllPlayersInSight(player, new_jx, new_jy))
11981 return MP_NO_ACTION;
11983 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11984 return MP_NO_ACTION;
11987 #if !USE_FIXED_DONT_RUN_INTO
11988 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11990 /* (moved to DigField()) */
11991 if (player_can_move && DONT_RUN_INTO(element))
11993 if (element == EL_ACID && dx == 0 && dy == 1)
11995 SplashAcid(new_jx, new_jy);
11996 Feld[jx][jy] = EL_PLAYER_1;
11997 InitMovingField(jx, jy, MV_DOWN);
11998 Store[jx][jy] = EL_ACID;
11999 ContinueMoving(jx, jy);
12000 BuryPlayer(player);
12003 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12009 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12010 if (can_move != MP_MOVING)
12013 /* check if DigField() has caused relocation of the player */
12014 if (player->jx != jx || player->jy != jy)
12015 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12017 StorePlayer[jx][jy] = 0;
12018 player->last_jx = jx;
12019 player->last_jy = jy;
12020 player->jx = new_jx;
12021 player->jy = new_jy;
12022 StorePlayer[new_jx][new_jy] = player->element_nr;
12024 if (player->move_delay_value_next != -1)
12026 player->move_delay_value = player->move_delay_value_next;
12027 player->move_delay_value_next = -1;
12031 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12033 player->step_counter++;
12035 PlayerVisit[jx][jy] = FrameCounter;
12037 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12038 player->is_moving = TRUE;
12042 /* should better be called in MovePlayer(), but this breaks some tapes */
12043 ScrollPlayer(player, SCROLL_INIT);
12049 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12051 int jx = player->jx, jy = player->jy;
12052 int old_jx = jx, old_jy = jy;
12053 int moved = MP_NO_ACTION;
12055 if (!player->active)
12060 if (player->MovPos == 0)
12062 player->is_moving = FALSE;
12063 player->is_digging = FALSE;
12064 player->is_collecting = FALSE;
12065 player->is_snapping = FALSE;
12066 player->is_pushing = FALSE;
12072 if (player->move_delay > 0)
12075 player->move_delay = -1; /* set to "uninitialized" value */
12077 /* store if player is automatically moved to next field */
12078 player->is_auto_moving = (player->programmed_action != MV_NONE);
12080 /* remove the last programmed player action */
12081 player->programmed_action = 0;
12083 if (player->MovPos)
12085 /* should only happen if pre-1.2 tape recordings are played */
12086 /* this is only for backward compatibility */
12088 int original_move_delay_value = player->move_delay_value;
12091 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12095 /* scroll remaining steps with finest movement resolution */
12096 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12098 while (player->MovPos)
12100 ScrollPlayer(player, SCROLL_GO_ON);
12101 ScrollScreen(NULL, SCROLL_GO_ON);
12103 AdvanceFrameAndPlayerCounters(player->index_nr);
12109 player->move_delay_value = original_move_delay_value;
12112 player->is_active = FALSE;
12114 if (player->last_move_dir & MV_HORIZONTAL)
12116 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12117 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12121 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12122 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12125 #if USE_FIXED_BORDER_RUNNING_GFX
12126 if (!moved && !player->is_active)
12128 player->is_moving = FALSE;
12129 player->is_digging = FALSE;
12130 player->is_collecting = FALSE;
12131 player->is_snapping = FALSE;
12132 player->is_pushing = FALSE;
12140 if (moved & MP_MOVING && !ScreenMovPos &&
12141 (player->index_nr == game.centered_player_nr ||
12142 game.centered_player_nr == -1))
12144 if (moved & MP_MOVING && !ScreenMovPos &&
12145 (player == local_player || !options.network))
12148 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12149 int offset = (setup.scroll_delay ? setup.scroll_delay_value : 0);
12151 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12153 /* actual player has left the screen -- scroll in that direction */
12154 if (jx != old_jx) /* player has moved horizontally */
12155 scroll_x += (jx - old_jx);
12156 else /* player has moved vertically */
12157 scroll_y += (jy - old_jy);
12161 if (jx != old_jx) /* player has moved horizontally */
12163 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12164 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12165 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12167 /* don't scroll over playfield boundaries */
12168 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12169 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12171 /* don't scroll more than one field at a time */
12172 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12174 /* don't scroll against the player's moving direction */
12175 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12176 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12177 scroll_x = old_scroll_x;
12179 else /* player has moved vertically */
12181 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12182 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12183 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12185 /* don't scroll over playfield boundaries */
12186 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12187 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12189 /* don't scroll more than one field at a time */
12190 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12192 /* don't scroll against the player's moving direction */
12193 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12194 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12195 scroll_y = old_scroll_y;
12199 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12202 if (!options.network && game.centered_player_nr == -1 &&
12203 !AllPlayersInVisibleScreen())
12205 scroll_x = old_scroll_x;
12206 scroll_y = old_scroll_y;
12210 if (!options.network && !AllPlayersInVisibleScreen())
12212 scroll_x = old_scroll_x;
12213 scroll_y = old_scroll_y;
12218 ScrollScreen(player, SCROLL_INIT);
12219 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12224 player->StepFrame = 0;
12226 if (moved & MP_MOVING)
12228 if (old_jx != jx && old_jy == jy)
12229 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12230 else if (old_jx == jx && old_jy != jy)
12231 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12233 DrawLevelField(jx, jy); /* for "crumbled sand" */
12235 player->last_move_dir = player->MovDir;
12236 player->is_moving = TRUE;
12237 player->is_snapping = FALSE;
12238 player->is_switching = FALSE;
12239 player->is_dropping = FALSE;
12240 player->is_dropping_pressed = FALSE;
12241 player->drop_pressed_delay = 0;
12244 /* should better be called here than above, but this breaks some tapes */
12245 ScrollPlayer(player, SCROLL_INIT);
12250 CheckGravityMovementWhenNotMoving(player);
12252 player->is_moving = FALSE;
12254 /* at this point, the player is allowed to move, but cannot move right now
12255 (e.g. because of something blocking the way) -- ensure that the player
12256 is also allowed to move in the next frame (in old versions before 3.1.1,
12257 the player was forced to wait again for eight frames before next try) */
12259 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12260 player->move_delay = 0; /* allow direct movement in the next frame */
12263 if (player->move_delay == -1) /* not yet initialized by DigField() */
12264 player->move_delay = player->move_delay_value;
12266 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12268 TestIfPlayerTouchesBadThing(jx, jy);
12269 TestIfPlayerTouchesCustomElement(jx, jy);
12272 if (!player->active)
12273 RemovePlayer(player);
12278 void ScrollPlayer(struct PlayerInfo *player, int mode)
12280 int jx = player->jx, jy = player->jy;
12281 int last_jx = player->last_jx, last_jy = player->last_jy;
12282 int move_stepsize = TILEX / player->move_delay_value;
12284 #if USE_NEW_PLAYER_SPEED
12285 if (!player->active)
12288 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12291 if (!player->active || player->MovPos == 0)
12295 if (mode == SCROLL_INIT)
12297 player->actual_frame_counter = FrameCounter;
12298 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12300 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12301 Feld[last_jx][last_jy] == EL_EMPTY)
12303 int last_field_block_delay = 0; /* start with no blocking at all */
12304 int block_delay_adjustment = player->block_delay_adjustment;
12306 /* if player blocks last field, add delay for exactly one move */
12307 if (player->block_last_field)
12309 last_field_block_delay += player->move_delay_value;
12311 /* when blocking enabled, prevent moving up despite gravity */
12312 #if USE_PLAYER_GRAVITY
12313 if (player->gravity && player->MovDir == MV_UP)
12314 block_delay_adjustment = -1;
12316 if (game.gravity && player->MovDir == MV_UP)
12317 block_delay_adjustment = -1;
12321 /* add block delay adjustment (also possible when not blocking) */
12322 last_field_block_delay += block_delay_adjustment;
12324 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12325 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12328 #if USE_NEW_PLAYER_SPEED
12329 if (player->MovPos != 0) /* player has not yet reached destination */
12335 else if (!FrameReached(&player->actual_frame_counter, 1))
12338 #if USE_NEW_PLAYER_SPEED
12339 if (player->MovPos != 0)
12341 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12342 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12344 /* before DrawPlayer() to draw correct player graphic for this case */
12345 if (player->MovPos == 0)
12346 CheckGravityMovement(player);
12349 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12350 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12352 /* before DrawPlayer() to draw correct player graphic for this case */
12353 if (player->MovPos == 0)
12354 CheckGravityMovement(player);
12357 if (player->MovPos == 0) /* player reached destination field */
12359 if (player->move_delay_reset_counter > 0)
12361 player->move_delay_reset_counter--;
12363 if (player->move_delay_reset_counter == 0)
12365 /* continue with normal speed after quickly moving through gate */
12366 HALVE_PLAYER_SPEED(player);
12368 /* be able to make the next move without delay */
12369 player->move_delay = 0;
12373 player->last_jx = jx;
12374 player->last_jy = jy;
12376 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12377 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12378 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12379 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12380 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12381 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12383 DrawPlayer(player); /* needed here only to cleanup last field */
12384 RemovePlayer(player);
12386 if (local_player->friends_still_needed == 0 ||
12387 IS_SP_ELEMENT(Feld[jx][jy]))
12388 PlayerWins(player);
12391 /* this breaks one level: "machine", level 000 */
12393 int move_direction = player->MovDir;
12394 int enter_side = MV_DIR_OPPOSITE(move_direction);
12395 int leave_side = move_direction;
12396 int old_jx = last_jx;
12397 int old_jy = last_jy;
12398 int old_element = Feld[old_jx][old_jy];
12399 int new_element = Feld[jx][jy];
12401 if (IS_CUSTOM_ELEMENT(old_element))
12402 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12404 player->index_bit, leave_side);
12406 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12407 CE_PLAYER_LEAVES_X,
12408 player->index_bit, leave_side);
12410 if (IS_CUSTOM_ELEMENT(new_element))
12411 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12412 player->index_bit, enter_side);
12414 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12415 CE_PLAYER_ENTERS_X,
12416 player->index_bit, enter_side);
12418 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12419 CE_MOVE_OF_X, move_direction);
12422 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12424 TestIfPlayerTouchesBadThing(jx, jy);
12425 TestIfPlayerTouchesCustomElement(jx, jy);
12427 /* needed because pushed element has not yet reached its destination,
12428 so it would trigger a change event at its previous field location */
12429 if (!player->is_pushing)
12430 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12432 if (!player->active)
12433 RemovePlayer(player);
12436 if (!local_player->LevelSolved && level.use_step_counter)
12446 if (TimeLeft <= 10 && setup.time_limit)
12447 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12450 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12452 DisplayGameControlValues();
12454 DrawGameValue_Time(TimeLeft);
12457 if (!TimeLeft && setup.time_limit)
12458 for (i = 0; i < MAX_PLAYERS; i++)
12459 KillPlayer(&stored_player[i]);
12462 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12464 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12466 DisplayGameControlValues();
12469 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12470 DrawGameValue_Time(TimePlayed);
12474 if (tape.single_step && tape.recording && !tape.pausing &&
12475 !player->programmed_action)
12476 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12480 void ScrollScreen(struct PlayerInfo *player, int mode)
12482 static unsigned long screen_frame_counter = 0;
12484 if (mode == SCROLL_INIT)
12486 /* set scrolling step size according to actual player's moving speed */
12487 ScrollStepSize = TILEX / player->move_delay_value;
12489 screen_frame_counter = FrameCounter;
12490 ScreenMovDir = player->MovDir;
12491 ScreenMovPos = player->MovPos;
12492 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12495 else if (!FrameReached(&screen_frame_counter, 1))
12500 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12501 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12502 redraw_mask |= REDRAW_FIELD;
12505 ScreenMovDir = MV_NONE;
12508 void TestIfPlayerTouchesCustomElement(int x, int y)
12510 static int xy[4][2] =
12517 static int trigger_sides[4][2] =
12519 /* center side border side */
12520 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12521 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12522 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12523 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12525 static int touch_dir[4] =
12527 MV_LEFT | MV_RIGHT,
12532 int center_element = Feld[x][y]; /* should always be non-moving! */
12535 for (i = 0; i < NUM_DIRECTIONS; i++)
12537 int xx = x + xy[i][0];
12538 int yy = y + xy[i][1];
12539 int center_side = trigger_sides[i][0];
12540 int border_side = trigger_sides[i][1];
12541 int border_element;
12543 if (!IN_LEV_FIELD(xx, yy))
12546 if (IS_PLAYER(x, y))
12548 struct PlayerInfo *player = PLAYERINFO(x, y);
12550 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12551 border_element = Feld[xx][yy]; /* may be moving! */
12552 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12553 border_element = Feld[xx][yy];
12554 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12555 border_element = MovingOrBlocked2Element(xx, yy);
12557 continue; /* center and border element do not touch */
12559 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12560 player->index_bit, border_side);
12561 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12562 CE_PLAYER_TOUCHES_X,
12563 player->index_bit, border_side);
12565 else if (IS_PLAYER(xx, yy))
12567 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12569 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12571 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12572 continue; /* center and border element do not touch */
12575 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12576 player->index_bit, center_side);
12577 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12578 CE_PLAYER_TOUCHES_X,
12579 player->index_bit, center_side);
12585 #if USE_ELEMENT_TOUCHING_BUGFIX
12587 void TestIfElementTouchesCustomElement(int x, int y)
12589 static int xy[4][2] =
12596 static int trigger_sides[4][2] =
12598 /* center side border side */
12599 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12600 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12601 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12602 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12604 static int touch_dir[4] =
12606 MV_LEFT | MV_RIGHT,
12611 boolean change_center_element = FALSE;
12612 int center_element = Feld[x][y]; /* should always be non-moving! */
12613 int border_element_old[NUM_DIRECTIONS];
12616 for (i = 0; i < NUM_DIRECTIONS; i++)
12618 int xx = x + xy[i][0];
12619 int yy = y + xy[i][1];
12620 int border_element;
12622 border_element_old[i] = -1;
12624 if (!IN_LEV_FIELD(xx, yy))
12627 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12628 border_element = Feld[xx][yy]; /* may be moving! */
12629 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12630 border_element = Feld[xx][yy];
12631 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12632 border_element = MovingOrBlocked2Element(xx, yy);
12634 continue; /* center and border element do not touch */
12636 border_element_old[i] = border_element;
12639 for (i = 0; i < NUM_DIRECTIONS; i++)
12641 int xx = x + xy[i][0];
12642 int yy = y + xy[i][1];
12643 int center_side = trigger_sides[i][0];
12644 int border_element = border_element_old[i];
12646 if (border_element == -1)
12649 /* check for change of border element */
12650 CheckElementChangeBySide(xx, yy, border_element, center_element,
12651 CE_TOUCHING_X, center_side);
12654 for (i = 0; i < NUM_DIRECTIONS; i++)
12656 int border_side = trigger_sides[i][1];
12657 int border_element = border_element_old[i];
12659 if (border_element == -1)
12662 /* check for change of center element (but change it only once) */
12663 if (!change_center_element)
12664 change_center_element =
12665 CheckElementChangeBySide(x, y, center_element, border_element,
12666 CE_TOUCHING_X, border_side);
12672 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12674 static int xy[4][2] =
12681 static int trigger_sides[4][2] =
12683 /* center side border side */
12684 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12685 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12686 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12687 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12689 static int touch_dir[4] =
12691 MV_LEFT | MV_RIGHT,
12696 boolean change_center_element = FALSE;
12697 int center_element = Feld[x][y]; /* should always be non-moving! */
12700 for (i = 0; i < NUM_DIRECTIONS; i++)
12702 int xx = x + xy[i][0];
12703 int yy = y + xy[i][1];
12704 int center_side = trigger_sides[i][0];
12705 int border_side = trigger_sides[i][1];
12706 int border_element;
12708 if (!IN_LEV_FIELD(xx, yy))
12711 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12712 border_element = Feld[xx][yy]; /* may be moving! */
12713 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12714 border_element = Feld[xx][yy];
12715 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12716 border_element = MovingOrBlocked2Element(xx, yy);
12718 continue; /* center and border element do not touch */
12720 /* check for change of center element (but change it only once) */
12721 if (!change_center_element)
12722 change_center_element =
12723 CheckElementChangeBySide(x, y, center_element, border_element,
12724 CE_TOUCHING_X, border_side);
12726 /* check for change of border element */
12727 CheckElementChangeBySide(xx, yy, border_element, center_element,
12728 CE_TOUCHING_X, center_side);
12734 void TestIfElementHitsCustomElement(int x, int y, int direction)
12736 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12737 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12738 int hitx = x + dx, hity = y + dy;
12739 int hitting_element = Feld[x][y];
12740 int touched_element;
12742 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12745 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12746 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12748 if (IN_LEV_FIELD(hitx, hity))
12750 int opposite_direction = MV_DIR_OPPOSITE(direction);
12751 int hitting_side = direction;
12752 int touched_side = opposite_direction;
12753 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12754 MovDir[hitx][hity] != direction ||
12755 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12761 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12762 CE_HITTING_X, touched_side);
12764 CheckElementChangeBySide(hitx, hity, touched_element,
12765 hitting_element, CE_HIT_BY_X, hitting_side);
12767 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12768 CE_HIT_BY_SOMETHING, opposite_direction);
12772 /* "hitting something" is also true when hitting the playfield border */
12773 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12774 CE_HITTING_SOMETHING, direction);
12778 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12780 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12781 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12782 int hitx = x + dx, hity = y + dy;
12783 int hitting_element = Feld[x][y];
12784 int touched_element;
12786 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12787 !IS_FREE(hitx, hity) &&
12788 (!IS_MOVING(hitx, hity) ||
12789 MovDir[hitx][hity] != direction ||
12790 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12793 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12797 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12801 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12802 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12804 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12805 EP_CAN_SMASH_EVERYTHING, direction);
12807 if (IN_LEV_FIELD(hitx, hity))
12809 int opposite_direction = MV_DIR_OPPOSITE(direction);
12810 int hitting_side = direction;
12811 int touched_side = opposite_direction;
12813 int touched_element = MovingOrBlocked2Element(hitx, hity);
12816 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12817 MovDir[hitx][hity] != direction ||
12818 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12827 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12828 CE_SMASHED_BY_SOMETHING, opposite_direction);
12830 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12831 CE_OTHER_IS_SMASHING, touched_side);
12833 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12834 CE_OTHER_GETS_SMASHED, hitting_side);
12840 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12842 int i, kill_x = -1, kill_y = -1;
12844 int bad_element = -1;
12845 static int test_xy[4][2] =
12852 static int test_dir[4] =
12860 for (i = 0; i < NUM_DIRECTIONS; i++)
12862 int test_x, test_y, test_move_dir, test_element;
12864 test_x = good_x + test_xy[i][0];
12865 test_y = good_y + test_xy[i][1];
12867 if (!IN_LEV_FIELD(test_x, test_y))
12871 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12873 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12875 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12876 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12878 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12879 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12883 bad_element = test_element;
12889 if (kill_x != -1 || kill_y != -1)
12891 if (IS_PLAYER(good_x, good_y))
12893 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12895 if (player->shield_deadly_time_left > 0 &&
12896 !IS_INDESTRUCTIBLE(bad_element))
12897 Bang(kill_x, kill_y);
12898 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12899 KillPlayer(player);
12902 Bang(good_x, good_y);
12906 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12908 int i, kill_x = -1, kill_y = -1;
12909 int bad_element = Feld[bad_x][bad_y];
12910 static int test_xy[4][2] =
12917 static int touch_dir[4] =
12919 MV_LEFT | MV_RIGHT,
12924 static int test_dir[4] =
12932 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12935 for (i = 0; i < NUM_DIRECTIONS; i++)
12937 int test_x, test_y, test_move_dir, test_element;
12939 test_x = bad_x + test_xy[i][0];
12940 test_y = bad_y + test_xy[i][1];
12941 if (!IN_LEV_FIELD(test_x, test_y))
12945 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12947 test_element = Feld[test_x][test_y];
12949 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12950 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12952 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12953 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12955 /* good thing is player or penguin that does not move away */
12956 if (IS_PLAYER(test_x, test_y))
12958 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12960 if (bad_element == EL_ROBOT && player->is_moving)
12961 continue; /* robot does not kill player if he is moving */
12963 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12965 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12966 continue; /* center and border element do not touch */
12973 else if (test_element == EL_PENGUIN)
12982 if (kill_x != -1 || kill_y != -1)
12984 if (IS_PLAYER(kill_x, kill_y))
12986 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12988 if (player->shield_deadly_time_left > 0 &&
12989 !IS_INDESTRUCTIBLE(bad_element))
12990 Bang(bad_x, bad_y);
12991 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12992 KillPlayer(player);
12995 Bang(kill_x, kill_y);
12999 void TestIfPlayerTouchesBadThing(int x, int y)
13001 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13004 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13006 TestIfGoodThingHitsBadThing(x, y, move_dir);
13009 void TestIfBadThingTouchesPlayer(int x, int y)
13011 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13014 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13016 TestIfBadThingHitsGoodThing(x, y, move_dir);
13019 void TestIfFriendTouchesBadThing(int x, int y)
13021 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13024 void TestIfBadThingTouchesFriend(int x, int y)
13026 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13029 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13031 int i, kill_x = bad_x, kill_y = bad_y;
13032 static int xy[4][2] =
13040 for (i = 0; i < NUM_DIRECTIONS; i++)
13044 x = bad_x + xy[i][0];
13045 y = bad_y + xy[i][1];
13046 if (!IN_LEV_FIELD(x, y))
13049 element = Feld[x][y];
13050 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13051 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13059 if (kill_x != bad_x || kill_y != bad_y)
13060 Bang(bad_x, bad_y);
13063 void KillPlayer(struct PlayerInfo *player)
13065 int jx = player->jx, jy = player->jy;
13067 if (!player->active)
13070 /* the following code was introduced to prevent an infinite loop when calling
13072 -> CheckTriggeredElementChangeExt()
13073 -> ExecuteCustomElementAction()
13075 -> (infinitely repeating the above sequence of function calls)
13076 which occurs when killing the player while having a CE with the setting
13077 "kill player X when explosion of <player X>"; the solution using a new
13078 field "player->killed" was chosen for backwards compatibility, although
13079 clever use of the fields "player->active" etc. would probably also work */
13081 if (player->killed)
13085 player->killed = TRUE;
13087 /* remove accessible field at the player's position */
13088 Feld[jx][jy] = EL_EMPTY;
13090 /* deactivate shield (else Bang()/Explode() would not work right) */
13091 player->shield_normal_time_left = 0;
13092 player->shield_deadly_time_left = 0;
13095 BuryPlayer(player);
13098 static void KillPlayerUnlessEnemyProtected(int x, int y)
13100 if (!PLAYER_ENEMY_PROTECTED(x, y))
13101 KillPlayer(PLAYERINFO(x, y));
13104 static void KillPlayerUnlessExplosionProtected(int x, int y)
13106 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13107 KillPlayer(PLAYERINFO(x, y));
13110 void BuryPlayer(struct PlayerInfo *player)
13112 int jx = player->jx, jy = player->jy;
13114 if (!player->active)
13117 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13118 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13120 player->GameOver = TRUE;
13121 RemovePlayer(player);
13124 void RemovePlayer(struct PlayerInfo *player)
13126 int jx = player->jx, jy = player->jy;
13127 int i, found = FALSE;
13129 player->present = FALSE;
13130 player->active = FALSE;
13132 if (!ExplodeField[jx][jy])
13133 StorePlayer[jx][jy] = 0;
13135 if (player->is_moving)
13136 DrawLevelField(player->last_jx, player->last_jy);
13138 for (i = 0; i < MAX_PLAYERS; i++)
13139 if (stored_player[i].active)
13143 AllPlayersGone = TRUE;
13149 #if USE_NEW_SNAP_DELAY
13150 static void setFieldForSnapping(int x, int y, int element, int direction)
13152 struct ElementInfo *ei = &element_info[element];
13153 int direction_bit = MV_DIR_TO_BIT(direction);
13154 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13155 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13156 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13158 Feld[x][y] = EL_ELEMENT_SNAPPING;
13159 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13161 ResetGfxAnimation(x, y);
13163 GfxElement[x][y] = element;
13164 GfxAction[x][y] = action;
13165 GfxDir[x][y] = direction;
13166 GfxFrame[x][y] = -1;
13171 =============================================================================
13172 checkDiagonalPushing()
13173 -----------------------------------------------------------------------------
13174 check if diagonal input device direction results in pushing of object
13175 (by checking if the alternative direction is walkable, diggable, ...)
13176 =============================================================================
13179 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13180 int x, int y, int real_dx, int real_dy)
13182 int jx, jy, dx, dy, xx, yy;
13184 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13187 /* diagonal direction: check alternative direction */
13192 xx = jx + (dx == 0 ? real_dx : 0);
13193 yy = jy + (dy == 0 ? real_dy : 0);
13195 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13199 =============================================================================
13201 -----------------------------------------------------------------------------
13202 x, y: field next to player (non-diagonal) to try to dig to
13203 real_dx, real_dy: direction as read from input device (can be diagonal)
13204 =============================================================================
13207 int DigField(struct PlayerInfo *player,
13208 int oldx, int oldy, int x, int y,
13209 int real_dx, int real_dy, int mode)
13211 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13212 boolean player_was_pushing = player->is_pushing;
13213 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13214 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13215 int jx = oldx, jy = oldy;
13216 int dx = x - jx, dy = y - jy;
13217 int nextx = x + dx, nexty = y + dy;
13218 int move_direction = (dx == -1 ? MV_LEFT :
13219 dx == +1 ? MV_RIGHT :
13221 dy == +1 ? MV_DOWN : MV_NONE);
13222 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13223 int dig_side = MV_DIR_OPPOSITE(move_direction);
13224 int old_element = Feld[jx][jy];
13225 #if USE_FIXED_DONT_RUN_INTO
13226 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13232 if (is_player) /* function can also be called by EL_PENGUIN */
13234 if (player->MovPos == 0)
13236 player->is_digging = FALSE;
13237 player->is_collecting = FALSE;
13240 if (player->MovPos == 0) /* last pushing move finished */
13241 player->is_pushing = FALSE;
13243 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13245 player->is_switching = FALSE;
13246 player->push_delay = -1;
13248 return MP_NO_ACTION;
13252 #if !USE_FIXED_DONT_RUN_INTO
13253 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13254 return MP_NO_ACTION;
13257 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13258 old_element = Back[jx][jy];
13260 /* in case of element dropped at player position, check background */
13261 else if (Back[jx][jy] != EL_EMPTY &&
13262 game.engine_version >= VERSION_IDENT(2,2,0,0))
13263 old_element = Back[jx][jy];
13265 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13266 return MP_NO_ACTION; /* field has no opening in this direction */
13268 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13269 return MP_NO_ACTION; /* field has no opening in this direction */
13271 #if USE_FIXED_DONT_RUN_INTO
13272 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13276 Feld[jx][jy] = player->artwork_element;
13277 InitMovingField(jx, jy, MV_DOWN);
13278 Store[jx][jy] = EL_ACID;
13279 ContinueMoving(jx, jy);
13280 BuryPlayer(player);
13282 return MP_DONT_RUN_INTO;
13286 #if USE_FIXED_DONT_RUN_INTO
13287 if (player_can_move && DONT_RUN_INTO(element))
13289 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13291 return MP_DONT_RUN_INTO;
13295 #if USE_FIXED_DONT_RUN_INTO
13296 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13297 return MP_NO_ACTION;
13300 #if !USE_FIXED_DONT_RUN_INTO
13301 element = Feld[x][y];
13304 collect_count = element_info[element].collect_count_initial;
13306 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13307 return MP_NO_ACTION;
13309 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13310 player_can_move = player_can_move_or_snap;
13312 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13313 game.engine_version >= VERSION_IDENT(2,2,0,0))
13315 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13316 player->index_bit, dig_side);
13317 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13318 player->index_bit, dig_side);
13320 if (element == EL_DC_LANDMINE)
13323 if (Feld[x][y] != element) /* field changed by snapping */
13326 return MP_NO_ACTION;
13329 #if USE_PLAYER_GRAVITY
13330 if (player->gravity && is_player && !player->is_auto_moving &&
13331 canFallDown(player) && move_direction != MV_DOWN &&
13332 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13333 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13335 if (game.gravity && is_player && !player->is_auto_moving &&
13336 canFallDown(player) && move_direction != MV_DOWN &&
13337 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13338 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13341 if (player_can_move &&
13342 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13344 int sound_element = SND_ELEMENT(element);
13345 int sound_action = ACTION_WALKING;
13347 if (IS_RND_GATE(element))
13349 if (!player->key[RND_GATE_NR(element)])
13350 return MP_NO_ACTION;
13352 else if (IS_RND_GATE_GRAY(element))
13354 if (!player->key[RND_GATE_GRAY_NR(element)])
13355 return MP_NO_ACTION;
13357 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13359 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13360 return MP_NO_ACTION;
13362 else if (element == EL_EXIT_OPEN ||
13363 element == EL_EM_EXIT_OPEN ||
13364 element == EL_STEEL_EXIT_OPEN ||
13365 element == EL_EM_STEEL_EXIT_OPEN ||
13366 element == EL_SP_EXIT_OPEN ||
13367 element == EL_SP_EXIT_OPENING)
13369 sound_action = ACTION_PASSING; /* player is passing exit */
13371 else if (element == EL_EMPTY)
13373 sound_action = ACTION_MOVING; /* nothing to walk on */
13376 /* play sound from background or player, whatever is available */
13377 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13378 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13380 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13382 else if (player_can_move &&
13383 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13385 if (!ACCESS_FROM(element, opposite_direction))
13386 return MP_NO_ACTION; /* field not accessible from this direction */
13388 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13389 return MP_NO_ACTION;
13391 if (IS_EM_GATE(element))
13393 if (!player->key[EM_GATE_NR(element)])
13394 return MP_NO_ACTION;
13396 else if (IS_EM_GATE_GRAY(element))
13398 if (!player->key[EM_GATE_GRAY_NR(element)])
13399 return MP_NO_ACTION;
13401 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13403 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13404 return MP_NO_ACTION;
13406 else if (IS_EMC_GATE(element))
13408 if (!player->key[EMC_GATE_NR(element)])
13409 return MP_NO_ACTION;
13411 else if (IS_EMC_GATE_GRAY(element))
13413 if (!player->key[EMC_GATE_GRAY_NR(element)])
13414 return MP_NO_ACTION;
13416 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13418 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13419 return MP_NO_ACTION;
13421 else if (element == EL_DC_GATE_WHITE ||
13422 element == EL_DC_GATE_WHITE_GRAY ||
13423 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13425 if (player->num_white_keys == 0)
13426 return MP_NO_ACTION;
13428 player->num_white_keys--;
13430 else if (IS_SP_PORT(element))
13432 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13433 element == EL_SP_GRAVITY_PORT_RIGHT ||
13434 element == EL_SP_GRAVITY_PORT_UP ||
13435 element == EL_SP_GRAVITY_PORT_DOWN)
13436 #if USE_PLAYER_GRAVITY
13437 player->gravity = !player->gravity;
13439 game.gravity = !game.gravity;
13441 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13442 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13443 element == EL_SP_GRAVITY_ON_PORT_UP ||
13444 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13445 #if USE_PLAYER_GRAVITY
13446 player->gravity = TRUE;
13448 game.gravity = TRUE;
13450 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13451 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13452 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13453 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13454 #if USE_PLAYER_GRAVITY
13455 player->gravity = FALSE;
13457 game.gravity = FALSE;
13461 /* automatically move to the next field with double speed */
13462 player->programmed_action = move_direction;
13464 if (player->move_delay_reset_counter == 0)
13466 player->move_delay_reset_counter = 2; /* two double speed steps */
13468 DOUBLE_PLAYER_SPEED(player);
13471 PlayLevelSoundAction(x, y, ACTION_PASSING);
13473 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13477 if (mode != DF_SNAP)
13479 GfxElement[x][y] = GFX_ELEMENT(element);
13480 player->is_digging = TRUE;
13483 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13485 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13486 player->index_bit, dig_side);
13488 if (mode == DF_SNAP)
13490 #if USE_NEW_SNAP_DELAY
13491 if (level.block_snap_field)
13492 setFieldForSnapping(x, y, element, move_direction);
13494 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13496 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13499 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13500 player->index_bit, dig_side);
13503 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13507 if (is_player && mode != DF_SNAP)
13509 GfxElement[x][y] = element;
13510 player->is_collecting = TRUE;
13513 if (element == EL_SPEED_PILL)
13515 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13517 else if (element == EL_EXTRA_TIME && level.time > 0)
13519 TimeLeft += level.extra_time;
13522 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13524 DisplayGameControlValues();
13526 DrawGameValue_Time(TimeLeft);
13529 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13531 player->shield_normal_time_left += level.shield_normal_time;
13532 if (element == EL_SHIELD_DEADLY)
13533 player->shield_deadly_time_left += level.shield_deadly_time;
13535 else if (element == EL_DYNAMITE ||
13536 element == EL_EM_DYNAMITE ||
13537 element == EL_SP_DISK_RED)
13539 if (player->inventory_size < MAX_INVENTORY_SIZE)
13540 player->inventory_element[player->inventory_size++] = element;
13542 DrawGameDoorValues();
13544 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13546 player->dynabomb_count++;
13547 player->dynabombs_left++;
13549 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13551 player->dynabomb_size++;
13553 else if (element == EL_DYNABOMB_INCREASE_POWER)
13555 player->dynabomb_xl = TRUE;
13557 else if (IS_KEY(element))
13559 player->key[KEY_NR(element)] = TRUE;
13561 DrawGameDoorValues();
13563 else if (element == EL_DC_KEY_WHITE)
13565 player->num_white_keys++;
13567 /* display white keys? */
13568 /* DrawGameDoorValues(); */
13570 else if (IS_ENVELOPE(element))
13572 player->show_envelope = element;
13574 else if (element == EL_EMC_LENSES)
13576 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13578 RedrawAllInvisibleElementsForLenses();
13580 else if (element == EL_EMC_MAGNIFIER)
13582 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13584 RedrawAllInvisibleElementsForMagnifier();
13586 else if (IS_DROPPABLE(element) ||
13587 IS_THROWABLE(element)) /* can be collected and dropped */
13591 if (collect_count == 0)
13592 player->inventory_infinite_element = element;
13594 for (i = 0; i < collect_count; i++)
13595 if (player->inventory_size < MAX_INVENTORY_SIZE)
13596 player->inventory_element[player->inventory_size++] = element;
13598 DrawGameDoorValues();
13600 else if (collect_count > 0)
13602 local_player->gems_still_needed -= collect_count;
13603 if (local_player->gems_still_needed < 0)
13604 local_player->gems_still_needed = 0;
13607 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13609 DisplayGameControlValues();
13611 DrawGameValue_Emeralds(local_player->gems_still_needed);
13615 RaiseScoreElement(element);
13616 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13619 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13620 player->index_bit, dig_side);
13622 if (mode == DF_SNAP)
13624 #if USE_NEW_SNAP_DELAY
13625 if (level.block_snap_field)
13626 setFieldForSnapping(x, y, element, move_direction);
13628 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13630 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13633 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13634 player->index_bit, dig_side);
13637 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13639 if (mode == DF_SNAP && element != EL_BD_ROCK)
13640 return MP_NO_ACTION;
13642 if (CAN_FALL(element) && dy)
13643 return MP_NO_ACTION;
13645 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13646 !(element == EL_SPRING && level.use_spring_bug))
13647 return MP_NO_ACTION;
13649 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13650 ((move_direction & MV_VERTICAL &&
13651 ((element_info[element].move_pattern & MV_LEFT &&
13652 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13653 (element_info[element].move_pattern & MV_RIGHT &&
13654 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13655 (move_direction & MV_HORIZONTAL &&
13656 ((element_info[element].move_pattern & MV_UP &&
13657 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13658 (element_info[element].move_pattern & MV_DOWN &&
13659 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13660 return MP_NO_ACTION;
13662 /* do not push elements already moving away faster than player */
13663 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13664 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13665 return MP_NO_ACTION;
13667 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13669 if (player->push_delay_value == -1 || !player_was_pushing)
13670 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13672 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13674 if (player->push_delay_value == -1)
13675 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13677 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13679 if (!player->is_pushing)
13680 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13683 player->is_pushing = TRUE;
13684 player->is_active = TRUE;
13686 if (!(IN_LEV_FIELD(nextx, nexty) &&
13687 (IS_FREE(nextx, nexty) ||
13688 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13689 IS_SB_ELEMENT(element)))))
13690 return MP_NO_ACTION;
13692 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13693 return MP_NO_ACTION;
13695 if (player->push_delay == -1) /* new pushing; restart delay */
13696 player->push_delay = 0;
13698 if (player->push_delay < player->push_delay_value &&
13699 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13700 element != EL_SPRING && element != EL_BALLOON)
13702 /* make sure that there is no move delay before next try to push */
13703 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13704 player->move_delay = 0;
13706 return MP_NO_ACTION;
13709 if (IS_SB_ELEMENT(element))
13711 if (element == EL_SOKOBAN_FIELD_FULL)
13713 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13714 local_player->sokobanfields_still_needed++;
13717 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13719 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13720 local_player->sokobanfields_still_needed--;
13723 Feld[x][y] = EL_SOKOBAN_OBJECT;
13725 if (Back[x][y] == Back[nextx][nexty])
13726 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13727 else if (Back[x][y] != 0)
13728 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13731 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13734 if (local_player->sokobanfields_still_needed == 0 &&
13735 game.emulation == EMU_SOKOBAN)
13737 PlayerWins(player);
13739 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13743 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13745 InitMovingField(x, y, move_direction);
13746 GfxAction[x][y] = ACTION_PUSHING;
13748 if (mode == DF_SNAP)
13749 ContinueMoving(x, y);
13751 MovPos[x][y] = (dx != 0 ? dx : dy);
13753 Pushed[x][y] = TRUE;
13754 Pushed[nextx][nexty] = TRUE;
13756 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13757 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13759 player->push_delay_value = -1; /* get new value later */
13761 /* check for element change _after_ element has been pushed */
13762 if (game.use_change_when_pushing_bug)
13764 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13765 player->index_bit, dig_side);
13766 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13767 player->index_bit, dig_side);
13770 else if (IS_SWITCHABLE(element))
13772 if (PLAYER_SWITCHING(player, x, y))
13774 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13775 player->index_bit, dig_side);
13780 player->is_switching = TRUE;
13781 player->switch_x = x;
13782 player->switch_y = y;
13784 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13786 if (element == EL_ROBOT_WHEEL)
13788 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13792 DrawLevelField(x, y);
13794 else if (element == EL_SP_TERMINAL)
13798 SCAN_PLAYFIELD(xx, yy)
13800 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13802 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13803 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13806 else if (IS_BELT_SWITCH(element))
13808 ToggleBeltSwitch(x, y);
13810 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13811 element == EL_SWITCHGATE_SWITCH_DOWN ||
13812 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13813 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13815 ToggleSwitchgateSwitch(x, y);
13817 else if (element == EL_LIGHT_SWITCH ||
13818 element == EL_LIGHT_SWITCH_ACTIVE)
13820 ToggleLightSwitch(x, y);
13822 else if (element == EL_TIMEGATE_SWITCH ||
13823 element == EL_DC_TIMEGATE_SWITCH)
13825 ActivateTimegateSwitch(x, y);
13827 else if (element == EL_BALLOON_SWITCH_LEFT ||
13828 element == EL_BALLOON_SWITCH_RIGHT ||
13829 element == EL_BALLOON_SWITCH_UP ||
13830 element == EL_BALLOON_SWITCH_DOWN ||
13831 element == EL_BALLOON_SWITCH_NONE ||
13832 element == EL_BALLOON_SWITCH_ANY)
13834 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13835 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13836 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13837 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13838 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13841 else if (element == EL_LAMP)
13843 Feld[x][y] = EL_LAMP_ACTIVE;
13844 local_player->lights_still_needed--;
13846 ResetGfxAnimation(x, y);
13847 DrawLevelField(x, y);
13849 else if (element == EL_TIME_ORB_FULL)
13851 Feld[x][y] = EL_TIME_ORB_EMPTY;
13853 if (level.time > 0 || level.use_time_orb_bug)
13855 TimeLeft += level.time_orb_time;
13858 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13860 DisplayGameControlValues();
13862 DrawGameValue_Time(TimeLeft);
13866 ResetGfxAnimation(x, y);
13867 DrawLevelField(x, y);
13869 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13870 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13874 game.ball_state = !game.ball_state;
13876 SCAN_PLAYFIELD(xx, yy)
13878 int e = Feld[xx][yy];
13880 if (game.ball_state)
13882 if (e == EL_EMC_MAGIC_BALL)
13883 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13884 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13885 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13889 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13890 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13891 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13892 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13897 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13898 player->index_bit, dig_side);
13900 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13901 player->index_bit, dig_side);
13903 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13904 player->index_bit, dig_side);
13910 if (!PLAYER_SWITCHING(player, x, y))
13912 player->is_switching = TRUE;
13913 player->switch_x = x;
13914 player->switch_y = y;
13916 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13917 player->index_bit, dig_side);
13918 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13919 player->index_bit, dig_side);
13921 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13922 player->index_bit, dig_side);
13923 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13924 player->index_bit, dig_side);
13927 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13928 player->index_bit, dig_side);
13929 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13930 player->index_bit, dig_side);
13932 return MP_NO_ACTION;
13935 player->push_delay = -1;
13937 if (is_player) /* function can also be called by EL_PENGUIN */
13939 if (Feld[x][y] != element) /* really digged/collected something */
13941 player->is_collecting = !player->is_digging;
13942 player->is_active = TRUE;
13949 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13951 int jx = player->jx, jy = player->jy;
13952 int x = jx + dx, y = jy + dy;
13953 int snap_direction = (dx == -1 ? MV_LEFT :
13954 dx == +1 ? MV_RIGHT :
13956 dy == +1 ? MV_DOWN : MV_NONE);
13957 boolean can_continue_snapping = (level.continuous_snapping &&
13958 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13960 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13963 if (!player->active || !IN_LEV_FIELD(x, y))
13971 if (player->MovPos == 0)
13972 player->is_pushing = FALSE;
13974 player->is_snapping = FALSE;
13976 if (player->MovPos == 0)
13978 player->is_moving = FALSE;
13979 player->is_digging = FALSE;
13980 player->is_collecting = FALSE;
13986 #if USE_NEW_CONTINUOUS_SNAPPING
13987 /* prevent snapping with already pressed snap key when not allowed */
13988 if (player->is_snapping && !can_continue_snapping)
13991 if (player->is_snapping)
13995 player->MovDir = snap_direction;
13997 if (player->MovPos == 0)
13999 player->is_moving = FALSE;
14000 player->is_digging = FALSE;
14001 player->is_collecting = FALSE;
14004 player->is_dropping = FALSE;
14005 player->is_dropping_pressed = FALSE;
14006 player->drop_pressed_delay = 0;
14008 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14011 player->is_snapping = TRUE;
14012 player->is_active = TRUE;
14014 if (player->MovPos == 0)
14016 player->is_moving = FALSE;
14017 player->is_digging = FALSE;
14018 player->is_collecting = FALSE;
14021 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14022 DrawLevelField(player->last_jx, player->last_jy);
14024 DrawLevelField(x, y);
14029 boolean DropElement(struct PlayerInfo *player)
14031 int old_element, new_element;
14032 int dropx = player->jx, dropy = player->jy;
14033 int drop_direction = player->MovDir;
14034 int drop_side = drop_direction;
14035 int drop_element = (player->inventory_size > 0 ?
14036 player->inventory_element[player->inventory_size - 1] :
14037 player->inventory_infinite_element != EL_UNDEFINED ?
14038 player->inventory_infinite_element :
14039 player->dynabombs_left > 0 ?
14040 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14043 player->is_dropping_pressed = TRUE;
14045 /* do not drop an element on top of another element; when holding drop key
14046 pressed without moving, dropped element must move away before the next
14047 element can be dropped (this is especially important if the next element
14048 is dynamite, which can be placed on background for historical reasons) */
14049 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14052 if (IS_THROWABLE(drop_element))
14054 dropx += GET_DX_FROM_DIR(drop_direction);
14055 dropy += GET_DY_FROM_DIR(drop_direction);
14057 if (!IN_LEV_FIELD(dropx, dropy))
14061 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14062 new_element = drop_element; /* default: no change when dropping */
14064 /* check if player is active, not moving and ready to drop */
14065 if (!player->active || player->MovPos || player->drop_delay > 0)
14068 /* check if player has anything that can be dropped */
14069 if (new_element == EL_UNDEFINED)
14072 /* check if drop key was pressed long enough for EM style dynamite */
14073 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14076 /* check if anything can be dropped at the current position */
14077 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14080 /* collected custom elements can only be dropped on empty fields */
14081 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14084 if (old_element != EL_EMPTY)
14085 Back[dropx][dropy] = old_element; /* store old element on this field */
14087 ResetGfxAnimation(dropx, dropy);
14088 ResetRandomAnimationValue(dropx, dropy);
14090 if (player->inventory_size > 0 ||
14091 player->inventory_infinite_element != EL_UNDEFINED)
14093 if (player->inventory_size > 0)
14095 player->inventory_size--;
14097 DrawGameDoorValues();
14099 if (new_element == EL_DYNAMITE)
14100 new_element = EL_DYNAMITE_ACTIVE;
14101 else if (new_element == EL_EM_DYNAMITE)
14102 new_element = EL_EM_DYNAMITE_ACTIVE;
14103 else if (new_element == EL_SP_DISK_RED)
14104 new_element = EL_SP_DISK_RED_ACTIVE;
14107 Feld[dropx][dropy] = new_element;
14109 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14110 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14111 el2img(Feld[dropx][dropy]), 0);
14113 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14115 /* needed if previous element just changed to "empty" in the last frame */
14116 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14118 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14119 player->index_bit, drop_side);
14120 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14122 player->index_bit, drop_side);
14124 TestIfElementTouchesCustomElement(dropx, dropy);
14126 else /* player is dropping a dyna bomb */
14128 player->dynabombs_left--;
14130 Feld[dropx][dropy] = new_element;
14132 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14133 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14134 el2img(Feld[dropx][dropy]), 0);
14136 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14139 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14140 InitField_WithBug1(dropx, dropy, FALSE);
14142 new_element = Feld[dropx][dropy]; /* element might have changed */
14144 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14145 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14147 int move_direction, nextx, nexty;
14149 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14150 MovDir[dropx][dropy] = drop_direction;
14152 move_direction = MovDir[dropx][dropy];
14153 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14154 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14156 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14158 #if USE_FIX_IMPACT_COLLISION
14159 /* do not cause impact style collision by dropping elements that can fall */
14160 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14162 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14166 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14167 player->is_dropping = TRUE;
14169 player->drop_pressed_delay = 0;
14170 player->is_dropping_pressed = FALSE;
14172 player->drop_x = dropx;
14173 player->drop_y = dropy;
14178 /* ------------------------------------------------------------------------- */
14179 /* game sound playing functions */
14180 /* ------------------------------------------------------------------------- */
14182 static int *loop_sound_frame = NULL;
14183 static int *loop_sound_volume = NULL;
14185 void InitPlayLevelSound()
14187 int num_sounds = getSoundListSize();
14189 checked_free(loop_sound_frame);
14190 checked_free(loop_sound_volume);
14192 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14193 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14196 static void PlayLevelSound(int x, int y, int nr)
14198 int sx = SCREENX(x), sy = SCREENY(y);
14199 int volume, stereo_position;
14200 int max_distance = 8;
14201 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14203 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14204 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14207 if (!IN_LEV_FIELD(x, y) ||
14208 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14209 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14212 volume = SOUND_MAX_VOLUME;
14214 if (!IN_SCR_FIELD(sx, sy))
14216 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14217 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14219 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14222 stereo_position = (SOUND_MAX_LEFT +
14223 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14224 (SCR_FIELDX + 2 * max_distance));
14226 if (IS_LOOP_SOUND(nr))
14228 /* This assures that quieter loop sounds do not overwrite louder ones,
14229 while restarting sound volume comparison with each new game frame. */
14231 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14234 loop_sound_volume[nr] = volume;
14235 loop_sound_frame[nr] = FrameCounter;
14238 PlaySoundExt(nr, volume, stereo_position, type);
14241 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14243 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14244 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14245 y < LEVELY(BY1) ? LEVELY(BY1) :
14246 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14250 static void PlayLevelSoundAction(int x, int y, int action)
14252 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14255 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14257 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14259 if (sound_effect != SND_UNDEFINED)
14260 PlayLevelSound(x, y, sound_effect);
14263 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14266 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14268 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14269 PlayLevelSound(x, y, sound_effect);
14272 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14274 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14276 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14277 PlayLevelSound(x, y, sound_effect);
14280 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14282 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14284 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14285 StopSound(sound_effect);
14288 static void PlayLevelMusic()
14290 if (levelset.music[level_nr] != MUS_UNDEFINED)
14291 PlayMusic(levelset.music[level_nr]); /* from config file */
14293 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14296 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14298 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14299 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14300 int x = xx - 1 - offset;
14301 int y = yy - 1 - offset;
14306 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14310 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14314 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14318 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14322 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14326 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14330 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14333 case SAMPLE_android_clone:
14334 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14337 case SAMPLE_android_move:
14338 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14341 case SAMPLE_spring:
14342 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14346 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14350 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14353 case SAMPLE_eater_eat:
14354 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14358 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14361 case SAMPLE_collect:
14362 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14365 case SAMPLE_diamond:
14366 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14369 case SAMPLE_squash:
14370 /* !!! CHECK THIS !!! */
14372 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14374 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14378 case SAMPLE_wonderfall:
14379 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14383 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14387 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14391 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14395 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14399 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14403 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14406 case SAMPLE_wonder:
14407 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14411 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14414 case SAMPLE_exit_open:
14415 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14418 case SAMPLE_exit_leave:
14419 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14422 case SAMPLE_dynamite:
14423 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14427 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14431 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14435 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14439 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14443 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14447 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14451 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14457 void ChangeTime(int value)
14459 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14463 /* EMC game engine uses value from time counter of RND game engine */
14464 level.native_em_level->lev->time = *time;
14466 DrawGameValue_Time(*time);
14469 void RaiseScore(int value)
14471 /* EMC game engine and RND game engine have separate score counters */
14472 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14473 &level.native_em_level->lev->score : &local_player->score);
14477 DrawGameValue_Score(*score);
14481 void RaiseScore(int value)
14483 local_player->score += value;
14486 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14488 DisplayGameControlValues();
14490 DrawGameValue_Score(local_player->score);
14494 void RaiseScoreElement(int element)
14499 case EL_BD_DIAMOND:
14500 case EL_EMERALD_YELLOW:
14501 case EL_EMERALD_RED:
14502 case EL_EMERALD_PURPLE:
14503 case EL_SP_INFOTRON:
14504 RaiseScore(level.score[SC_EMERALD]);
14507 RaiseScore(level.score[SC_DIAMOND]);
14510 RaiseScore(level.score[SC_CRYSTAL]);
14513 RaiseScore(level.score[SC_PEARL]);
14516 case EL_BD_BUTTERFLY:
14517 case EL_SP_ELECTRON:
14518 RaiseScore(level.score[SC_BUG]);
14521 case EL_BD_FIREFLY:
14522 case EL_SP_SNIKSNAK:
14523 RaiseScore(level.score[SC_SPACESHIP]);
14526 case EL_DARK_YAMYAM:
14527 RaiseScore(level.score[SC_YAMYAM]);
14530 RaiseScore(level.score[SC_ROBOT]);
14533 RaiseScore(level.score[SC_PACMAN]);
14536 RaiseScore(level.score[SC_NUT]);
14539 case EL_EM_DYNAMITE:
14540 case EL_SP_DISK_RED:
14541 case EL_DYNABOMB_INCREASE_NUMBER:
14542 case EL_DYNABOMB_INCREASE_SIZE:
14543 case EL_DYNABOMB_INCREASE_POWER:
14544 RaiseScore(level.score[SC_DYNAMITE]);
14546 case EL_SHIELD_NORMAL:
14547 case EL_SHIELD_DEADLY:
14548 RaiseScore(level.score[SC_SHIELD]);
14550 case EL_EXTRA_TIME:
14551 RaiseScore(level.extra_time_score);
14565 case EL_DC_KEY_WHITE:
14566 RaiseScore(level.score[SC_KEY]);
14569 RaiseScore(element_info[element].collect_score);
14574 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14576 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14578 #if defined(NETWORK_AVALIABLE)
14579 if (options.network)
14580 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14589 FadeSkipNextFadeIn();
14591 fading = fading_none;
14595 OpenDoor(DOOR_CLOSE_1);
14598 game_status = GAME_MODE_MAIN;
14601 DrawAndFadeInMainMenu(REDRAW_FIELD);
14609 FadeOut(REDRAW_FIELD);
14612 game_status = GAME_MODE_MAIN;
14614 DrawAndFadeInMainMenu(REDRAW_FIELD);
14618 else /* continue playing the game */
14620 if (tape.playing && tape.deactivate_display)
14621 TapeDeactivateDisplayOff(TRUE);
14623 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14625 if (tape.playing && tape.deactivate_display)
14626 TapeDeactivateDisplayOn();
14630 void RequestQuitGame(boolean ask_if_really_quit)
14632 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14633 boolean skip_request = AllPlayersGone || quick_quit;
14635 RequestQuitGameExt(skip_request, quick_quit,
14636 "Do you really want to quit the game ?");
14640 /* ------------------------------------------------------------------------- */
14641 /* random generator functions */
14642 /* ------------------------------------------------------------------------- */
14644 unsigned int InitEngineRandom_RND(long seed)
14646 game.num_random_calls = 0;
14649 unsigned int rnd_seed = InitEngineRandom(seed);
14651 printf("::: START RND: %d\n", rnd_seed);
14656 return InitEngineRandom(seed);
14662 unsigned int RND(int max)
14666 game.num_random_calls++;
14668 return GetEngineRandom(max);
14675 /* ------------------------------------------------------------------------- */
14676 /* game engine snapshot handling functions */
14677 /* ------------------------------------------------------------------------- */
14679 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14681 struct EngineSnapshotInfo
14683 /* runtime values for custom element collect score */
14684 int collect_score[NUM_CUSTOM_ELEMENTS];
14686 /* runtime values for group element choice position */
14687 int choice_pos[NUM_GROUP_ELEMENTS];
14689 /* runtime values for belt position animations */
14690 int belt_graphic[4 * NUM_BELT_PARTS];
14691 int belt_anim_mode[4 * NUM_BELT_PARTS];
14694 struct EngineSnapshotNodeInfo
14701 static struct EngineSnapshotInfo engine_snapshot_rnd;
14702 static ListNode *engine_snapshot_list = NULL;
14703 static char *snapshot_level_identifier = NULL;
14704 static int snapshot_level_nr = -1;
14706 void FreeEngineSnapshot()
14708 while (engine_snapshot_list != NULL)
14709 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14712 setString(&snapshot_level_identifier, NULL);
14713 snapshot_level_nr = -1;
14716 static void SaveEngineSnapshotValues_RND()
14718 static int belt_base_active_element[4] =
14720 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14721 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14722 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14723 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14727 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14729 int element = EL_CUSTOM_START + i;
14731 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14734 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14736 int element = EL_GROUP_START + i;
14738 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14741 for (i = 0; i < 4; i++)
14743 for (j = 0; j < NUM_BELT_PARTS; j++)
14745 int element = belt_base_active_element[i] + j;
14746 int graphic = el2img(element);
14747 int anim_mode = graphic_info[graphic].anim_mode;
14749 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14750 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14755 static void LoadEngineSnapshotValues_RND()
14757 unsigned long num_random_calls = game.num_random_calls;
14760 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14762 int element = EL_CUSTOM_START + i;
14764 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14767 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14769 int element = EL_GROUP_START + i;
14771 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14774 for (i = 0; i < 4; i++)
14776 for (j = 0; j < NUM_BELT_PARTS; j++)
14778 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14779 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14781 graphic_info[graphic].anim_mode = anim_mode;
14785 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14787 InitRND(tape.random_seed);
14788 for (i = 0; i < num_random_calls; i++)
14792 if (game.num_random_calls != num_random_calls)
14794 Error(ERR_INFO, "number of random calls out of sync");
14795 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14796 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14797 Error(ERR_EXIT, "this should not happen -- please debug");
14801 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14803 struct EngineSnapshotNodeInfo *bi =
14804 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14806 bi->buffer_orig = buffer;
14807 bi->buffer_copy = checked_malloc(size);
14810 memcpy(bi->buffer_copy, buffer, size);
14812 addNodeToList(&engine_snapshot_list, NULL, bi);
14815 void SaveEngineSnapshot()
14817 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14819 if (level_editor_test_game) /* do not save snapshots from editor */
14822 /* copy some special values to a structure better suited for the snapshot */
14824 SaveEngineSnapshotValues_RND();
14825 SaveEngineSnapshotValues_EM();
14827 /* save values stored in special snapshot structure */
14829 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14830 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14832 /* save further RND engine values */
14834 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14835 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14836 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14838 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14839 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14840 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14841 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14843 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14844 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14845 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14846 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14847 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14849 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14850 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14851 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14853 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14855 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14857 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14858 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14860 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14861 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14862 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14863 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14864 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14865 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14866 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14867 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14868 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14869 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14870 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14871 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14872 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14873 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14874 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14875 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14876 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14877 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14879 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14880 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14882 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14883 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14884 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14886 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14887 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14889 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14890 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14891 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14892 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14893 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14895 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14896 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14898 /* save level identification information */
14900 setString(&snapshot_level_identifier, leveldir_current->identifier);
14901 snapshot_level_nr = level_nr;
14904 ListNode *node = engine_snapshot_list;
14907 while (node != NULL)
14909 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14914 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14918 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14920 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14923 void LoadEngineSnapshot()
14925 ListNode *node = engine_snapshot_list;
14927 if (engine_snapshot_list == NULL)
14930 while (node != NULL)
14932 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14937 /* restore special values from snapshot structure */
14939 LoadEngineSnapshotValues_RND();
14940 LoadEngineSnapshotValues_EM();
14943 boolean CheckEngineSnapshot()
14945 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14946 snapshot_level_nr == level_nr);
14950 /* ---------- new game button stuff ---------------------------------------- */
14952 /* graphic position values for game buttons */
14953 #define GAME_BUTTON_XSIZE 30
14954 #define GAME_BUTTON_YSIZE 30
14955 #define GAME_BUTTON_XPOS 5
14956 #define GAME_BUTTON_YPOS 215
14957 #define SOUND_BUTTON_XPOS 5
14958 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14960 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14961 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14962 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14963 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14964 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14965 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14973 } gamebutton_info[NUM_GAME_BUTTONS] =
14977 &game.button.stop.x, &game.button.stop.y,
14978 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14983 &game.button.pause.x, &game.button.pause.y,
14984 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14985 GAME_CTRL_ID_PAUSE,
14989 &game.button.play.x, &game.button.play.y,
14990 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14995 &game.button.sound_music.x, &game.button.sound_music.y,
14996 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14997 SOUND_CTRL_ID_MUSIC,
14998 "background music on/off"
15001 &game.button.sound_loops.x, &game.button.sound_loops.y,
15002 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15003 SOUND_CTRL_ID_LOOPS,
15004 "sound loops on/off"
15007 &game.button.sound_simple.x,&game.button.sound_simple.y,
15008 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15009 SOUND_CTRL_ID_SIMPLE,
15010 "normal sounds on/off"
15014 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15019 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15020 GAME_CTRL_ID_PAUSE,
15024 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15029 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15030 SOUND_CTRL_ID_MUSIC,
15031 "background music on/off"
15034 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15035 SOUND_CTRL_ID_LOOPS,
15036 "sound loops on/off"
15039 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15040 SOUND_CTRL_ID_SIMPLE,
15041 "normal sounds on/off"
15046 void CreateGameButtons()
15050 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15052 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15053 struct GadgetInfo *gi;
15056 unsigned long event_mask;
15058 int gd_xoffset, gd_yoffset;
15059 int gd_x1, gd_x2, gd_y1, gd_y2;
15062 x = DX + *gamebutton_info[i].x;
15063 y = DY + *gamebutton_info[i].y;
15064 gd_xoffset = gamebutton_info[i].gd_x;
15065 gd_yoffset = gamebutton_info[i].gd_y;
15066 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15067 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15069 if (id == GAME_CTRL_ID_STOP ||
15070 id == GAME_CTRL_ID_PAUSE ||
15071 id == GAME_CTRL_ID_PLAY)
15073 button_type = GD_TYPE_NORMAL_BUTTON;
15075 event_mask = GD_EVENT_RELEASED;
15076 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15077 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15081 button_type = GD_TYPE_CHECK_BUTTON;
15083 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15084 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15085 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15086 event_mask = GD_EVENT_PRESSED;
15087 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15088 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15091 gi = CreateGadget(GDI_CUSTOM_ID, id,
15092 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15097 GDI_X, DX + gd_xoffset,
15098 GDI_Y, DY + gd_yoffset,
15100 GDI_WIDTH, GAME_BUTTON_XSIZE,
15101 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15102 GDI_TYPE, button_type,
15103 GDI_STATE, GD_BUTTON_UNPRESSED,
15104 GDI_CHECKED, checked,
15105 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15106 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15107 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15108 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15109 GDI_EVENT_MASK, event_mask,
15110 GDI_CALLBACK_ACTION, HandleGameButtons,
15114 Error(ERR_EXIT, "cannot create gadget");
15116 game_gadget[id] = gi;
15120 void FreeGameButtons()
15124 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15125 FreeGadget(game_gadget[i]);
15128 static void MapGameButtons()
15132 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15133 MapGadget(game_gadget[i]);
15136 void UnmapGameButtons()
15140 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15141 UnmapGadget(game_gadget[i]);
15144 static void HandleGameButtons(struct GadgetInfo *gi)
15146 int id = gi->custom_id;
15148 if (game_status != GAME_MODE_PLAYING)
15153 case GAME_CTRL_ID_STOP:
15157 RequestQuitGame(TRUE);
15160 case GAME_CTRL_ID_PAUSE:
15161 if (options.network)
15163 #if defined(NETWORK_AVALIABLE)
15165 SendToServer_ContinuePlaying();
15167 SendToServer_PausePlaying();
15171 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15174 case GAME_CTRL_ID_PLAY:
15177 #if defined(NETWORK_AVALIABLE)
15178 if (options.network)
15179 SendToServer_ContinuePlaying();
15183 tape.pausing = FALSE;
15184 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15189 case SOUND_CTRL_ID_MUSIC:
15190 if (setup.sound_music)
15192 setup.sound_music = FALSE;
15195 else if (audio.music_available)
15197 setup.sound = setup.sound_music = TRUE;
15199 SetAudioMode(setup.sound);
15205 case SOUND_CTRL_ID_LOOPS:
15206 if (setup.sound_loops)
15207 setup.sound_loops = FALSE;
15208 else if (audio.loops_available)
15210 setup.sound = setup.sound_loops = TRUE;
15211 SetAudioMode(setup.sound);
15215 case SOUND_CTRL_ID_SIMPLE:
15216 if (setup.sound_simple)
15217 setup.sound_simple = FALSE;
15218 else if (audio.sound_available)
15220 setup.sound = setup.sound_simple = TRUE;
15221 SetAudioMode(setup.sound);