1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
91 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
92 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
93 #define PANEL_XPOS(p) (DX + ALIGNED_MENU_XPOS(p))
94 #define PANEL_YPOS(p) (DY + ALIGNED_MENU_YPOS(p))
96 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
97 #define PANEL_XPOS(p) (ALIGNED_XPOS((p).x, (p).width, (p).align))
98 #define PANEL_YPOS(p) ((p).y)
101 /* special positions in the game control window (relative to control window) */
102 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
103 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
104 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
105 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
106 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
107 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
108 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
109 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
110 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
111 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
112 #define XX_SCORE (PANEL_XPOS(game.panel.score))
113 #define YY_SCORE (PANEL_YPOS(game.panel.score))
114 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
115 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
116 #define XX_TIME (PANEL_XPOS(game.panel.time))
117 #define YY_TIME (PANEL_YPOS(game.panel.time))
119 /* special positions in the game control window (relative to main window) */
120 #define DX_LEVEL1 (DX + XX_LEVEL1)
121 #define DX_LEVEL2 (DX + XX_LEVEL2)
122 #define DX_LEVEL (DX + XX_LEVEL)
123 #define DY_LEVEL (DY + YY_LEVEL)
124 #define DX_EMERALDS (DX + XX_EMERALDS)
125 #define DY_EMERALDS (DY + YY_EMERALDS)
126 #define DX_DYNAMITE (DX + XX_DYNAMITE)
127 #define DY_DYNAMITE (DY + YY_DYNAMITE)
128 #define DX_KEYS (DX + XX_KEYS)
129 #define DY_KEYS (DY + YY_KEYS)
130 #define DX_SCORE (DX + XX_SCORE)
131 #define DY_SCORE (DY + YY_SCORE)
132 #define DX_TIME1 (DX + XX_TIME1)
133 #define DX_TIME2 (DX + XX_TIME2)
134 #define DX_TIME (DX + XX_TIME)
135 #define DY_TIME (DY + YY_TIME)
138 /* game panel display and control definitions */
140 #define GAME_CONTROL_LEVEL 0
141 #define GAME_CONTROL_GEMS 1
142 #define GAME_CONTROL_INVENTORY 2
143 #define GAME_CONTROL_KEY_1 3
144 #define GAME_CONTROL_KEY_2 4
145 #define GAME_CONTROL_KEY_3 5
146 #define GAME_CONTROL_KEY_4 6
147 #define GAME_CONTROL_KEY_5 7
148 #define GAME_CONTROL_KEY_6 8
149 #define GAME_CONTROL_KEY_7 9
150 #define GAME_CONTROL_KEY_8 10
151 #define GAME_CONTROL_KEY_WHITE 11
152 #define GAME_CONTROL_KEY_WHITE_COUNT 12
153 #define GAME_CONTROL_SCORE 13
154 #define GAME_CONTROL_TIME 14
155 #define GAME_CONTROL_TIME_HH 15
156 #define GAME_CONTROL_TIME_MM 16
157 #define GAME_CONTROL_TIME_SS 17
158 #define GAME_CONTROL_DROP_NEXT_1 18
159 #define GAME_CONTROL_DROP_NEXT_2 19
160 #define GAME_CONTROL_DROP_NEXT_3 20
161 #define GAME_CONTROL_DROP_NEXT_4 21
162 #define GAME_CONTROL_DROP_NEXT_5 22
163 #define GAME_CONTROL_DROP_NEXT_6 23
164 #define GAME_CONTROL_DROP_NEXT_7 24
165 #define GAME_CONTROL_DROP_NEXT_8 25
166 #define GAME_CONTROL_SHIELD_NORMAL 26
167 #define GAME_CONTROL_SHIELD_NORMAL_TIME 27
168 #define GAME_CONTROL_SHIELD_DEADLY 28
169 #define GAME_CONTROL_SHIELD_DEADLY_TIME 29
170 #define GAME_CONTROL_EXIT 30
171 #define GAME_CONTROL_EM_EXIT 31
172 #define GAME_CONTROL_SP_EXIT 32
173 #define GAME_CONTROL_STEEL_EXIT 33
174 #define GAME_CONTROL_EM_STEEL_EXIT 34
175 #define GAME_CONTROL_EMC_MAGIC_BALL 35
176 #define GAME_CONTROL_EMC_MAGIC_BALL_TIME 36
177 #define GAME_CONTROL_LIGHT_SWITCH 37
178 #define GAME_CONTROL_LIGHT_SWITCH_TIME 38
179 #define GAME_CONTROL_TIMEGATE_SWITCH 39
180 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME 40
181 #define GAME_CONTROL_SWITCHGATE_SWITCH 41
182 #define GAME_CONTROL_EMC_LENSES 42
183 #define GAME_CONTROL_EMC_LENSES_TIME 43
184 #define GAME_CONTROL_EMC_MAGNIFIER 44
185 #define GAME_CONTROL_EMC_MAGNIFIER_TIME 45
186 #define GAME_CONTROL_BALLOON_SWITCH 46
187 #define GAME_CONTROL_DYNABOMB_NUMBER 47
188 #define GAME_CONTROL_DYNABOMB_SIZE 48
189 #define GAME_CONTROL_DYNABOMB_POWER 49
190 #define GAME_CONTROL_PENGUINS 50
191 #define GAME_CONTROL_SOKOBAN_OBJECTS 51
192 #define GAME_CONTROL_SOKOBAN_FIELDS 52
193 #define GAME_CONTROL_ROBOT_WHEEL 53
194 #define GAME_CONTROL_CONVEYOR_BELT_1 54
195 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH 55
196 #define GAME_CONTROL_CONVEYOR_BELT_2 56
197 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH 57
198 #define GAME_CONTROL_CONVEYOR_BELT_3 58
199 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH 59
200 #define GAME_CONTROL_CONVEYOR_BELT_4 60
201 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH 61
202 #define GAME_CONTROL_MAGIC_WALL 62
203 #define GAME_CONTROL_MAGIC_WALL_TIME 63
204 #define GAME_CONTROL_BD_MAGIC_WALL 64
205 #define GAME_CONTROL_DC_MAGIC_WALL 65
206 #define GAME_CONTROL_PLAYER_NAME 66
207 #define GAME_CONTROL_LEVEL_NAME 67
208 #define GAME_CONTROL_LEVEL_AUTHOR 68
210 struct GameControlInfo
214 struct TextPosInfo *pos_text;
219 static struct GameControlInfo game_controls[] =
232 GAME_CONTROL_INVENTORY,
233 &game.panel.inventory,
252 GAME_CONTROL_TIME_HH,
257 GAME_CONTROL_TIME_MM,
262 GAME_CONTROL_TIME_SS,
267 GAME_CONTROL_DROP_NEXT_1,
268 &game.panel.drop_next_1,
272 GAME_CONTROL_DROP_NEXT_2,
273 &game.panel.drop_next_2,
277 GAME_CONTROL_DROP_NEXT_3,
278 &game.panel.drop_next_3,
282 GAME_CONTROL_DROP_NEXT_4,
283 &game.panel.drop_next_4,
287 GAME_CONTROL_DROP_NEXT_5,
288 &game.panel.drop_next_5,
292 GAME_CONTROL_DROP_NEXT_6,
293 &game.panel.drop_next_6,
297 GAME_CONTROL_DROP_NEXT_7,
298 &game.panel.drop_next_7,
302 GAME_CONTROL_DROP_NEXT_8,
303 &game.panel.drop_next_8,
307 GAME_CONTROL_EMC_KEYS,
308 &game.panel.emc_keys,
352 GAME_CONTROL_KEY_WHITE,
353 &game.panel.key_white,
357 GAME_CONTROL_KEY_WHITE_COUNT,
358 &game.panel.key_white_count,
362 GAME_CONTROL_SHIELD_NORMAL,
363 &game.panel.shield_normal,
367 GAME_CONTROL_SHIELD_NORMAL_TIME,
368 &game.panel.shield_normal_time,
372 GAME_CONTROL_SHIELD_DEADLY,
373 &game.panel.shield_deadly,
377 GAME_CONTROL_SHIELD_DEADLY_TIME,
378 &game.panel.shield_deadly_time,
387 GAME_CONTROL_EM_EXIT,
392 GAME_CONTROL_SP_EXIT,
397 GAME_CONTROL_STEEL_EXIT,
398 &game.panel.steel_exit,
402 GAME_CONTROL_EM_STEEL_EXIT,
403 &game.panel.em_steel_exit,
407 GAME_CONTROL_EMC_MAGIC_BALL,
408 &game.panel.emc_magic_ball,
412 GAME_CONTROL_EMC_MAGIC_BALL_TIME,
413 &game.panel.emc_magic_ball_time,
417 GAME_CONTROL_LIGHT_SWITCH,
418 &game.panel.light_switch,
422 GAME_CONTROL_LIGHT_SWITCH_TIME,
423 &game.panel.light_switch_time,
427 GAME_CONTROL_TIMEGATE_SWITCH,
428 &game.panel.timegate_switch,
432 GAME_CONTROL_TIMEGATE_SWITCH_TIME,
433 &game.panel.timegate_switch_time,
437 GAME_CONTROL_SWITCHGATE_SWITCH,
438 &game.panel.switchgate_switch,
442 GAME_CONTROL_EMC_LENSES,
443 &game.panel.emc_lenses,
447 GAME_CONTROL_EMC_LENSES_TIME,
448 &game.panel.emc_lenses_time,
452 GAME_CONTROL_EMC_MAGNIFIER,
453 &game.panel.emc_magnifier,
457 GAME_CONTROL_EMC_MAGNIFIER_TIME,
458 &game.panel.emc_magnifier_time,
462 GAME_CONTROL_BALLOON_SWITCH,
463 &game.panel.balloon_switch,
467 GAME_CONTROL_DYNABOMB_NUMBER,
468 &game.panel.dynabomb_number,
472 GAME_CONTROL_DYNABOMB_SIZE,
473 &game.panel.dynabomb_size,
477 GAME_CONTROL_DYNABOMB_POWER,
478 &game.panel.dynabomb_power,
482 GAME_CONTROL_PENGUINS,
483 &game.panel.penguins,
487 GAME_CONTROL_SOKOBAN_OBJECTS,
488 &game.panel.sokoban_objects,
492 GAME_CONTROL_SOKOBAN_FIELDS,
493 &game.panel.sokoban_fields,
497 GAME_CONTROL_ROBOT_WHEEL,
498 &game.panel.robot_wheel,
502 GAME_CONTROL_CONVEYOR_BELT_1,
503 &game.panel.conveyor_belt_1,
507 GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
508 &game.panel.conveyor_belt_1_switch,
512 GAME_CONTROL_CONVEYOR_BELT_2,
513 &game.panel.conveyor_belt_2,
517 GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
518 &game.panel.conveyor_belt_2_switch,
522 GAME_CONTROL_CONVEYOR_BELT_3,
523 &game.panel.conveyor_belt_3,
527 GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
528 &game.panel.conveyor_belt_3_switch,
532 GAME_CONTROL_CONVEYOR_BELT_4,
533 &game.panel.conveyor_belt_4,
537 GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
538 &game.panel.conveyor_belt_4_switch,
542 GAME_CONTROL_MAGIC_WALL,
543 &game.panel.magic_wall,
547 GAME_CONTROL_MAGIC_WALL_TIME,
548 &game.panel.magic_wall_time,
552 GAME_CONTROL_BD_MAGIC_WALL,
553 &game.panel.bd_magic_wall,
557 GAME_CONTROL_DC_MAGIC_WALL,
558 &game.panel.dc_magic_wall,
562 GAME_CONTROL_PLAYER_NAME,
563 &game.panel.player_name,
567 GAME_CONTROL_LEVEL_NAME,
568 &game.panel.level_name,
572 GAME_CONTROL_LEVEL_AUTHOR,
573 &game.panel.level_author,
587 /* values for delayed check of falling and moving elements and for collision */
588 #define CHECK_DELAY_MOVING 3
589 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
590 #define CHECK_DELAY_COLLISION 2
591 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
593 /* values for initial player move delay (initial delay counter value) */
594 #define INITIAL_MOVE_DELAY_OFF -1
595 #define INITIAL_MOVE_DELAY_ON 0
597 /* values for player movement speed (which is in fact a delay value) */
598 #define MOVE_DELAY_MIN_SPEED 32
599 #define MOVE_DELAY_NORMAL_SPEED 8
600 #define MOVE_DELAY_HIGH_SPEED 4
601 #define MOVE_DELAY_MAX_SPEED 1
603 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
604 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
606 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
607 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
609 /* values for other actions */
610 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
611 #define MOVE_STEPSIZE_MIN (1)
612 #define MOVE_STEPSIZE_MAX (TILEX)
614 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
615 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
617 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
619 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
620 RND(element_info[e].push_delay_random))
621 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
622 RND(element_info[e].drop_delay_random))
623 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
624 RND(element_info[e].move_delay_random))
625 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
626 (element_info[e].move_delay_random))
627 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
628 RND(element_info[e].ce_value_random_initial))
629 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
630 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
631 RND((c)->delay_random * (c)->delay_frames))
632 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
633 RND((c)->delay_random))
636 #define GET_VALID_RUNTIME_ELEMENT(e) \
637 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
639 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
640 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
641 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
642 (be) + (e) - EL_SELF)
644 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
645 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
646 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
647 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
648 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
649 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
650 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
651 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
652 RESOLVED_REFERENCE_ELEMENT(be, e) : \
655 #define CAN_GROW_INTO(e) \
656 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
658 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
659 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
662 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
663 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
664 (CAN_MOVE_INTO_ACID(e) && \
665 Feld[x][y] == EL_ACID) || \
668 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
669 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
670 (CAN_MOVE_INTO_ACID(e) && \
671 Feld[x][y] == EL_ACID) || \
674 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
675 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
677 (CAN_MOVE_INTO_ACID(e) && \
678 Feld[x][y] == EL_ACID) || \
679 (DONT_COLLIDE_WITH(e) && \
681 !PLAYER_ENEMY_PROTECTED(x, y))))
683 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
684 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
686 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
687 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
689 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
690 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
692 #define ANDROID_CAN_CLONE_FIELD(x, y) \
693 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
694 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
696 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
697 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
699 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
700 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
702 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
703 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
705 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
706 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
708 #define PIG_CAN_ENTER_FIELD(e, x, y) \
709 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
711 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
712 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
713 Feld[x][y] == EL_EM_EXIT_OPEN || \
714 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
715 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
716 IS_FOOD_PENGUIN(Feld[x][y])))
717 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
718 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
720 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
721 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
723 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
724 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
726 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
727 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
728 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
730 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
732 #define CE_ENTER_FIELD_COND(e, x, y) \
733 (!IS_PLAYER(x, y) && \
734 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
736 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
737 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
739 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
740 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
742 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
743 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
744 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
745 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
747 /* game button identifiers */
748 #define GAME_CTRL_ID_STOP 0
749 #define GAME_CTRL_ID_PAUSE 1
750 #define GAME_CTRL_ID_PLAY 2
751 #define SOUND_CTRL_ID_MUSIC 3
752 #define SOUND_CTRL_ID_LOOPS 4
753 #define SOUND_CTRL_ID_SIMPLE 5
755 #define NUM_GAME_BUTTONS 6
758 /* forward declaration for internal use */
760 static void CreateField(int, int, int);
762 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
763 static void AdvanceFrameAndPlayerCounters(int);
765 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
766 static boolean MovePlayer(struct PlayerInfo *, int, int);
767 static void ScrollPlayer(struct PlayerInfo *, int);
768 static void ScrollScreen(struct PlayerInfo *, int);
770 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
772 static void InitBeltMovement(void);
773 static void CloseAllOpenTimegates(void);
774 static void CheckGravityMovement(struct PlayerInfo *);
775 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
776 static void KillPlayerUnlessEnemyProtected(int, int);
777 static void KillPlayerUnlessExplosionProtected(int, int);
779 static void TestIfPlayerTouchesCustomElement(int, int);
780 static void TestIfElementTouchesCustomElement(int, int);
781 static void TestIfElementHitsCustomElement(int, int, int);
783 static void TestIfElementSmashesCustomElement(int, int, int);
786 static void HandleElementChange(int, int, int);
787 static void ExecuteCustomElementAction(int, int, int, int);
788 static boolean ChangeElement(int, int, int, int);
790 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
791 #define CheckTriggeredElementChange(x, y, e, ev) \
792 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
793 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
794 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
795 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
796 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
797 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
798 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
800 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
801 #define CheckElementChange(x, y, e, te, ev) \
802 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
803 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
804 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
805 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
806 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
808 static void PlayLevelSound(int, int, int);
809 static void PlayLevelSoundNearest(int, int, int);
810 static void PlayLevelSoundAction(int, int, int);
811 static void PlayLevelSoundElementAction(int, int, int, int);
812 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
813 static void PlayLevelSoundActionIfLoop(int, int, int);
814 static void StopLevelSoundActionIfLoop(int, int, int);
815 static void PlayLevelMusic();
817 static void MapGameButtons();
818 static void HandleGameButtons(struct GadgetInfo *);
820 int AmoebeNachbarNr(int, int);
821 void AmoebeUmwandeln(int, int);
822 void ContinueMoving(int, int);
824 void InitMovDir(int, int);
825 void InitAmoebaNr(int, int);
826 int NewHiScore(void);
828 void TestIfGoodThingHitsBadThing(int, int, int);
829 void TestIfBadThingHitsGoodThing(int, int, int);
830 void TestIfPlayerTouchesBadThing(int, int);
831 void TestIfPlayerRunsIntoBadThing(int, int, int);
832 void TestIfBadThingTouchesPlayer(int, int);
833 void TestIfBadThingRunsIntoPlayer(int, int, int);
834 void TestIfFriendTouchesBadThing(int, int);
835 void TestIfBadThingTouchesFriend(int, int);
836 void TestIfBadThingTouchesOtherBadThing(int, int);
838 void KillPlayer(struct PlayerInfo *);
839 void BuryPlayer(struct PlayerInfo *);
840 void RemovePlayer(struct PlayerInfo *);
842 boolean SnapField(struct PlayerInfo *, int, int);
843 boolean DropElement(struct PlayerInfo *);
845 static int getInvisibleActiveFromInvisibleElement(int);
846 static int getInvisibleFromInvisibleActiveElement(int);
848 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
850 /* for detection of endless loops, caused by custom element programming */
851 /* (using maximal playfield width x 10 is just a rough approximation) */
852 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
854 #define RECURSION_LOOP_DETECTION_START(e, rc) \
856 if (recursion_loop_detected) \
859 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
861 recursion_loop_detected = TRUE; \
862 recursion_loop_element = (e); \
865 recursion_loop_depth++; \
868 #define RECURSION_LOOP_DETECTION_END() \
870 recursion_loop_depth--; \
873 static int recursion_loop_depth;
874 static boolean recursion_loop_detected;
875 static boolean recursion_loop_element;
878 /* ------------------------------------------------------------------------- */
879 /* definition of elements that automatically change to other elements after */
880 /* a specified time, eventually calling a function when changing */
881 /* ------------------------------------------------------------------------- */
883 /* forward declaration for changer functions */
884 static void InitBuggyBase(int, int);
885 static void WarnBuggyBase(int, int);
887 static void InitTrap(int, int);
888 static void ActivateTrap(int, int);
889 static void ChangeActiveTrap(int, int);
891 static void InitRobotWheel(int, int);
892 static void RunRobotWheel(int, int);
893 static void StopRobotWheel(int, int);
895 static void InitTimegateWheel(int, int);
896 static void RunTimegateWheel(int, int);
898 static void InitMagicBallDelay(int, int);
899 static void ActivateMagicBall(int, int);
901 struct ChangingElementInfo
906 void (*pre_change_function)(int x, int y);
907 void (*change_function)(int x, int y);
908 void (*post_change_function)(int x, int y);
911 static struct ChangingElementInfo change_delay_list[] =
946 EL_STEEL_EXIT_OPENING,
954 EL_STEEL_EXIT_CLOSING,
955 EL_STEEL_EXIT_CLOSED,
982 EL_EM_STEEL_EXIT_OPENING,
983 EL_EM_STEEL_EXIT_OPEN,
990 EL_EM_STEEL_EXIT_CLOSING,
994 EL_EM_STEEL_EXIT_CLOSED,
1018 EL_SWITCHGATE_OPENING,
1026 EL_SWITCHGATE_CLOSING,
1027 EL_SWITCHGATE_CLOSED,
1034 EL_TIMEGATE_OPENING,
1042 EL_TIMEGATE_CLOSING,
1051 EL_ACID_SPLASH_LEFT,
1059 EL_ACID_SPLASH_RIGHT,
1068 EL_SP_BUGGY_BASE_ACTIVATING,
1075 EL_SP_BUGGY_BASE_ACTIVATING,
1076 EL_SP_BUGGY_BASE_ACTIVE,
1083 EL_SP_BUGGY_BASE_ACTIVE,
1107 EL_ROBOT_WHEEL_ACTIVE,
1115 EL_TIMEGATE_SWITCH_ACTIVE,
1123 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1124 EL_DC_TIMEGATE_SWITCH,
1131 EL_EMC_MAGIC_BALL_ACTIVE,
1132 EL_EMC_MAGIC_BALL_ACTIVE,
1139 EL_EMC_SPRING_BUMPER_ACTIVE,
1140 EL_EMC_SPRING_BUMPER,
1147 EL_DIAGONAL_SHRINKING,
1155 EL_DIAGONAL_GROWING,
1176 int push_delay_fixed, push_delay_random;
1180 { EL_SPRING, 0, 0 },
1181 { EL_BALLOON, 0, 0 },
1183 { EL_SOKOBAN_OBJECT, 2, 0 },
1184 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1185 { EL_SATELLITE, 2, 0 },
1186 { EL_SP_DISK_YELLOW, 2, 0 },
1188 { EL_UNDEFINED, 0, 0 },
1196 move_stepsize_list[] =
1198 { EL_AMOEBA_DROP, 2 },
1199 { EL_AMOEBA_DROPPING, 2 },
1200 { EL_QUICKSAND_FILLING, 1 },
1201 { EL_QUICKSAND_EMPTYING, 1 },
1202 { EL_QUICKSAND_FAST_FILLING, 2 },
1203 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1204 { EL_MAGIC_WALL_FILLING, 2 },
1205 { EL_MAGIC_WALL_EMPTYING, 2 },
1206 { EL_BD_MAGIC_WALL_FILLING, 2 },
1207 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1208 { EL_DC_MAGIC_WALL_FILLING, 2 },
1209 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1211 { EL_UNDEFINED, 0 },
1219 collect_count_list[] =
1222 { EL_BD_DIAMOND, 1 },
1223 { EL_EMERALD_YELLOW, 1 },
1224 { EL_EMERALD_RED, 1 },
1225 { EL_EMERALD_PURPLE, 1 },
1227 { EL_SP_INFOTRON, 1 },
1231 { EL_UNDEFINED, 0 },
1239 access_direction_list[] =
1241 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1242 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1243 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1244 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1245 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1246 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1247 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1248 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1249 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1250 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1251 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1253 { EL_SP_PORT_LEFT, MV_RIGHT },
1254 { EL_SP_PORT_RIGHT, MV_LEFT },
1255 { EL_SP_PORT_UP, MV_DOWN },
1256 { EL_SP_PORT_DOWN, MV_UP },
1257 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1258 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1259 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1260 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1261 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1262 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1263 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1264 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1265 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1266 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1267 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1268 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1269 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1270 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1271 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1273 { EL_UNDEFINED, MV_NONE }
1276 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1278 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1279 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1280 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1281 IS_JUST_CHANGING(x, y))
1283 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1285 /* static variables for playfield scan mode (scanning forward or backward) */
1286 static int playfield_scan_start_x = 0;
1287 static int playfield_scan_start_y = 0;
1288 static int playfield_scan_delta_x = 1;
1289 static int playfield_scan_delta_y = 1;
1291 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1292 (y) >= 0 && (y) <= lev_fieldy - 1; \
1293 (y) += playfield_scan_delta_y) \
1294 for ((x) = playfield_scan_start_x; \
1295 (x) >= 0 && (x) <= lev_fieldx - 1; \
1296 (x) += playfield_scan_delta_x)
1299 void DEBUG_SetMaximumDynamite()
1303 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1304 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1305 local_player->inventory_element[local_player->inventory_size++] =
1310 static void InitPlayfieldScanModeVars()
1312 if (game.use_reverse_scan_direction)
1314 playfield_scan_start_x = lev_fieldx - 1;
1315 playfield_scan_start_y = lev_fieldy - 1;
1317 playfield_scan_delta_x = -1;
1318 playfield_scan_delta_y = -1;
1322 playfield_scan_start_x = 0;
1323 playfield_scan_start_y = 0;
1325 playfield_scan_delta_x = 1;
1326 playfield_scan_delta_y = 1;
1330 static void InitPlayfieldScanMode(int mode)
1332 game.use_reverse_scan_direction =
1333 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1335 InitPlayfieldScanModeVars();
1338 static int get_move_delay_from_stepsize(int move_stepsize)
1341 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1343 /* make sure that stepsize value is always a power of 2 */
1344 move_stepsize = (1 << log_2(move_stepsize));
1346 return TILEX / move_stepsize;
1349 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1352 int player_nr = player->index_nr;
1353 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1354 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1356 /* do no immediately change move delay -- the player might just be moving */
1357 player->move_delay_value_next = move_delay;
1359 /* information if player can move must be set separately */
1360 player->cannot_move = cannot_move;
1364 player->move_delay = game.initial_move_delay[player_nr];
1365 player->move_delay_value = game.initial_move_delay_value[player_nr];
1367 player->move_delay_value_next = -1;
1369 player->move_delay_reset_counter = 0;
1373 void GetPlayerConfig()
1375 GameFrameDelay = setup.game_frame_delay;
1377 if (!audio.sound_available)
1378 setup.sound_simple = FALSE;
1380 if (!audio.loops_available)
1381 setup.sound_loops = FALSE;
1383 if (!audio.music_available)
1384 setup.sound_music = FALSE;
1386 if (!video.fullscreen_available)
1387 setup.fullscreen = FALSE;
1389 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1391 SetAudioMode(setup.sound);
1395 int GetElementFromGroupElement(int element)
1397 if (IS_GROUP_ELEMENT(element))
1399 struct ElementGroupInfo *group = element_info[element].group;
1400 int last_anim_random_frame = gfx.anim_random_frame;
1403 if (group->choice_mode == ANIM_RANDOM)
1404 gfx.anim_random_frame = RND(group->num_elements_resolved);
1406 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1407 group->choice_mode, 0,
1410 if (group->choice_mode == ANIM_RANDOM)
1411 gfx.anim_random_frame = last_anim_random_frame;
1413 group->choice_pos++;
1415 element = group->element_resolved[element_pos];
1421 static void InitPlayerField(int x, int y, int element, boolean init_game)
1423 if (element == EL_SP_MURPHY)
1427 if (stored_player[0].present)
1429 Feld[x][y] = EL_SP_MURPHY_CLONE;
1435 stored_player[0].use_murphy = TRUE;
1437 if (!level.use_artwork_element[0])
1438 stored_player[0].artwork_element = EL_SP_MURPHY;
1441 Feld[x][y] = EL_PLAYER_1;
1447 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1448 int jx = player->jx, jy = player->jy;
1450 player->present = TRUE;
1452 player->block_last_field = (element == EL_SP_MURPHY ?
1453 level.sp_block_last_field :
1454 level.block_last_field);
1456 /* ---------- initialize player's last field block delay --------------- */
1458 /* always start with reliable default value (no adjustment needed) */
1459 player->block_delay_adjustment = 0;
1461 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1462 if (player->block_last_field && element == EL_SP_MURPHY)
1463 player->block_delay_adjustment = 1;
1465 /* special case 2: in game engines before 3.1.1, blocking was different */
1466 if (game.use_block_last_field_bug)
1467 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1469 if (!options.network || player->connected)
1471 player->active = TRUE;
1473 /* remove potentially duplicate players */
1474 if (StorePlayer[jx][jy] == Feld[x][y])
1475 StorePlayer[jx][jy] = 0;
1477 StorePlayer[x][y] = Feld[x][y];
1481 printf("Player %d activated.\n", player->element_nr);
1482 printf("[Local player is %d and currently %s.]\n",
1483 local_player->element_nr,
1484 local_player->active ? "active" : "not active");
1488 Feld[x][y] = EL_EMPTY;
1490 player->jx = player->last_jx = x;
1491 player->jy = player->last_jy = y;
1495 static void InitField(int x, int y, boolean init_game)
1497 int element = Feld[x][y];
1506 InitPlayerField(x, y, element, init_game);
1509 case EL_SOKOBAN_FIELD_PLAYER:
1510 element = Feld[x][y] = EL_PLAYER_1;
1511 InitField(x, y, init_game);
1513 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1514 InitField(x, y, init_game);
1517 case EL_SOKOBAN_FIELD_EMPTY:
1518 local_player->sokobanfields_still_needed++;
1522 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1523 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1524 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1525 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1526 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1527 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1528 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1529 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1530 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1531 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1540 case EL_SPACESHIP_RIGHT:
1541 case EL_SPACESHIP_UP:
1542 case EL_SPACESHIP_LEFT:
1543 case EL_SPACESHIP_DOWN:
1544 case EL_BD_BUTTERFLY:
1545 case EL_BD_BUTTERFLY_RIGHT:
1546 case EL_BD_BUTTERFLY_UP:
1547 case EL_BD_BUTTERFLY_LEFT:
1548 case EL_BD_BUTTERFLY_DOWN:
1550 case EL_BD_FIREFLY_RIGHT:
1551 case EL_BD_FIREFLY_UP:
1552 case EL_BD_FIREFLY_LEFT:
1553 case EL_BD_FIREFLY_DOWN:
1554 case EL_PACMAN_RIGHT:
1556 case EL_PACMAN_LEFT:
1557 case EL_PACMAN_DOWN:
1559 case EL_YAMYAM_LEFT:
1560 case EL_YAMYAM_RIGHT:
1562 case EL_YAMYAM_DOWN:
1563 case EL_DARK_YAMYAM:
1566 case EL_SP_SNIKSNAK:
1567 case EL_SP_ELECTRON:
1576 case EL_AMOEBA_FULL:
1581 case EL_AMOEBA_DROP:
1582 if (y == lev_fieldy - 1)
1584 Feld[x][y] = EL_AMOEBA_GROWING;
1585 Store[x][y] = EL_AMOEBA_WET;
1589 case EL_DYNAMITE_ACTIVE:
1590 case EL_SP_DISK_RED_ACTIVE:
1591 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1592 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1593 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1594 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1595 MovDelay[x][y] = 96;
1598 case EL_EM_DYNAMITE_ACTIVE:
1599 MovDelay[x][y] = 32;
1603 local_player->lights_still_needed++;
1607 local_player->friends_still_needed++;
1612 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1615 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1616 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1617 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1618 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1619 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1620 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1621 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1622 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1623 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1624 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1625 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1626 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1629 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1630 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1631 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1633 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1635 game.belt_dir[belt_nr] = belt_dir;
1636 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1638 else /* more than one switch -- set it like the first switch */
1640 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1645 #if !USE_BOTH_SWITCHGATE_SWITCHES
1646 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1648 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1651 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1653 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1657 case EL_LIGHT_SWITCH_ACTIVE:
1659 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1662 case EL_INVISIBLE_STEELWALL:
1663 case EL_INVISIBLE_WALL:
1664 case EL_INVISIBLE_SAND:
1665 if (game.light_time_left > 0 ||
1666 game.lenses_time_left > 0)
1667 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1670 case EL_EMC_MAGIC_BALL:
1671 if (game.ball_state)
1672 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1675 case EL_EMC_MAGIC_BALL_SWITCH:
1676 if (game.ball_state)
1677 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1681 if (IS_CUSTOM_ELEMENT(element))
1683 if (CAN_MOVE(element))
1686 #if USE_NEW_CUSTOM_VALUE
1687 if (!element_info[element].use_last_ce_value || init_game)
1688 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1691 else if (IS_GROUP_ELEMENT(element))
1693 Feld[x][y] = GetElementFromGroupElement(element);
1695 InitField(x, y, init_game);
1702 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1705 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1707 InitField(x, y, init_game);
1709 /* not needed to call InitMovDir() -- already done by InitField()! */
1710 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1711 CAN_MOVE(Feld[x][y]))
1715 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1717 int old_element = Feld[x][y];
1719 InitField(x, y, init_game);
1721 /* not needed to call InitMovDir() -- already done by InitField()! */
1722 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1723 CAN_MOVE(old_element) &&
1724 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1727 /* this case is in fact a combination of not less than three bugs:
1728 first, it calls InitMovDir() for elements that can move, although this is
1729 already done by InitField(); then, it checks the element that was at this
1730 field _before_ the call to InitField() (which can change it); lastly, it
1731 was not called for "mole with direction" elements, which were treated as
1732 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1738 void DrawGameValue_Emeralds(int value)
1740 struct TextPosInfo *pos = &game.panel.gems;
1742 int font_nr = pos->font;
1744 int font_nr = FONT_TEXT_2;
1746 int font_width = getFontWidth(font_nr);
1747 int chars = pos->chars;
1749 if (PANEL_DEACTIVATED(pos))
1752 pos->width = chars * font_width;
1754 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1757 void DrawGameValue_Dynamite(int value)
1759 struct TextPosInfo *pos = &game.panel.inventory;
1761 int font_nr = pos->font;
1763 int font_nr = FONT_TEXT_2;
1765 int font_width = getFontWidth(font_nr);
1766 int chars = pos->chars;
1768 if (PANEL_DEACTIVATED(pos))
1771 pos->width = chars * font_width;
1773 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1776 void DrawGameValue_Score(int value)
1778 struct TextPosInfo *pos = &game.panel.score;
1780 int font_nr = pos->font;
1782 int font_nr = FONT_TEXT_2;
1784 int font_width = getFontWidth(font_nr);
1785 int chars = pos->chars;
1787 if (PANEL_DEACTIVATED(pos))
1790 pos->width = chars * font_width;
1792 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1795 void DrawGameValue_Time(int value)
1797 struct TextPosInfo *pos = &game.panel.time;
1798 static int last_value = -1;
1801 int chars = pos->chars;
1803 int font1_nr = pos->font;
1804 int font2_nr = pos->font_alt;
1806 int font1_nr = FONT_TEXT_2;
1807 int font2_nr = FONT_TEXT_1;
1809 int font_nr = font1_nr;
1810 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1812 if (PANEL_DEACTIVATED(pos))
1815 if (use_dynamic_chars) /* use dynamic number of chars */
1817 chars = (value < 1000 ? chars1 : chars2);
1818 font_nr = (value < 1000 ? font1_nr : font2_nr);
1821 /* clear background if value just changed its size (dynamic chars only) */
1822 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
1824 int width1 = chars1 * getFontWidth(font1_nr);
1825 int width2 = chars2 * getFontWidth(font2_nr);
1826 int max_width = MAX(width1, width2);
1827 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1829 pos->width = max_width;
1831 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1832 max_width, max_height);
1835 pos->width = chars * getFontWidth(font_nr);
1837 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1842 void DrawGameValue_Level(int value)
1844 struct TextPosInfo *pos = &game.panel.level_number;
1847 int chars = pos->chars;
1849 int font1_nr = pos->font;
1850 int font2_nr = pos->font_alt;
1852 int font1_nr = FONT_TEXT_2;
1853 int font2_nr = FONT_TEXT_1;
1855 int font_nr = font1_nr;
1856 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1858 if (PANEL_DEACTIVATED(pos))
1861 if (use_dynamic_chars) /* use dynamic number of chars */
1863 chars = (level_nr < 100 ? chars1 : chars2);
1864 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1867 pos->width = chars * getFontWidth(font_nr);
1869 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1872 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1875 struct TextPosInfo *pos = &game.panel.keys;
1878 int base_key_graphic = EL_KEY_1;
1883 if (PANEL_DEACTIVATED(pos))
1888 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1889 base_key_graphic = EL_EM_KEY_1;
1893 pos->width = 4 * MINI_TILEX;
1897 for (i = 0; i < MAX_NUM_KEYS; i++)
1899 /* currently only 4 of 8 possible keys are displayed */
1900 for (i = 0; i < STD_NUM_KEYS; i++)
1904 struct TextPosInfo *pos = &game.panel.key[i];
1906 int src_x = DOOR_GFX_PAGEX5 + 18;
1907 int src_y = DOOR_GFX_PAGEY1 + 123;
1909 int dst_x = PANEL_XPOS(pos);
1910 int dst_y = PANEL_YPOS(pos);
1912 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1913 int dst_y = PANEL_YPOS(pos);
1917 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
1918 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
1920 int graphic = el2edimg(element);
1924 if (PANEL_DEACTIVATED(pos))
1929 /* masked blit with tiles from half-size scaled bitmap does not work yet
1930 (no mask bitmap created for these sizes after loading and scaling) --
1931 solution: load without creating mask, scale, then create final mask */
1933 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1934 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1939 int graphic = el2edimg(base_key_graphic + i);
1944 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1946 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1947 dst_x - src_x, dst_y - src_y);
1948 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
1954 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1956 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1957 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1960 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1962 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1963 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1971 void DrawGameValue_Emeralds(int value)
1973 int font_nr = FONT_TEXT_2;
1974 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1976 if (PANEL_DEACTIVATED(game.panel.gems))
1979 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1982 void DrawGameValue_Dynamite(int value)
1984 int font_nr = FONT_TEXT_2;
1985 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1987 if (PANEL_DEACTIVATED(game.panel.inventory))
1990 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1993 void DrawGameValue_Score(int value)
1995 int font_nr = FONT_TEXT_2;
1996 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1998 if (PANEL_DEACTIVATED(game.panel.score))
2001 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2004 void DrawGameValue_Time(int value)
2006 int font1_nr = FONT_TEXT_2;
2008 int font2_nr = FONT_TEXT_1;
2010 int font2_nr = FONT_LEVEL_NUMBER;
2012 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2013 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2015 if (PANEL_DEACTIVATED(game.panel.time))
2018 /* clear background if value just changed its size */
2019 if (value == 999 || value == 1000)
2020 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2023 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2025 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2028 void DrawGameValue_Level(int value)
2030 int font1_nr = FONT_TEXT_2;
2032 int font2_nr = FONT_TEXT_1;
2034 int font2_nr = FONT_LEVEL_NUMBER;
2037 if (PANEL_DEACTIVATED(game.panel.level))
2041 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2043 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2046 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2048 int base_key_graphic = EL_KEY_1;
2051 if (PANEL_DEACTIVATED(game.panel.keys))
2054 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2055 base_key_graphic = EL_EM_KEY_1;
2057 /* currently only 4 of 8 possible keys are displayed */
2058 for (i = 0; i < STD_NUM_KEYS; i++)
2060 int x = XX_KEYS + i * MINI_TILEX;
2064 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2066 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2067 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2073 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2076 int key[MAX_NUM_KEYS];
2079 /* prevent EM engine from updating time/score values parallel to GameWon() */
2080 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2081 local_player->LevelSolved)
2084 for (i = 0; i < MAX_NUM_KEYS; i++)
2085 key[i] = key_bits & (1 << i);
2087 DrawGameValue_Level(level_nr);
2089 DrawGameValue_Emeralds(emeralds);
2090 DrawGameValue_Dynamite(dynamite);
2091 DrawGameValue_Score(score);
2092 DrawGameValue_Time(time);
2094 DrawGameValue_Keys(key);
2097 void DrawGameDoorValues()
2099 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2100 int dynamite_value = 0;
2101 int score_value = (local_player->LevelSolved ? local_player->score_final :
2102 local_player->score);
2103 int gems_value = local_player->gems_still_needed;
2107 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2109 DrawGameDoorValues_EM();
2114 if (game.centered_player_nr == -1)
2116 for (i = 0; i < MAX_PLAYERS; i++)
2118 for (j = 0; j < MAX_NUM_KEYS; j++)
2119 if (stored_player[i].key[j])
2120 key_bits |= (1 << j);
2122 dynamite_value += stored_player[i].inventory_size;
2127 int player_nr = game.centered_player_nr;
2129 for (i = 0; i < MAX_NUM_KEYS; i++)
2130 if (stored_player[player_nr].key[i])
2131 key_bits |= (1 << i);
2133 dynamite_value = stored_player[player_nr].inventory_size;
2136 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2142 =============================================================================
2144 -----------------------------------------------------------------------------
2145 initialize game engine due to level / tape version number
2146 =============================================================================
2149 static void InitGameEngine()
2151 int i, j, k, l, x, y;
2153 /* set game engine from tape file when re-playing, else from level file */
2154 game.engine_version = (tape.playing ? tape.engine_version :
2155 level.game_version);
2157 /* ---------------------------------------------------------------------- */
2158 /* set flags for bugs and changes according to active game engine version */
2159 /* ---------------------------------------------------------------------- */
2162 Summary of bugfix/change:
2163 Fixed handling for custom elements that change when pushed by the player.
2165 Fixed/changed in version:
2169 Before 3.1.0, custom elements that "change when pushing" changed directly
2170 after the player started pushing them (until then handled in "DigField()").
2171 Since 3.1.0, these custom elements are not changed until the "pushing"
2172 move of the element is finished (now handled in "ContinueMoving()").
2174 Affected levels/tapes:
2175 The first condition is generally needed for all levels/tapes before version
2176 3.1.0, which might use the old behaviour before it was changed; known tapes
2177 that are affected are some tapes from the level set "Walpurgis Gardens" by
2179 The second condition is an exception from the above case and is needed for
2180 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2181 above (including some development versions of 3.1.0), but before it was
2182 known that this change would break tapes like the above and was fixed in
2183 3.1.1, so that the changed behaviour was active although the engine version
2184 while recording maybe was before 3.1.0. There is at least one tape that is
2185 affected by this exception, which is the tape for the one-level set "Bug
2186 Machine" by Juergen Bonhagen.
2189 game.use_change_when_pushing_bug =
2190 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2192 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2193 tape.game_version < VERSION_IDENT(3,1,1,0)));
2196 Summary of bugfix/change:
2197 Fixed handling for blocking the field the player leaves when moving.
2199 Fixed/changed in version:
2203 Before 3.1.1, when "block last field when moving" was enabled, the field
2204 the player is leaving when moving was blocked for the time of the move,
2205 and was directly unblocked afterwards. This resulted in the last field
2206 being blocked for exactly one less than the number of frames of one player
2207 move. Additionally, even when blocking was disabled, the last field was
2208 blocked for exactly one frame.
2209 Since 3.1.1, due to changes in player movement handling, the last field
2210 is not blocked at all when blocking is disabled. When blocking is enabled,
2211 the last field is blocked for exactly the number of frames of one player
2212 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2213 last field is blocked for exactly one more than the number of frames of
2216 Affected levels/tapes:
2217 (!!! yet to be determined -- probably many !!!)
2220 game.use_block_last_field_bug =
2221 (game.engine_version < VERSION_IDENT(3,1,1,0));
2224 Summary of bugfix/change:
2225 Changed behaviour of CE changes with multiple changes per single frame.
2227 Fixed/changed in version:
2231 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2232 This resulted in race conditions where CEs seem to behave strange in some
2233 situations (where triggered CE changes were just skipped because there was
2234 already a CE change on that tile in the playfield in that engine frame).
2235 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2236 (The number of changes per frame must be limited in any case, because else
2237 it is easily possible to define CE changes that would result in an infinite
2238 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2239 should be set large enough so that it would only be reached in cases where
2240 the corresponding CE change conditions run into a loop. Therefore, it seems
2241 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2242 maximal number of change pages for custom elements.)
2244 Affected levels/tapes:
2248 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2249 game.max_num_changes_per_frame = 1;
2251 game.max_num_changes_per_frame =
2252 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2255 /* ---------------------------------------------------------------------- */
2257 /* default scan direction: scan playfield from top/left to bottom/right */
2258 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2260 /* dynamically adjust element properties according to game engine version */
2261 InitElementPropertiesEngine(game.engine_version);
2264 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2265 printf(" tape version == %06d [%s] [file: %06d]\n",
2266 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2268 printf(" => game.engine_version == %06d\n", game.engine_version);
2271 /* ---------- initialize player's initial move delay --------------------- */
2273 /* dynamically adjust player properties according to level information */
2274 for (i = 0; i < MAX_PLAYERS; i++)
2275 game.initial_move_delay_value[i] =
2276 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2278 /* dynamically adjust player properties according to game engine version */
2279 for (i = 0; i < MAX_PLAYERS; i++)
2280 game.initial_move_delay[i] =
2281 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2282 game.initial_move_delay_value[i] : 0);
2284 /* ---------- initialize player's initial push delay --------------------- */
2286 /* dynamically adjust player properties according to game engine version */
2287 game.initial_push_delay_value =
2288 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2290 /* ---------- initialize changing elements ------------------------------- */
2292 /* initialize changing elements information */
2293 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2295 struct ElementInfo *ei = &element_info[i];
2297 /* this pointer might have been changed in the level editor */
2298 ei->change = &ei->change_page[0];
2300 if (!IS_CUSTOM_ELEMENT(i))
2302 ei->change->target_element = EL_EMPTY_SPACE;
2303 ei->change->delay_fixed = 0;
2304 ei->change->delay_random = 0;
2305 ei->change->delay_frames = 1;
2308 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2310 ei->has_change_event[j] = FALSE;
2312 ei->event_page_nr[j] = 0;
2313 ei->event_page[j] = &ei->change_page[0];
2317 /* add changing elements from pre-defined list */
2318 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2320 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2321 struct ElementInfo *ei = &element_info[ch_delay->element];
2323 ei->change->target_element = ch_delay->target_element;
2324 ei->change->delay_fixed = ch_delay->change_delay;
2326 ei->change->pre_change_function = ch_delay->pre_change_function;
2327 ei->change->change_function = ch_delay->change_function;
2328 ei->change->post_change_function = ch_delay->post_change_function;
2330 ei->change->can_change = TRUE;
2331 ei->change->can_change_or_has_action = TRUE;
2333 ei->has_change_event[CE_DELAY] = TRUE;
2335 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2336 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2339 /* ---------- initialize internal run-time variables ------------- */
2341 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2343 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2345 for (j = 0; j < ei->num_change_pages; j++)
2347 ei->change_page[j].can_change_or_has_action =
2348 (ei->change_page[j].can_change |
2349 ei->change_page[j].has_action);
2353 /* add change events from custom element configuration */
2354 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2356 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2358 for (j = 0; j < ei->num_change_pages; j++)
2360 if (!ei->change_page[j].can_change_or_has_action)
2363 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2365 /* only add event page for the first page found with this event */
2366 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2368 ei->has_change_event[k] = TRUE;
2370 ei->event_page_nr[k] = j;
2371 ei->event_page[k] = &ei->change_page[j];
2377 /* ---------- initialize run-time trigger player and element ------------- */
2379 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2381 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2383 for (j = 0; j < ei->num_change_pages; j++)
2385 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2386 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2387 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2388 ei->change_page[j].actual_trigger_ce_value = 0;
2389 ei->change_page[j].actual_trigger_ce_score = 0;
2393 /* ---------- initialize trigger events ---------------------------------- */
2395 /* initialize trigger events information */
2396 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2397 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2398 trigger_events[i][j] = FALSE;
2400 /* add trigger events from element change event properties */
2401 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2403 struct ElementInfo *ei = &element_info[i];
2405 for (j = 0; j < ei->num_change_pages; j++)
2407 if (!ei->change_page[j].can_change_or_has_action)
2410 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2412 int trigger_element = ei->change_page[j].trigger_element;
2414 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2416 if (ei->change_page[j].has_event[k])
2418 if (IS_GROUP_ELEMENT(trigger_element))
2420 struct ElementGroupInfo *group =
2421 element_info[trigger_element].group;
2423 for (l = 0; l < group->num_elements_resolved; l++)
2424 trigger_events[group->element_resolved[l]][k] = TRUE;
2426 else if (trigger_element == EL_ANY_ELEMENT)
2427 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2428 trigger_events[l][k] = TRUE;
2430 trigger_events[trigger_element][k] = TRUE;
2437 /* ---------- initialize push delay -------------------------------------- */
2439 /* initialize push delay values to default */
2440 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2442 if (!IS_CUSTOM_ELEMENT(i))
2444 /* set default push delay values (corrected since version 3.0.7-1) */
2445 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2447 element_info[i].push_delay_fixed = 2;
2448 element_info[i].push_delay_random = 8;
2452 element_info[i].push_delay_fixed = 8;
2453 element_info[i].push_delay_random = 8;
2458 /* set push delay value for certain elements from pre-defined list */
2459 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2461 int e = push_delay_list[i].element;
2463 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2464 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2467 /* set push delay value for Supaplex elements for newer engine versions */
2468 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2470 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2472 if (IS_SP_ELEMENT(i))
2474 /* set SP push delay to just enough to push under a falling zonk */
2475 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2477 element_info[i].push_delay_fixed = delay;
2478 element_info[i].push_delay_random = 0;
2483 /* ---------- initialize move stepsize ----------------------------------- */
2485 /* initialize move stepsize values to default */
2486 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2487 if (!IS_CUSTOM_ELEMENT(i))
2488 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2490 /* set move stepsize value for certain elements from pre-defined list */
2491 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2493 int e = move_stepsize_list[i].element;
2495 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2498 /* ---------- initialize collect score ----------------------------------- */
2500 /* initialize collect score values for custom elements from initial value */
2501 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2502 if (IS_CUSTOM_ELEMENT(i))
2503 element_info[i].collect_score = element_info[i].collect_score_initial;
2505 /* ---------- initialize collect count ----------------------------------- */
2507 /* initialize collect count values for non-custom elements */
2508 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2509 if (!IS_CUSTOM_ELEMENT(i))
2510 element_info[i].collect_count_initial = 0;
2512 /* add collect count values for all elements from pre-defined list */
2513 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2514 element_info[collect_count_list[i].element].collect_count_initial =
2515 collect_count_list[i].count;
2517 /* ---------- initialize access direction -------------------------------- */
2519 /* initialize access direction values to default (access from every side) */
2520 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2521 if (!IS_CUSTOM_ELEMENT(i))
2522 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2524 /* set access direction value for certain elements from pre-defined list */
2525 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2526 element_info[access_direction_list[i].element].access_direction =
2527 access_direction_list[i].direction;
2529 /* ---------- initialize explosion content ------------------------------- */
2530 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2532 if (IS_CUSTOM_ELEMENT(i))
2535 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2537 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2539 element_info[i].content.e[x][y] =
2540 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2541 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2542 i == EL_PLAYER_3 ? EL_EMERALD :
2543 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2544 i == EL_MOLE ? EL_EMERALD_RED :
2545 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2546 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2547 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2548 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2549 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2550 i == EL_WALL_EMERALD ? EL_EMERALD :
2551 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2552 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2553 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2554 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2555 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2556 i == EL_WALL_PEARL ? EL_PEARL :
2557 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2562 /* ---------- initialize recursion detection ------------------------------ */
2563 recursion_loop_depth = 0;
2564 recursion_loop_detected = FALSE;
2565 recursion_loop_element = EL_UNDEFINED;
2568 int get_num_special_action(int element, int action_first, int action_last)
2570 int num_special_action = 0;
2573 for (i = action_first; i <= action_last; i++)
2575 boolean found = FALSE;
2577 for (j = 0; j < NUM_DIRECTIONS; j++)
2578 if (el_act_dir2img(element, i, j) !=
2579 el_act_dir2img(element, ACTION_DEFAULT, j))
2583 num_special_action++;
2588 return num_special_action;
2593 =============================================================================
2595 -----------------------------------------------------------------------------
2596 initialize and start new game
2597 =============================================================================
2602 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2603 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2604 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2605 boolean do_fading = (game_status == GAME_MODE_MAIN);
2608 game_status = GAME_MODE_PLAYING;
2612 /* don't play tapes over network */
2613 network_playing = (options.network && !tape.playing);
2615 for (i = 0; i < MAX_PLAYERS; i++)
2617 struct PlayerInfo *player = &stored_player[i];
2619 player->index_nr = i;
2620 player->index_bit = (1 << i);
2621 player->element_nr = EL_PLAYER_1 + i;
2623 player->present = FALSE;
2624 player->active = FALSE;
2625 player->killed = FALSE;
2628 player->effective_action = 0;
2629 player->programmed_action = 0;
2632 player->score_final = 0;
2634 player->gems_still_needed = level.gems_needed;
2635 player->sokobanfields_still_needed = 0;
2636 player->lights_still_needed = 0;
2637 player->friends_still_needed = 0;
2639 for (j = 0; j < MAX_NUM_KEYS; j++)
2640 player->key[j] = FALSE;
2642 player->num_white_keys = 0;
2644 player->dynabomb_count = 0;
2645 player->dynabomb_size = 1;
2646 player->dynabombs_left = 0;
2647 player->dynabomb_xl = FALSE;
2649 player->MovDir = MV_NONE;
2652 player->GfxDir = MV_NONE;
2653 player->GfxAction = ACTION_DEFAULT;
2655 player->StepFrame = 0;
2657 player->use_murphy = FALSE;
2658 player->artwork_element =
2659 (level.use_artwork_element[i] ? level.artwork_element[i] :
2660 player->element_nr);
2662 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2663 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2665 player->gravity = level.initial_player_gravity[i];
2667 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2669 player->actual_frame_counter = 0;
2671 player->step_counter = 0;
2673 player->last_move_dir = MV_NONE;
2675 player->is_active = FALSE;
2677 player->is_waiting = FALSE;
2678 player->is_moving = FALSE;
2679 player->is_auto_moving = FALSE;
2680 player->is_digging = FALSE;
2681 player->is_snapping = FALSE;
2682 player->is_collecting = FALSE;
2683 player->is_pushing = FALSE;
2684 player->is_switching = FALSE;
2685 player->is_dropping = FALSE;
2686 player->is_dropping_pressed = FALSE;
2688 player->is_bored = FALSE;
2689 player->is_sleeping = FALSE;
2691 player->frame_counter_bored = -1;
2692 player->frame_counter_sleeping = -1;
2694 player->anim_delay_counter = 0;
2695 player->post_delay_counter = 0;
2697 player->dir_waiting = MV_NONE;
2698 player->action_waiting = ACTION_DEFAULT;
2699 player->last_action_waiting = ACTION_DEFAULT;
2700 player->special_action_bored = ACTION_DEFAULT;
2701 player->special_action_sleeping = ACTION_DEFAULT;
2703 player->switch_x = -1;
2704 player->switch_y = -1;
2706 player->drop_x = -1;
2707 player->drop_y = -1;
2709 player->show_envelope = 0;
2711 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2713 player->push_delay = -1; /* initialized when pushing starts */
2714 player->push_delay_value = game.initial_push_delay_value;
2716 player->drop_delay = 0;
2717 player->drop_pressed_delay = 0;
2719 player->last_jx = -1;
2720 player->last_jy = -1;
2724 player->shield_normal_time_left = 0;
2725 player->shield_deadly_time_left = 0;
2727 player->inventory_infinite_element = EL_UNDEFINED;
2728 player->inventory_size = 0;
2730 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2731 SnapField(player, 0, 0);
2733 player->LevelSolved = FALSE;
2734 player->GameOver = FALSE;
2736 player->LevelSolved_GameWon = FALSE;
2737 player->LevelSolved_GameEnd = FALSE;
2738 player->LevelSolved_PanelOff = FALSE;
2739 player->LevelSolved_SaveTape = FALSE;
2740 player->LevelSolved_SaveScore = FALSE;
2743 network_player_action_received = FALSE;
2745 #if defined(NETWORK_AVALIABLE)
2746 /* initial null action */
2747 if (network_playing)
2748 SendToServer_MovePlayer(MV_NONE);
2757 TimeLeft = level.time;
2760 ScreenMovDir = MV_NONE;
2764 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2766 AllPlayersGone = FALSE;
2768 game.yamyam_content_nr = 0;
2769 game.magic_wall_active = FALSE;
2770 game.magic_wall_time_left = 0;
2771 game.light_time_left = 0;
2772 game.timegate_time_left = 0;
2773 game.switchgate_pos = 0;
2774 game.wind_direction = level.wind_direction_initial;
2776 #if !USE_PLAYER_GRAVITY
2777 game.gravity = FALSE;
2778 game.explosions_delayed = TRUE;
2781 game.lenses_time_left = 0;
2782 game.magnify_time_left = 0;
2784 game.ball_state = level.ball_state_initial;
2785 game.ball_content_nr = 0;
2787 game.envelope_active = FALSE;
2789 /* set focus to local player for network games, else to all players */
2790 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2791 game.centered_player_nr_next = game.centered_player_nr;
2792 game.set_centered_player = FALSE;
2794 if (network_playing && tape.recording)
2796 /* store client dependent player focus when recording network games */
2797 tape.centered_player_nr_next = game.centered_player_nr_next;
2798 tape.set_centered_player = TRUE;
2801 for (i = 0; i < NUM_BELTS; i++)
2803 game.belt_dir[i] = MV_NONE;
2804 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2807 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2808 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2810 SCAN_PLAYFIELD(x, y)
2812 Feld[x][y] = level.field[x][y];
2813 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2814 ChangeDelay[x][y] = 0;
2815 ChangePage[x][y] = -1;
2816 #if USE_NEW_CUSTOM_VALUE
2817 CustomValue[x][y] = 0; /* initialized in InitField() */
2819 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2821 WasJustMoving[x][y] = 0;
2822 WasJustFalling[x][y] = 0;
2823 CheckCollision[x][y] = 0;
2824 CheckImpact[x][y] = 0;
2826 Pushed[x][y] = FALSE;
2828 ChangeCount[x][y] = 0;
2829 ChangeEvent[x][y] = -1;
2831 ExplodePhase[x][y] = 0;
2832 ExplodeDelay[x][y] = 0;
2833 ExplodeField[x][y] = EX_TYPE_NONE;
2835 RunnerVisit[x][y] = 0;
2836 PlayerVisit[x][y] = 0;
2839 GfxRandom[x][y] = INIT_GFX_RANDOM();
2840 GfxElement[x][y] = EL_UNDEFINED;
2841 GfxAction[x][y] = ACTION_DEFAULT;
2842 GfxDir[x][y] = MV_NONE;
2845 SCAN_PLAYFIELD(x, y)
2847 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2849 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2851 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2854 InitField(x, y, TRUE);
2859 for (i = 0; i < MAX_PLAYERS; i++)
2861 struct PlayerInfo *player = &stored_player[i];
2863 /* set number of special actions for bored and sleeping animation */
2864 player->num_special_action_bored =
2865 get_num_special_action(player->artwork_element,
2866 ACTION_BORING_1, ACTION_BORING_LAST);
2867 player->num_special_action_sleeping =
2868 get_num_special_action(player->artwork_element,
2869 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2872 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2873 emulate_sb ? EMU_SOKOBAN :
2874 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2876 #if USE_NEW_ALL_SLIPPERY
2877 /* initialize type of slippery elements */
2878 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2880 if (!IS_CUSTOM_ELEMENT(i))
2882 /* default: elements slip down either to the left or right randomly */
2883 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2885 /* SP style elements prefer to slip down on the left side */
2886 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2887 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2889 /* BD style elements prefer to slip down on the left side */
2890 if (game.emulation == EMU_BOULDERDASH)
2891 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2896 /* initialize explosion and ignition delay */
2897 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2899 if (!IS_CUSTOM_ELEMENT(i))
2902 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2903 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2904 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2905 int last_phase = (num_phase + 1) * delay;
2906 int half_phase = (num_phase / 2) * delay;
2908 element_info[i].explosion_delay = last_phase - 1;
2909 element_info[i].ignition_delay = half_phase;
2911 if (i == EL_BLACK_ORB)
2912 element_info[i].ignition_delay = 1;
2916 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2917 element_info[i].explosion_delay = 1;
2919 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2920 element_info[i].ignition_delay = 1;
2924 /* correct non-moving belts to start moving left */
2925 for (i = 0; i < NUM_BELTS; i++)
2926 if (game.belt_dir[i] == MV_NONE)
2927 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2929 /* check if any connected player was not found in playfield */
2930 for (i = 0; i < MAX_PLAYERS; i++)
2932 struct PlayerInfo *player = &stored_player[i];
2934 if (player->connected && !player->present)
2936 for (j = 0; j < MAX_PLAYERS; j++)
2938 struct PlayerInfo *some_player = &stored_player[j];
2939 int jx = some_player->jx, jy = some_player->jy;
2941 /* assign first free player found that is present in the playfield */
2942 if (some_player->present && !some_player->connected)
2944 player->present = TRUE;
2945 player->active = TRUE;
2947 some_player->present = FALSE;
2948 some_player->active = FALSE;
2950 player->artwork_element = some_player->artwork_element;
2952 player->block_last_field = some_player->block_last_field;
2953 player->block_delay_adjustment = some_player->block_delay_adjustment;
2955 StorePlayer[jx][jy] = player->element_nr;
2956 player->jx = player->last_jx = jx;
2957 player->jy = player->last_jy = jy;
2967 /* when playing a tape, eliminate all players who do not participate */
2969 for (i = 0; i < MAX_PLAYERS; i++)
2971 if (stored_player[i].active && !tape.player_participates[i])
2973 struct PlayerInfo *player = &stored_player[i];
2974 int jx = player->jx, jy = player->jy;
2976 player->active = FALSE;
2977 StorePlayer[jx][jy] = 0;
2978 Feld[jx][jy] = EL_EMPTY;
2982 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2984 /* when in single player mode, eliminate all but the first active player */
2986 for (i = 0; i < MAX_PLAYERS; i++)
2988 if (stored_player[i].active)
2990 for (j = i + 1; j < MAX_PLAYERS; j++)
2992 if (stored_player[j].active)
2994 struct PlayerInfo *player = &stored_player[j];
2995 int jx = player->jx, jy = player->jy;
2997 player->active = FALSE;
2998 player->present = FALSE;
3000 StorePlayer[jx][jy] = 0;
3001 Feld[jx][jy] = EL_EMPTY;
3008 /* when recording the game, store which players take part in the game */
3011 for (i = 0; i < MAX_PLAYERS; i++)
3012 if (stored_player[i].active)
3013 tape.player_participates[i] = TRUE;
3018 for (i = 0; i < MAX_PLAYERS; i++)
3020 struct PlayerInfo *player = &stored_player[i];
3022 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3027 if (local_player == player)
3028 printf("Player %d is local player.\n", i+1);
3032 if (BorderElement == EL_EMPTY)
3035 SBX_Right = lev_fieldx - SCR_FIELDX;
3037 SBY_Lower = lev_fieldy - SCR_FIELDY;
3042 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3044 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3047 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3048 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3050 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3051 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3053 /* if local player not found, look for custom element that might create
3054 the player (make some assumptions about the right custom element) */
3055 if (!local_player->present)
3057 int start_x = 0, start_y = 0;
3058 int found_rating = 0;
3059 int found_element = EL_UNDEFINED;
3060 int player_nr = local_player->index_nr;
3062 SCAN_PLAYFIELD(x, y)
3064 int element = Feld[x][y];
3069 if (level.use_start_element[player_nr] &&
3070 level.start_element[player_nr] == element &&
3077 found_element = element;
3080 if (!IS_CUSTOM_ELEMENT(element))
3083 if (CAN_CHANGE(element))
3085 for (i = 0; i < element_info[element].num_change_pages; i++)
3087 /* check for player created from custom element as single target */
3088 content = element_info[element].change_page[i].target_element;
3089 is_player = ELEM_IS_PLAYER(content);
3091 if (is_player && (found_rating < 3 || element < found_element))
3097 found_element = element;
3102 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3104 /* check for player created from custom element as explosion content */
3105 content = element_info[element].content.e[xx][yy];
3106 is_player = ELEM_IS_PLAYER(content);
3108 if (is_player && (found_rating < 2 || element < found_element))
3110 start_x = x + xx - 1;
3111 start_y = y + yy - 1;
3114 found_element = element;
3117 if (!CAN_CHANGE(element))
3120 for (i = 0; i < element_info[element].num_change_pages; i++)
3122 /* check for player created from custom element as extended target */
3124 element_info[element].change_page[i].target_content.e[xx][yy];
3126 is_player = ELEM_IS_PLAYER(content);
3128 if (is_player && (found_rating < 1 || element < found_element))
3130 start_x = x + xx - 1;
3131 start_y = y + yy - 1;
3134 found_element = element;
3140 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3141 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3144 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3145 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3150 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3151 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3152 local_player->jx - MIDPOSX);
3154 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3155 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3156 local_player->jy - MIDPOSY);
3161 if (!game.restart_level)
3162 CloseDoor(DOOR_CLOSE_1);
3165 FadeOut(REDRAW_FIELD);
3167 /* !!! FIX THIS (START) !!! */
3168 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3170 InitGameEngine_EM();
3172 /* blit playfield from scroll buffer to normal back buffer for fading in */
3173 BlitScreenToBitmap_EM(backbuffer);
3180 /* after drawing the level, correct some elements */
3181 if (game.timegate_time_left == 0)
3182 CloseAllOpenTimegates();
3184 /* blit playfield from scroll buffer to normal back buffer for fading in */
3185 if (setup.soft_scrolling)
3186 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3188 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3190 /* !!! FIX THIS (END) !!! */
3193 FadeIn(REDRAW_FIELD);
3197 if (!game.restart_level)
3199 /* copy default game door content to main double buffer */
3200 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3201 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3204 SetPanelBackground();
3205 SetDrawBackgroundMask(REDRAW_DOOR_1);
3207 DrawGameDoorValues();
3209 if (!game.restart_level)
3213 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3214 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3215 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3219 /* copy actual game door content to door double buffer for OpenDoor() */
3220 BlitBitmap(drawto, bitmap_db_door,
3221 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3223 OpenDoor(DOOR_OPEN_ALL);
3225 PlaySound(SND_GAME_STARTING);
3227 if (setup.sound_music)
3230 KeyboardAutoRepeatOffUnlessAutoplay();
3234 for (i = 0; i < MAX_PLAYERS; i++)
3235 printf("Player %d %sactive.\n",
3236 i + 1, (stored_player[i].active ? "" : "not "));
3247 game.restart_level = FALSE;
3250 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3252 /* this is used for non-R'n'D game engines to update certain engine values */
3254 /* needed to determine if sounds are played within the visible screen area */
3255 scroll_x = actual_scroll_x;
3256 scroll_y = actual_scroll_y;
3259 void InitMovDir(int x, int y)
3261 int i, element = Feld[x][y];
3262 static int xy[4][2] =
3269 static int direction[3][4] =
3271 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3272 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3273 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3282 Feld[x][y] = EL_BUG;
3283 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3286 case EL_SPACESHIP_RIGHT:
3287 case EL_SPACESHIP_UP:
3288 case EL_SPACESHIP_LEFT:
3289 case EL_SPACESHIP_DOWN:
3290 Feld[x][y] = EL_SPACESHIP;
3291 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3294 case EL_BD_BUTTERFLY_RIGHT:
3295 case EL_BD_BUTTERFLY_UP:
3296 case EL_BD_BUTTERFLY_LEFT:
3297 case EL_BD_BUTTERFLY_DOWN:
3298 Feld[x][y] = EL_BD_BUTTERFLY;
3299 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3302 case EL_BD_FIREFLY_RIGHT:
3303 case EL_BD_FIREFLY_UP:
3304 case EL_BD_FIREFLY_LEFT:
3305 case EL_BD_FIREFLY_DOWN:
3306 Feld[x][y] = EL_BD_FIREFLY;
3307 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3310 case EL_PACMAN_RIGHT:
3312 case EL_PACMAN_LEFT:
3313 case EL_PACMAN_DOWN:
3314 Feld[x][y] = EL_PACMAN;
3315 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3318 case EL_YAMYAM_LEFT:
3319 case EL_YAMYAM_RIGHT:
3321 case EL_YAMYAM_DOWN:
3322 Feld[x][y] = EL_YAMYAM;
3323 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3326 case EL_SP_SNIKSNAK:
3327 MovDir[x][y] = MV_UP;
3330 case EL_SP_ELECTRON:
3331 MovDir[x][y] = MV_LEFT;
3338 Feld[x][y] = EL_MOLE;
3339 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3343 if (IS_CUSTOM_ELEMENT(element))
3345 struct ElementInfo *ei = &element_info[element];
3346 int move_direction_initial = ei->move_direction_initial;
3347 int move_pattern = ei->move_pattern;
3349 if (move_direction_initial == MV_START_PREVIOUS)
3351 if (MovDir[x][y] != MV_NONE)
3354 move_direction_initial = MV_START_AUTOMATIC;
3357 if (move_direction_initial == MV_START_RANDOM)
3358 MovDir[x][y] = 1 << RND(4);
3359 else if (move_direction_initial & MV_ANY_DIRECTION)
3360 MovDir[x][y] = move_direction_initial;
3361 else if (move_pattern == MV_ALL_DIRECTIONS ||
3362 move_pattern == MV_TURNING_LEFT ||
3363 move_pattern == MV_TURNING_RIGHT ||
3364 move_pattern == MV_TURNING_LEFT_RIGHT ||
3365 move_pattern == MV_TURNING_RIGHT_LEFT ||
3366 move_pattern == MV_TURNING_RANDOM)
3367 MovDir[x][y] = 1 << RND(4);
3368 else if (move_pattern == MV_HORIZONTAL)
3369 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3370 else if (move_pattern == MV_VERTICAL)
3371 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3372 else if (move_pattern & MV_ANY_DIRECTION)
3373 MovDir[x][y] = element_info[element].move_pattern;
3374 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3375 move_pattern == MV_ALONG_RIGHT_SIDE)
3377 /* use random direction as default start direction */
3378 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3379 MovDir[x][y] = 1 << RND(4);
3381 for (i = 0; i < NUM_DIRECTIONS; i++)
3383 int x1 = x + xy[i][0];
3384 int y1 = y + xy[i][1];
3386 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3388 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3389 MovDir[x][y] = direction[0][i];
3391 MovDir[x][y] = direction[1][i];
3400 MovDir[x][y] = 1 << RND(4);
3402 if (element != EL_BUG &&
3403 element != EL_SPACESHIP &&
3404 element != EL_BD_BUTTERFLY &&
3405 element != EL_BD_FIREFLY)
3408 for (i = 0; i < NUM_DIRECTIONS; i++)
3410 int x1 = x + xy[i][0];
3411 int y1 = y + xy[i][1];
3413 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3415 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3417 MovDir[x][y] = direction[0][i];
3420 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3421 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3423 MovDir[x][y] = direction[1][i];
3432 GfxDir[x][y] = MovDir[x][y];
3435 void InitAmoebaNr(int x, int y)
3438 int group_nr = AmoebeNachbarNr(x, y);
3442 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3444 if (AmoebaCnt[i] == 0)
3452 AmoebaNr[x][y] = group_nr;
3453 AmoebaCnt[group_nr]++;
3454 AmoebaCnt2[group_nr]++;
3457 static void PlayerWins(struct PlayerInfo *player)
3459 player->LevelSolved = TRUE;
3460 player->GameOver = TRUE;
3462 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3463 level.native_em_level->lev->score : player->score);
3468 static int time, time_final;
3469 static int score, score_final;
3470 static int game_over_delay_1 = 0;
3471 static int game_over_delay_2 = 0;
3472 int game_over_delay_value_1 = 50;
3473 int game_over_delay_value_2 = 50;
3475 if (!local_player->LevelSolved_GameWon)
3479 /* do not start end game actions before the player stops moving (to exit) */
3480 if (local_player->MovPos)
3483 local_player->LevelSolved_GameWon = TRUE;
3484 local_player->LevelSolved_SaveTape = tape.recording;
3485 local_player->LevelSolved_SaveScore = !tape.playing;
3487 if (tape.auto_play) /* tape might already be stopped here */
3488 tape.auto_play_level_solved = TRUE;
3494 game_over_delay_1 = game_over_delay_value_1;
3495 game_over_delay_2 = game_over_delay_value_2;
3497 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3498 score = score_final = local_player->score_final;
3503 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3505 else if (level.time == 0 && TimePlayed < 999)
3508 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3511 local_player->score_final = score_final;
3513 if (level_editor_test_game)
3516 score = score_final;
3518 DrawGameValue_Time(time);
3519 DrawGameValue_Score(score);
3522 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3524 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3526 /* close exit door after last player */
3527 if ((AllPlayersGone &&
3528 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3529 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3530 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3531 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3532 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3534 int element = Feld[ExitX][ExitY];
3537 if (element == EL_EM_EXIT_OPEN ||
3538 element == EL_EM_STEEL_EXIT_OPEN)
3545 Feld[ExitX][ExitY] =
3546 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3547 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3548 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3549 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3550 EL_EM_STEEL_EXIT_CLOSING);
3552 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3556 /* player disappears */
3557 DrawLevelField(ExitX, ExitY);
3560 for (i = 0; i < MAX_PLAYERS; i++)
3562 struct PlayerInfo *player = &stored_player[i];
3564 if (player->present)
3566 RemovePlayer(player);
3568 /* player disappears */
3569 DrawLevelField(player->jx, player->jy);
3574 PlaySound(SND_GAME_WINNING);
3577 if (game_over_delay_1 > 0)
3579 game_over_delay_1--;
3584 if (time != time_final)
3586 int time_to_go = ABS(time_final - time);
3587 int time_count_dir = (time < time_final ? +1 : -1);
3588 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3590 time += time_count_steps * time_count_dir;
3591 score += time_count_steps * level.score[SC_TIME_BONUS];
3593 DrawGameValue_Time(time);
3594 DrawGameValue_Score(score);
3596 if (time == time_final)
3597 StopSound(SND_GAME_LEVELTIME_BONUS);
3598 else if (setup.sound_loops)
3599 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3601 PlaySound(SND_GAME_LEVELTIME_BONUS);
3606 local_player->LevelSolved_PanelOff = TRUE;
3608 if (game_over_delay_2 > 0)
3610 game_over_delay_2--;
3623 boolean raise_level = FALSE;
3625 local_player->LevelSolved_GameEnd = TRUE;
3627 CloseDoor(DOOR_CLOSE_1);
3629 if (local_player->LevelSolved_SaveTape)
3636 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3638 SaveTape(tape.level_nr); /* ask to save tape */
3642 if (level_editor_test_game)
3644 game_status = GAME_MODE_MAIN;
3651 if (!local_player->LevelSolved_SaveScore)
3653 FadeOut(REDRAW_FIELD);
3655 game_status = GAME_MODE_MAIN;
3657 DrawAndFadeInMainMenu(REDRAW_FIELD);
3662 if (level_nr == leveldir_current->handicap_level)
3664 leveldir_current->handicap_level++;
3665 SaveLevelSetup_SeriesInfo();
3668 if (level_nr < leveldir_current->last_level)
3669 raise_level = TRUE; /* advance to next level */
3671 if ((hi_pos = NewHiScore()) >= 0)
3673 game_status = GAME_MODE_SCORES;
3675 DrawHallOfFame(hi_pos);
3685 FadeOut(REDRAW_FIELD);
3687 game_status = GAME_MODE_MAIN;
3695 DrawAndFadeInMainMenu(REDRAW_FIELD);
3704 LoadScore(level_nr);
3706 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3707 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3710 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3712 if (local_player->score_final > highscore[k].Score)
3714 /* player has made it to the hall of fame */
3716 if (k < MAX_SCORE_ENTRIES - 1)
3718 int m = MAX_SCORE_ENTRIES - 1;
3721 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3722 if (strEqual(setup.player_name, highscore[l].Name))
3724 if (m == k) /* player's new highscore overwrites his old one */
3728 for (l = m; l > k; l--)
3730 strcpy(highscore[l].Name, highscore[l - 1].Name);
3731 highscore[l].Score = highscore[l - 1].Score;
3738 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3739 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3740 highscore[k].Score = local_player->score_final;
3746 else if (!strncmp(setup.player_name, highscore[k].Name,
3747 MAX_PLAYER_NAME_LEN))
3748 break; /* player already there with a higher score */
3754 SaveScore(level_nr);
3759 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3761 int element = Feld[x][y];
3762 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3763 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3764 int horiz_move = (dx != 0);
3765 int sign = (horiz_move ? dx : dy);
3766 int step = sign * element_info[element].move_stepsize;
3768 /* special values for move stepsize for spring and things on conveyor belt */
3771 if (CAN_FALL(element) &&
3772 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3773 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3774 else if (element == EL_SPRING)
3775 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3781 inline static int getElementMoveStepsize(int x, int y)
3783 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3786 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3788 if (player->GfxAction != action || player->GfxDir != dir)
3791 printf("Player frame reset! (%d => %d, %d => %d)\n",
3792 player->GfxAction, action, player->GfxDir, dir);
3795 player->GfxAction = action;
3796 player->GfxDir = dir;
3798 player->StepFrame = 0;
3802 #if USE_GFX_RESET_GFX_ANIMATION
3803 static void ResetGfxFrame(int x, int y, boolean redraw)
3805 int element = Feld[x][y];
3806 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3807 int last_gfx_frame = GfxFrame[x][y];
3809 if (graphic_info[graphic].anim_global_sync)
3810 GfxFrame[x][y] = FrameCounter;
3811 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3812 GfxFrame[x][y] = CustomValue[x][y];
3813 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3814 GfxFrame[x][y] = element_info[element].collect_score;
3815 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3816 GfxFrame[x][y] = ChangeDelay[x][y];
3818 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3819 DrawLevelGraphicAnimation(x, y, graphic);
3823 static void ResetGfxAnimation(int x, int y)
3825 GfxAction[x][y] = ACTION_DEFAULT;
3826 GfxDir[x][y] = MovDir[x][y];
3829 #if USE_GFX_RESET_GFX_ANIMATION
3830 ResetGfxFrame(x, y, FALSE);
3834 static void ResetRandomAnimationValue(int x, int y)
3836 GfxRandom[x][y] = INIT_GFX_RANDOM();
3839 void InitMovingField(int x, int y, int direction)
3841 int element = Feld[x][y];
3842 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3843 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3846 boolean is_moving_before, is_moving_after;
3848 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3851 /* check if element was/is moving or being moved before/after mode change */
3854 is_moving_before = (WasJustMoving[x][y] != 0);
3856 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3857 is_moving_before = WasJustMoving[x][y];
3860 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3862 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3864 /* reset animation only for moving elements which change direction of moving
3865 or which just started or stopped moving
3866 (else CEs with property "can move" / "not moving" are reset each frame) */
3867 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3869 if (is_moving_before != is_moving_after ||
3870 direction != MovDir[x][y])
3871 ResetGfxAnimation(x, y);
3873 if ((is_moving_before || is_moving_after) && !continues_moving)
3874 ResetGfxAnimation(x, y);
3877 if (!continues_moving)
3878 ResetGfxAnimation(x, y);
3881 MovDir[x][y] = direction;
3882 GfxDir[x][y] = direction;
3884 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3885 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3886 direction == MV_DOWN && CAN_FALL(element) ?
3887 ACTION_FALLING : ACTION_MOVING);
3889 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3890 ACTION_FALLING : ACTION_MOVING);
3893 /* this is needed for CEs with property "can move" / "not moving" */
3895 if (is_moving_after)
3897 if (Feld[newx][newy] == EL_EMPTY)
3898 Feld[newx][newy] = EL_BLOCKED;
3900 MovDir[newx][newy] = MovDir[x][y];
3902 #if USE_NEW_CUSTOM_VALUE
3903 CustomValue[newx][newy] = CustomValue[x][y];
3906 GfxFrame[newx][newy] = GfxFrame[x][y];
3907 GfxRandom[newx][newy] = GfxRandom[x][y];
3908 GfxAction[newx][newy] = GfxAction[x][y];
3909 GfxDir[newx][newy] = GfxDir[x][y];
3913 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3915 int direction = MovDir[x][y];
3916 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3917 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3923 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3925 int oldx = x, oldy = y;
3926 int direction = MovDir[x][y];
3928 if (direction == MV_LEFT)
3930 else if (direction == MV_RIGHT)
3932 else if (direction == MV_UP)
3934 else if (direction == MV_DOWN)
3937 *comes_from_x = oldx;
3938 *comes_from_y = oldy;
3941 int MovingOrBlocked2Element(int x, int y)
3943 int element = Feld[x][y];
3945 if (element == EL_BLOCKED)
3949 Blocked2Moving(x, y, &oldx, &oldy);
3950 return Feld[oldx][oldy];
3956 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3958 /* like MovingOrBlocked2Element(), but if element is moving
3959 and (x,y) is the field the moving element is just leaving,
3960 return EL_BLOCKED instead of the element value */
3961 int element = Feld[x][y];
3963 if (IS_MOVING(x, y))
3965 if (element == EL_BLOCKED)
3969 Blocked2Moving(x, y, &oldx, &oldy);
3970 return Feld[oldx][oldy];
3979 static void RemoveField(int x, int y)
3981 Feld[x][y] = EL_EMPTY;
3987 #if USE_NEW_CUSTOM_VALUE
3988 CustomValue[x][y] = 0;
3992 ChangeDelay[x][y] = 0;
3993 ChangePage[x][y] = -1;
3994 Pushed[x][y] = FALSE;
3997 ExplodeField[x][y] = EX_TYPE_NONE;
4000 GfxElement[x][y] = EL_UNDEFINED;
4001 GfxAction[x][y] = ACTION_DEFAULT;
4002 GfxDir[x][y] = MV_NONE;
4005 void RemoveMovingField(int x, int y)
4007 int oldx = x, oldy = y, newx = x, newy = y;
4008 int element = Feld[x][y];
4009 int next_element = EL_UNDEFINED;
4011 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4014 if (IS_MOVING(x, y))
4016 Moving2Blocked(x, y, &newx, &newy);
4018 if (Feld[newx][newy] != EL_BLOCKED)
4020 /* element is moving, but target field is not free (blocked), but
4021 already occupied by something different (example: acid pool);
4022 in this case, only remove the moving field, but not the target */
4024 RemoveField(oldx, oldy);
4026 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4028 DrawLevelField(oldx, oldy);
4033 else if (element == EL_BLOCKED)
4035 Blocked2Moving(x, y, &oldx, &oldy);
4036 if (!IS_MOVING(oldx, oldy))
4040 if (element == EL_BLOCKED &&
4041 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4042 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4043 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4044 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4045 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4046 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4047 next_element = get_next_element(Feld[oldx][oldy]);
4049 RemoveField(oldx, oldy);
4050 RemoveField(newx, newy);
4052 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4054 if (next_element != EL_UNDEFINED)
4055 Feld[oldx][oldy] = next_element;
4057 DrawLevelField(oldx, oldy);
4058 DrawLevelField(newx, newy);
4061 void DrawDynamite(int x, int y)
4063 int sx = SCREENX(x), sy = SCREENY(y);
4064 int graphic = el2img(Feld[x][y]);
4067 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4070 if (IS_WALKABLE_INSIDE(Back[x][y]))
4074 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4075 else if (Store[x][y])
4076 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4078 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4080 if (Back[x][y] || Store[x][y])
4081 DrawGraphicThruMask(sx, sy, graphic, frame);
4083 DrawGraphic(sx, sy, graphic, frame);
4086 void CheckDynamite(int x, int y)
4088 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4092 if (MovDelay[x][y] != 0)
4095 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4101 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4106 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4108 boolean num_checked_players = 0;
4111 for (i = 0; i < MAX_PLAYERS; i++)
4113 if (stored_player[i].active)
4115 int sx = stored_player[i].jx;
4116 int sy = stored_player[i].jy;
4118 if (num_checked_players == 0)
4125 *sx1 = MIN(*sx1, sx);
4126 *sy1 = MIN(*sy1, sy);
4127 *sx2 = MAX(*sx2, sx);
4128 *sy2 = MAX(*sy2, sy);
4131 num_checked_players++;
4136 static boolean checkIfAllPlayersFitToScreen_RND()
4138 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4140 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4142 return (sx2 - sx1 < SCR_FIELDX &&
4143 sy2 - sy1 < SCR_FIELDY);
4146 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4148 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4150 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4152 *sx = (sx1 + sx2) / 2;
4153 *sy = (sy1 + sy2) / 2;
4156 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4157 boolean center_screen, boolean quick_relocation)
4159 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4160 boolean no_delay = (tape.warp_forward);
4161 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4162 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4164 if (quick_relocation)
4166 int offset = (setup.scroll_delay ? 3 : 0);
4168 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4172 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4173 x > SBX_Right + MIDPOSX ? SBX_Right :
4176 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4177 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4182 /* quick relocation (without scrolling), but do not center screen */
4184 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4185 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4188 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4189 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4192 int offset_x = x + (scroll_x - center_scroll_x);
4193 int offset_y = y + (scroll_y - center_scroll_y);
4195 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4196 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4197 offset_x - MIDPOSX);
4199 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4200 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4201 offset_y - MIDPOSY);
4206 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4207 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4208 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4210 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4211 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4212 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4214 /* don't scroll over playfield boundaries */
4215 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4216 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4218 /* don't scroll over playfield boundaries */
4219 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4220 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4223 RedrawPlayfield(TRUE, 0,0,0,0);
4227 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4228 x > SBX_Right + MIDPOSX ? SBX_Right :
4231 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4232 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4235 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4237 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4240 int fx = FX, fy = FY;
4242 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4243 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4245 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4251 fx += dx * TILEX / 2;
4252 fy += dy * TILEY / 2;
4254 ScrollLevel(dx, dy);
4257 /* scroll in two steps of half tile size to make things smoother */
4258 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4260 Delay(wait_delay_value);
4262 /* scroll second step to align at full tile size */
4264 Delay(wait_delay_value);
4269 Delay(wait_delay_value);
4273 void RelocatePlayer(int jx, int jy, int el_player_raw)
4275 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4276 int player_nr = GET_PLAYER_NR(el_player);
4277 struct PlayerInfo *player = &stored_player[player_nr];
4278 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4279 boolean no_delay = (tape.warp_forward);
4280 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4281 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4282 int old_jx = player->jx;
4283 int old_jy = player->jy;
4284 int old_element = Feld[old_jx][old_jy];
4285 int element = Feld[jx][jy];
4286 boolean player_relocated = (old_jx != jx || old_jy != jy);
4288 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4289 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4290 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4291 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4292 int leave_side_horiz = move_dir_horiz;
4293 int leave_side_vert = move_dir_vert;
4294 int enter_side = enter_side_horiz | enter_side_vert;
4295 int leave_side = leave_side_horiz | leave_side_vert;
4297 if (player->GameOver) /* do not reanimate dead player */
4300 if (!player_relocated) /* no need to relocate the player */
4303 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4305 RemoveField(jx, jy); /* temporarily remove newly placed player */
4306 DrawLevelField(jx, jy);
4309 if (player->present)
4311 while (player->MovPos)
4313 ScrollPlayer(player, SCROLL_GO_ON);
4314 ScrollScreen(NULL, SCROLL_GO_ON);
4316 AdvanceFrameAndPlayerCounters(player->index_nr);
4321 Delay(wait_delay_value);
4324 DrawPlayer(player); /* needed here only to cleanup last field */
4325 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4327 player->is_moving = FALSE;
4330 if (IS_CUSTOM_ELEMENT(old_element))
4331 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4333 player->index_bit, leave_side);
4335 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4337 player->index_bit, leave_side);
4339 Feld[jx][jy] = el_player;
4340 InitPlayerField(jx, jy, el_player, TRUE);
4342 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4344 Feld[jx][jy] = element;
4345 InitField(jx, jy, FALSE);
4348 /* only visually relocate centered player */
4349 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4350 FALSE, level.instant_relocation);
4352 TestIfPlayerTouchesBadThing(jx, jy);
4353 TestIfPlayerTouchesCustomElement(jx, jy);
4355 if (IS_CUSTOM_ELEMENT(element))
4356 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4357 player->index_bit, enter_side);
4359 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4360 player->index_bit, enter_side);
4363 void Explode(int ex, int ey, int phase, int mode)
4369 /* !!! eliminate this variable !!! */
4370 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4372 if (game.explosions_delayed)
4374 ExplodeField[ex][ey] = mode;
4378 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4380 int center_element = Feld[ex][ey];
4381 int artwork_element, explosion_element; /* set these values later */
4384 /* --- This is only really needed (and now handled) in "Impact()". --- */
4385 /* do not explode moving elements that left the explode field in time */
4386 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4387 center_element == EL_EMPTY &&
4388 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4393 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4394 if (mode == EX_TYPE_NORMAL ||
4395 mode == EX_TYPE_CENTER ||
4396 mode == EX_TYPE_CROSS)
4397 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4400 /* remove things displayed in background while burning dynamite */
4401 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4404 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4406 /* put moving element to center field (and let it explode there) */
4407 center_element = MovingOrBlocked2Element(ex, ey);
4408 RemoveMovingField(ex, ey);
4409 Feld[ex][ey] = center_element;
4412 /* now "center_element" is finally determined -- set related values now */
4413 artwork_element = center_element; /* for custom player artwork */
4414 explosion_element = center_element; /* for custom player artwork */
4416 if (IS_PLAYER(ex, ey))
4418 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4420 artwork_element = stored_player[player_nr].artwork_element;
4422 if (level.use_explosion_element[player_nr])
4424 explosion_element = level.explosion_element[player_nr];
4425 artwork_element = explosion_element;
4430 if (mode == EX_TYPE_NORMAL ||
4431 mode == EX_TYPE_CENTER ||
4432 mode == EX_TYPE_CROSS)
4433 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4436 last_phase = element_info[explosion_element].explosion_delay + 1;
4438 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4440 int xx = x - ex + 1;
4441 int yy = y - ey + 1;
4444 if (!IN_LEV_FIELD(x, y) ||
4445 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4446 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4449 element = Feld[x][y];
4451 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4453 element = MovingOrBlocked2Element(x, y);
4455 if (!IS_EXPLOSION_PROOF(element))
4456 RemoveMovingField(x, y);
4459 /* indestructible elements can only explode in center (but not flames) */
4460 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4461 mode == EX_TYPE_BORDER)) ||
4462 element == EL_FLAMES)
4465 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4466 behaviour, for example when touching a yamyam that explodes to rocks
4467 with active deadly shield, a rock is created under the player !!! */
4468 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4470 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4471 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4472 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4474 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4477 if (IS_ACTIVE_BOMB(element))
4479 /* re-activate things under the bomb like gate or penguin */
4480 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4487 /* save walkable background elements while explosion on same tile */
4488 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4489 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4490 Back[x][y] = element;
4492 /* ignite explodable elements reached by other explosion */
4493 if (element == EL_EXPLOSION)
4494 element = Store2[x][y];
4496 if (AmoebaNr[x][y] &&
4497 (element == EL_AMOEBA_FULL ||
4498 element == EL_BD_AMOEBA ||
4499 element == EL_AMOEBA_GROWING))
4501 AmoebaCnt[AmoebaNr[x][y]]--;
4502 AmoebaCnt2[AmoebaNr[x][y]]--;
4507 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4509 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4511 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4513 if (PLAYERINFO(ex, ey)->use_murphy)
4514 Store[x][y] = EL_EMPTY;
4517 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4518 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4519 else if (ELEM_IS_PLAYER(center_element))
4520 Store[x][y] = EL_EMPTY;
4521 else if (center_element == EL_YAMYAM)
4522 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4523 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4524 Store[x][y] = element_info[center_element].content.e[xx][yy];
4526 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4527 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4528 otherwise) -- FIX THIS !!! */
4529 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4530 Store[x][y] = element_info[element].content.e[1][1];
4532 else if (!CAN_EXPLODE(element))
4533 Store[x][y] = element_info[element].content.e[1][1];
4536 Store[x][y] = EL_EMPTY;
4538 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4539 center_element == EL_AMOEBA_TO_DIAMOND)
4540 Store2[x][y] = element;
4542 Feld[x][y] = EL_EXPLOSION;
4543 GfxElement[x][y] = artwork_element;
4545 ExplodePhase[x][y] = 1;
4546 ExplodeDelay[x][y] = last_phase;
4551 if (center_element == EL_YAMYAM)
4552 game.yamyam_content_nr =
4553 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4565 GfxFrame[x][y] = 0; /* restart explosion animation */
4567 last_phase = ExplodeDelay[x][y];
4569 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4573 /* activate this even in non-DEBUG version until cause for crash in
4574 getGraphicAnimationFrame() (see below) is found and eliminated */
4580 /* this can happen if the player leaves an explosion just in time */
4581 if (GfxElement[x][y] == EL_UNDEFINED)
4582 GfxElement[x][y] = EL_EMPTY;
4584 if (GfxElement[x][y] == EL_UNDEFINED)
4587 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4588 printf("Explode(): This should never happen!\n");
4591 GfxElement[x][y] = EL_EMPTY;
4597 border_element = Store2[x][y];
4598 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4599 border_element = StorePlayer[x][y];
4601 if (phase == element_info[border_element].ignition_delay ||
4602 phase == last_phase)
4604 boolean border_explosion = FALSE;
4606 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4607 !PLAYER_EXPLOSION_PROTECTED(x, y))
4609 KillPlayerUnlessExplosionProtected(x, y);
4610 border_explosion = TRUE;
4612 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4614 Feld[x][y] = Store2[x][y];
4617 border_explosion = TRUE;
4619 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4621 AmoebeUmwandeln(x, y);
4623 border_explosion = TRUE;
4626 /* if an element just explodes due to another explosion (chain-reaction),
4627 do not immediately end the new explosion when it was the last frame of
4628 the explosion (as it would be done in the following "if"-statement!) */
4629 if (border_explosion && phase == last_phase)
4633 if (phase == last_phase)
4637 element = Feld[x][y] = Store[x][y];
4638 Store[x][y] = Store2[x][y] = 0;
4639 GfxElement[x][y] = EL_UNDEFINED;
4641 /* player can escape from explosions and might therefore be still alive */
4642 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4643 element <= EL_PLAYER_IS_EXPLODING_4)
4645 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4646 int explosion_element = EL_PLAYER_1 + player_nr;
4647 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4648 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4650 if (level.use_explosion_element[player_nr])
4651 explosion_element = level.explosion_element[player_nr];
4653 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4654 element_info[explosion_element].content.e[xx][yy]);
4657 /* restore probably existing indestructible background element */
4658 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4659 element = Feld[x][y] = Back[x][y];
4662 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4663 GfxDir[x][y] = MV_NONE;
4664 ChangeDelay[x][y] = 0;
4665 ChangePage[x][y] = -1;
4667 #if USE_NEW_CUSTOM_VALUE
4668 CustomValue[x][y] = 0;
4671 InitField_WithBug2(x, y, FALSE);
4673 DrawLevelField(x, y);
4675 TestIfElementTouchesCustomElement(x, y);
4677 if (GFX_CRUMBLED(element))
4678 DrawLevelFieldCrumbledSandNeighbours(x, y);
4680 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4681 StorePlayer[x][y] = 0;
4683 if (ELEM_IS_PLAYER(element))
4684 RelocatePlayer(x, y, element);
4686 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4688 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4689 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4692 DrawLevelFieldCrumbledSand(x, y);
4694 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4696 DrawLevelElement(x, y, Back[x][y]);
4697 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4699 else if (IS_WALKABLE_UNDER(Back[x][y]))
4701 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4702 DrawLevelElementThruMask(x, y, Back[x][y]);
4704 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4705 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4709 void DynaExplode(int ex, int ey)
4712 int dynabomb_element = Feld[ex][ey];
4713 int dynabomb_size = 1;
4714 boolean dynabomb_xl = FALSE;
4715 struct PlayerInfo *player;
4716 static int xy[4][2] =
4724 if (IS_ACTIVE_BOMB(dynabomb_element))
4726 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4727 dynabomb_size = player->dynabomb_size;
4728 dynabomb_xl = player->dynabomb_xl;
4729 player->dynabombs_left++;
4732 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4734 for (i = 0; i < NUM_DIRECTIONS; i++)
4736 for (j = 1; j <= dynabomb_size; j++)
4738 int x = ex + j * xy[i][0];
4739 int y = ey + j * xy[i][1];
4742 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4745 element = Feld[x][y];
4747 /* do not restart explosions of fields with active bombs */
4748 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4751 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4753 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4754 !IS_DIGGABLE(element) && !dynabomb_xl)
4760 void Bang(int x, int y)
4762 int element = MovingOrBlocked2Element(x, y);
4763 int explosion_type = EX_TYPE_NORMAL;
4765 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4767 struct PlayerInfo *player = PLAYERINFO(x, y);
4769 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4770 player->element_nr);
4772 if (level.use_explosion_element[player->index_nr])
4774 int explosion_element = level.explosion_element[player->index_nr];
4776 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4777 explosion_type = EX_TYPE_CROSS;
4778 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4779 explosion_type = EX_TYPE_CENTER;
4787 case EL_BD_BUTTERFLY:
4790 case EL_DARK_YAMYAM:
4794 RaiseScoreElement(element);
4797 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4798 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4799 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4800 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4801 case EL_DYNABOMB_INCREASE_NUMBER:
4802 case EL_DYNABOMB_INCREASE_SIZE:
4803 case EL_DYNABOMB_INCREASE_POWER:
4804 explosion_type = EX_TYPE_DYNA;
4807 case EL_DC_LANDMINE:
4809 case EL_EM_EXIT_OPEN:
4810 case EL_EM_STEEL_EXIT_OPEN:
4812 explosion_type = EX_TYPE_CENTER;
4817 case EL_LAMP_ACTIVE:
4818 case EL_AMOEBA_TO_DIAMOND:
4819 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4820 explosion_type = EX_TYPE_CENTER;
4824 if (element_info[element].explosion_type == EXPLODES_CROSS)
4825 explosion_type = EX_TYPE_CROSS;
4826 else if (element_info[element].explosion_type == EXPLODES_1X1)
4827 explosion_type = EX_TYPE_CENTER;
4831 if (explosion_type == EX_TYPE_DYNA)
4834 Explode(x, y, EX_PHASE_START, explosion_type);
4836 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4839 void SplashAcid(int x, int y)
4841 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4842 (!IN_LEV_FIELD(x - 1, y - 2) ||
4843 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4844 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4846 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4847 (!IN_LEV_FIELD(x + 1, y - 2) ||
4848 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4849 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4851 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4854 static void InitBeltMovement()
4856 static int belt_base_element[4] =
4858 EL_CONVEYOR_BELT_1_LEFT,
4859 EL_CONVEYOR_BELT_2_LEFT,
4860 EL_CONVEYOR_BELT_3_LEFT,
4861 EL_CONVEYOR_BELT_4_LEFT
4863 static int belt_base_active_element[4] =
4865 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4866 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4867 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4868 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4873 /* set frame order for belt animation graphic according to belt direction */
4874 for (i = 0; i < NUM_BELTS; i++)
4878 for (j = 0; j < NUM_BELT_PARTS; j++)
4880 int element = belt_base_active_element[belt_nr] + j;
4881 int graphic = el2img(element);
4883 if (game.belt_dir[i] == MV_LEFT)
4884 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4886 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4890 SCAN_PLAYFIELD(x, y)
4892 int element = Feld[x][y];
4894 for (i = 0; i < NUM_BELTS; i++)
4896 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4898 int e_belt_nr = getBeltNrFromBeltElement(element);
4901 if (e_belt_nr == belt_nr)
4903 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4905 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4912 static void ToggleBeltSwitch(int x, int y)
4914 static int belt_base_element[4] =
4916 EL_CONVEYOR_BELT_1_LEFT,
4917 EL_CONVEYOR_BELT_2_LEFT,
4918 EL_CONVEYOR_BELT_3_LEFT,
4919 EL_CONVEYOR_BELT_4_LEFT
4921 static int belt_base_active_element[4] =
4923 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4924 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4925 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4926 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4928 static int belt_base_switch_element[4] =
4930 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4931 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4932 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4933 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4935 static int belt_move_dir[4] =
4943 int element = Feld[x][y];
4944 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4945 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4946 int belt_dir = belt_move_dir[belt_dir_nr];
4949 if (!IS_BELT_SWITCH(element))
4952 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4953 game.belt_dir[belt_nr] = belt_dir;
4955 if (belt_dir_nr == 3)
4958 /* set frame order for belt animation graphic according to belt direction */
4959 for (i = 0; i < NUM_BELT_PARTS; i++)
4961 int element = belt_base_active_element[belt_nr] + i;
4962 int graphic = el2img(element);
4964 if (belt_dir == MV_LEFT)
4965 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4967 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4970 SCAN_PLAYFIELD(xx, yy)
4972 int element = Feld[xx][yy];
4974 if (IS_BELT_SWITCH(element))
4976 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4978 if (e_belt_nr == belt_nr)
4980 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4981 DrawLevelField(xx, yy);
4984 else if (IS_BELT(element) && belt_dir != MV_NONE)
4986 int e_belt_nr = getBeltNrFromBeltElement(element);
4988 if (e_belt_nr == belt_nr)
4990 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4992 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4993 DrawLevelField(xx, yy);
4996 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4998 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5000 if (e_belt_nr == belt_nr)
5002 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5004 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5005 DrawLevelField(xx, yy);
5011 static void ToggleSwitchgateSwitch(int x, int y)
5015 game.switchgate_pos = !game.switchgate_pos;
5017 SCAN_PLAYFIELD(xx, yy)
5019 int element = Feld[xx][yy];
5021 #if !USE_BOTH_SWITCHGATE_SWITCHES
5022 if (element == EL_SWITCHGATE_SWITCH_UP ||
5023 element == EL_SWITCHGATE_SWITCH_DOWN)
5025 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5026 DrawLevelField(xx, yy);
5028 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5029 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5031 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5032 DrawLevelField(xx, yy);
5035 if (element == EL_SWITCHGATE_SWITCH_UP)
5037 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5038 DrawLevelField(xx, yy);
5040 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5042 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5043 DrawLevelField(xx, yy);
5045 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5047 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5048 DrawLevelField(xx, yy);
5050 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5052 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5053 DrawLevelField(xx, yy);
5056 else if (element == EL_SWITCHGATE_OPEN ||
5057 element == EL_SWITCHGATE_OPENING)
5059 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5061 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5063 else if (element == EL_SWITCHGATE_CLOSED ||
5064 element == EL_SWITCHGATE_CLOSING)
5066 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5068 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5073 static int getInvisibleActiveFromInvisibleElement(int element)
5075 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5076 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5077 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5081 static int getInvisibleFromInvisibleActiveElement(int element)
5083 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5084 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5085 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5089 static void RedrawAllLightSwitchesAndInvisibleElements()
5093 SCAN_PLAYFIELD(x, y)
5095 int element = Feld[x][y];
5097 if (element == EL_LIGHT_SWITCH &&
5098 game.light_time_left > 0)
5100 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5101 DrawLevelField(x, y);
5103 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5104 game.light_time_left == 0)
5106 Feld[x][y] = EL_LIGHT_SWITCH;
5107 DrawLevelField(x, y);
5109 else if (element == EL_EMC_DRIPPER &&
5110 game.light_time_left > 0)
5112 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5113 DrawLevelField(x, y);
5115 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5116 game.light_time_left == 0)
5118 Feld[x][y] = EL_EMC_DRIPPER;
5119 DrawLevelField(x, y);
5121 else if (element == EL_INVISIBLE_STEELWALL ||
5122 element == EL_INVISIBLE_WALL ||
5123 element == EL_INVISIBLE_SAND)
5125 if (game.light_time_left > 0)
5126 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5128 DrawLevelField(x, y);
5130 /* uncrumble neighbour fields, if needed */
5131 if (element == EL_INVISIBLE_SAND)
5132 DrawLevelFieldCrumbledSandNeighbours(x, y);
5134 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5135 element == EL_INVISIBLE_WALL_ACTIVE ||
5136 element == EL_INVISIBLE_SAND_ACTIVE)
5138 if (game.light_time_left == 0)
5139 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5141 DrawLevelField(x, y);
5143 /* re-crumble neighbour fields, if needed */
5144 if (element == EL_INVISIBLE_SAND)
5145 DrawLevelFieldCrumbledSandNeighbours(x, y);
5150 static void RedrawAllInvisibleElementsForLenses()
5154 SCAN_PLAYFIELD(x, y)
5156 int element = Feld[x][y];
5158 if (element == EL_EMC_DRIPPER &&
5159 game.lenses_time_left > 0)
5161 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5162 DrawLevelField(x, y);
5164 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5165 game.lenses_time_left == 0)
5167 Feld[x][y] = EL_EMC_DRIPPER;
5168 DrawLevelField(x, y);
5170 else if (element == EL_INVISIBLE_STEELWALL ||
5171 element == EL_INVISIBLE_WALL ||
5172 element == EL_INVISIBLE_SAND)
5174 if (game.lenses_time_left > 0)
5175 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5177 DrawLevelField(x, y);
5179 /* uncrumble neighbour fields, if needed */
5180 if (element == EL_INVISIBLE_SAND)
5181 DrawLevelFieldCrumbledSandNeighbours(x, y);
5183 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5184 element == EL_INVISIBLE_WALL_ACTIVE ||
5185 element == EL_INVISIBLE_SAND_ACTIVE)
5187 if (game.lenses_time_left == 0)
5188 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5190 DrawLevelField(x, y);
5192 /* re-crumble neighbour fields, if needed */
5193 if (element == EL_INVISIBLE_SAND)
5194 DrawLevelFieldCrumbledSandNeighbours(x, y);
5199 static void RedrawAllInvisibleElementsForMagnifier()
5203 SCAN_PLAYFIELD(x, y)
5205 int element = Feld[x][y];
5207 if (element == EL_EMC_FAKE_GRASS &&
5208 game.magnify_time_left > 0)
5210 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5211 DrawLevelField(x, y);
5213 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5214 game.magnify_time_left == 0)
5216 Feld[x][y] = EL_EMC_FAKE_GRASS;
5217 DrawLevelField(x, y);
5219 else if (IS_GATE_GRAY(element) &&
5220 game.magnify_time_left > 0)
5222 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5223 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5224 IS_EM_GATE_GRAY(element) ?
5225 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5226 IS_EMC_GATE_GRAY(element) ?
5227 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5229 DrawLevelField(x, y);
5231 else if (IS_GATE_GRAY_ACTIVE(element) &&
5232 game.magnify_time_left == 0)
5234 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5235 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5236 IS_EM_GATE_GRAY_ACTIVE(element) ?
5237 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5238 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5239 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5241 DrawLevelField(x, y);
5246 static void ToggleLightSwitch(int x, int y)
5248 int element = Feld[x][y];
5250 game.light_time_left =
5251 (element == EL_LIGHT_SWITCH ?
5252 level.time_light * FRAMES_PER_SECOND : 0);
5254 RedrawAllLightSwitchesAndInvisibleElements();
5257 static void ActivateTimegateSwitch(int x, int y)
5261 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5263 SCAN_PLAYFIELD(xx, yy)
5265 int element = Feld[xx][yy];
5267 if (element == EL_TIMEGATE_CLOSED ||
5268 element == EL_TIMEGATE_CLOSING)
5270 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5271 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5275 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5277 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5278 DrawLevelField(xx, yy);
5285 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5286 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5288 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5292 void Impact(int x, int y)
5294 boolean last_line = (y == lev_fieldy - 1);
5295 boolean object_hit = FALSE;
5296 boolean impact = (last_line || object_hit);
5297 int element = Feld[x][y];
5298 int smashed = EL_STEELWALL;
5300 if (!last_line) /* check if element below was hit */
5302 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5305 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5306 MovDir[x][y + 1] != MV_DOWN ||
5307 MovPos[x][y + 1] <= TILEY / 2));
5309 /* do not smash moving elements that left the smashed field in time */
5310 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5311 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5314 #if USE_QUICKSAND_IMPACT_BUGFIX
5315 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5317 RemoveMovingField(x, y + 1);
5318 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5319 Feld[x][y + 2] = EL_ROCK;
5320 DrawLevelField(x, y + 2);
5325 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5327 RemoveMovingField(x, y + 1);
5328 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5329 Feld[x][y + 2] = EL_ROCK;
5330 DrawLevelField(x, y + 2);
5337 smashed = MovingOrBlocked2Element(x, y + 1);
5339 impact = (last_line || object_hit);
5342 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5344 SplashAcid(x, y + 1);
5348 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5349 /* only reset graphic animation if graphic really changes after impact */
5351 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5353 ResetGfxAnimation(x, y);
5354 DrawLevelField(x, y);
5357 if (impact && CAN_EXPLODE_IMPACT(element))
5362 else if (impact && element == EL_PEARL &&
5363 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5365 ResetGfxAnimation(x, y);
5367 Feld[x][y] = EL_PEARL_BREAKING;
5368 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5371 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5373 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5378 if (impact && element == EL_AMOEBA_DROP)
5380 if (object_hit && IS_PLAYER(x, y + 1))
5381 KillPlayerUnlessEnemyProtected(x, y + 1);
5382 else if (object_hit && smashed == EL_PENGUIN)
5386 Feld[x][y] = EL_AMOEBA_GROWING;
5387 Store[x][y] = EL_AMOEBA_WET;
5389 ResetRandomAnimationValue(x, y);
5394 if (object_hit) /* check which object was hit */
5396 if ((CAN_PASS_MAGIC_WALL(element) &&
5397 (smashed == EL_MAGIC_WALL ||
5398 smashed == EL_BD_MAGIC_WALL)) ||
5399 (CAN_PASS_DC_MAGIC_WALL(element) &&
5400 smashed == EL_DC_MAGIC_WALL))
5403 int activated_magic_wall =
5404 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5405 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5406 EL_DC_MAGIC_WALL_ACTIVE);
5408 /* activate magic wall / mill */
5409 SCAN_PLAYFIELD(xx, yy)
5411 if (Feld[xx][yy] == smashed)
5412 Feld[xx][yy] = activated_magic_wall;
5415 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5416 game.magic_wall_active = TRUE;
5418 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5419 SND_MAGIC_WALL_ACTIVATING :
5420 smashed == EL_BD_MAGIC_WALL ?
5421 SND_BD_MAGIC_WALL_ACTIVATING :
5422 SND_DC_MAGIC_WALL_ACTIVATING));
5425 if (IS_PLAYER(x, y + 1))
5427 if (CAN_SMASH_PLAYER(element))
5429 KillPlayerUnlessEnemyProtected(x, y + 1);
5433 else if (smashed == EL_PENGUIN)
5435 if (CAN_SMASH_PLAYER(element))
5441 else if (element == EL_BD_DIAMOND)
5443 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5449 else if (((element == EL_SP_INFOTRON ||
5450 element == EL_SP_ZONK) &&
5451 (smashed == EL_SP_SNIKSNAK ||
5452 smashed == EL_SP_ELECTRON ||
5453 smashed == EL_SP_DISK_ORANGE)) ||
5454 (element == EL_SP_INFOTRON &&
5455 smashed == EL_SP_DISK_YELLOW))
5460 else if (CAN_SMASH_EVERYTHING(element))
5462 if (IS_CLASSIC_ENEMY(smashed) ||
5463 CAN_EXPLODE_SMASHED(smashed))
5468 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5470 if (smashed == EL_LAMP ||
5471 smashed == EL_LAMP_ACTIVE)
5476 else if (smashed == EL_NUT)
5478 Feld[x][y + 1] = EL_NUT_BREAKING;
5479 PlayLevelSound(x, y, SND_NUT_BREAKING);
5480 RaiseScoreElement(EL_NUT);
5483 else if (smashed == EL_PEARL)
5485 ResetGfxAnimation(x, y);
5487 Feld[x][y + 1] = EL_PEARL_BREAKING;
5488 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5491 else if (smashed == EL_DIAMOND)
5493 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5494 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5497 else if (IS_BELT_SWITCH(smashed))
5499 ToggleBeltSwitch(x, y + 1);
5501 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5502 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5503 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5504 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5506 ToggleSwitchgateSwitch(x, y + 1);
5508 else if (smashed == EL_LIGHT_SWITCH ||
5509 smashed == EL_LIGHT_SWITCH_ACTIVE)
5511 ToggleLightSwitch(x, y + 1);
5516 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5519 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5521 CheckElementChangeBySide(x, y + 1, smashed, element,
5522 CE_SWITCHED, CH_SIDE_TOP);
5523 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5529 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5534 /* play sound of magic wall / mill */
5536 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5537 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5538 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5540 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5541 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5542 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5543 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5544 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5545 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5550 /* play sound of object that hits the ground */
5551 if (last_line || object_hit)
5552 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5555 inline static void TurnRoundExt(int x, int y)
5567 { 0, 0 }, { 0, 0 }, { 0, 0 },
5572 int left, right, back;
5576 { MV_DOWN, MV_UP, MV_RIGHT },
5577 { MV_UP, MV_DOWN, MV_LEFT },
5579 { MV_LEFT, MV_RIGHT, MV_DOWN },
5583 { MV_RIGHT, MV_LEFT, MV_UP }
5586 int element = Feld[x][y];
5587 int move_pattern = element_info[element].move_pattern;
5589 int old_move_dir = MovDir[x][y];
5590 int left_dir = turn[old_move_dir].left;
5591 int right_dir = turn[old_move_dir].right;
5592 int back_dir = turn[old_move_dir].back;
5594 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5595 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5596 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5597 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5599 int left_x = x + left_dx, left_y = y + left_dy;
5600 int right_x = x + right_dx, right_y = y + right_dy;
5601 int move_x = x + move_dx, move_y = y + move_dy;
5605 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5607 TestIfBadThingTouchesOtherBadThing(x, y);
5609 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5610 MovDir[x][y] = right_dir;
5611 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5612 MovDir[x][y] = left_dir;
5614 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5616 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5619 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5621 TestIfBadThingTouchesOtherBadThing(x, y);
5623 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5624 MovDir[x][y] = left_dir;
5625 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5626 MovDir[x][y] = right_dir;
5628 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5630 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5633 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5635 TestIfBadThingTouchesOtherBadThing(x, y);
5637 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5638 MovDir[x][y] = left_dir;
5639 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5640 MovDir[x][y] = right_dir;
5642 if (MovDir[x][y] != old_move_dir)
5645 else if (element == EL_YAMYAM)
5647 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5648 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5650 if (can_turn_left && can_turn_right)
5651 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5652 else if (can_turn_left)
5653 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5654 else if (can_turn_right)
5655 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5657 MovDir[x][y] = back_dir;
5659 MovDelay[x][y] = 16 + 16 * RND(3);
5661 else if (element == EL_DARK_YAMYAM)
5663 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5665 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5668 if (can_turn_left && can_turn_right)
5669 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5670 else if (can_turn_left)
5671 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5672 else if (can_turn_right)
5673 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5675 MovDir[x][y] = back_dir;
5677 MovDelay[x][y] = 16 + 16 * RND(3);
5679 else if (element == EL_PACMAN)
5681 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5682 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5684 if (can_turn_left && can_turn_right)
5685 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5686 else if (can_turn_left)
5687 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5688 else if (can_turn_right)
5689 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5691 MovDir[x][y] = back_dir;
5693 MovDelay[x][y] = 6 + RND(40);
5695 else if (element == EL_PIG)
5697 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5698 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5699 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5700 boolean should_turn_left, should_turn_right, should_move_on;
5702 int rnd = RND(rnd_value);
5704 should_turn_left = (can_turn_left &&
5706 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5707 y + back_dy + left_dy)));
5708 should_turn_right = (can_turn_right &&
5710 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5711 y + back_dy + right_dy)));
5712 should_move_on = (can_move_on &&
5715 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5716 y + move_dy + left_dy) ||
5717 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5718 y + move_dy + right_dy)));
5720 if (should_turn_left || should_turn_right || should_move_on)
5722 if (should_turn_left && should_turn_right && should_move_on)
5723 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5724 rnd < 2 * rnd_value / 3 ? right_dir :
5726 else if (should_turn_left && should_turn_right)
5727 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5728 else if (should_turn_left && should_move_on)
5729 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5730 else if (should_turn_right && should_move_on)
5731 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5732 else if (should_turn_left)
5733 MovDir[x][y] = left_dir;
5734 else if (should_turn_right)
5735 MovDir[x][y] = right_dir;
5736 else if (should_move_on)
5737 MovDir[x][y] = old_move_dir;
5739 else if (can_move_on && rnd > rnd_value / 8)
5740 MovDir[x][y] = old_move_dir;
5741 else if (can_turn_left && can_turn_right)
5742 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5743 else if (can_turn_left && rnd > rnd_value / 8)
5744 MovDir[x][y] = left_dir;
5745 else if (can_turn_right && rnd > rnd_value/8)
5746 MovDir[x][y] = right_dir;
5748 MovDir[x][y] = back_dir;
5750 xx = x + move_xy[MovDir[x][y]].dx;
5751 yy = y + move_xy[MovDir[x][y]].dy;
5753 if (!IN_LEV_FIELD(xx, yy) ||
5754 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5755 MovDir[x][y] = old_move_dir;
5759 else if (element == EL_DRAGON)
5761 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5762 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5763 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5765 int rnd = RND(rnd_value);
5767 if (can_move_on && rnd > rnd_value / 8)
5768 MovDir[x][y] = old_move_dir;
5769 else if (can_turn_left && can_turn_right)
5770 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5771 else if (can_turn_left && rnd > rnd_value / 8)
5772 MovDir[x][y] = left_dir;
5773 else if (can_turn_right && rnd > rnd_value / 8)
5774 MovDir[x][y] = right_dir;
5776 MovDir[x][y] = back_dir;
5778 xx = x + move_xy[MovDir[x][y]].dx;
5779 yy = y + move_xy[MovDir[x][y]].dy;
5781 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5782 MovDir[x][y] = old_move_dir;
5786 else if (element == EL_MOLE)
5788 boolean can_move_on =
5789 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5790 IS_AMOEBOID(Feld[move_x][move_y]) ||
5791 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5794 boolean can_turn_left =
5795 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5796 IS_AMOEBOID(Feld[left_x][left_y])));
5798 boolean can_turn_right =
5799 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5800 IS_AMOEBOID(Feld[right_x][right_y])));
5802 if (can_turn_left && can_turn_right)
5803 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5804 else if (can_turn_left)
5805 MovDir[x][y] = left_dir;
5807 MovDir[x][y] = right_dir;
5810 if (MovDir[x][y] != old_move_dir)
5813 else if (element == EL_BALLOON)
5815 MovDir[x][y] = game.wind_direction;
5818 else if (element == EL_SPRING)
5820 #if USE_NEW_SPRING_BUMPER
5821 if (MovDir[x][y] & MV_HORIZONTAL)
5823 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5824 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5826 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5827 ResetGfxAnimation(move_x, move_y);
5828 DrawLevelField(move_x, move_y);
5830 MovDir[x][y] = back_dir;
5832 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5833 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5834 MovDir[x][y] = MV_NONE;
5837 if (MovDir[x][y] & MV_HORIZONTAL &&
5838 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5839 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5840 MovDir[x][y] = MV_NONE;
5845 else if (element == EL_ROBOT ||
5846 element == EL_SATELLITE ||
5847 element == EL_PENGUIN ||
5848 element == EL_EMC_ANDROID)
5850 int attr_x = -1, attr_y = -1;
5861 for (i = 0; i < MAX_PLAYERS; i++)
5863 struct PlayerInfo *player = &stored_player[i];
5864 int jx = player->jx, jy = player->jy;
5866 if (!player->active)
5870 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5878 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5879 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5880 game.engine_version < VERSION_IDENT(3,1,0,0)))
5886 if (element == EL_PENGUIN)
5889 static int xy[4][2] =
5897 for (i = 0; i < NUM_DIRECTIONS; i++)
5899 int ex = x + xy[i][0];
5900 int ey = y + xy[i][1];
5902 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5903 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5904 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5905 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5914 MovDir[x][y] = MV_NONE;
5916 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5917 else if (attr_x > x)
5918 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5920 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5921 else if (attr_y > y)
5922 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5924 if (element == EL_ROBOT)
5928 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5929 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5930 Moving2Blocked(x, y, &newx, &newy);
5932 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5933 MovDelay[x][y] = 8 + 8 * !RND(3);
5935 MovDelay[x][y] = 16;
5937 else if (element == EL_PENGUIN)
5943 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5945 boolean first_horiz = RND(2);
5946 int new_move_dir = MovDir[x][y];
5949 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5950 Moving2Blocked(x, y, &newx, &newy);
5952 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5956 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5957 Moving2Blocked(x, y, &newx, &newy);
5959 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5962 MovDir[x][y] = old_move_dir;
5966 else if (element == EL_SATELLITE)
5972 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5974 boolean first_horiz = RND(2);
5975 int new_move_dir = MovDir[x][y];
5978 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5979 Moving2Blocked(x, y, &newx, &newy);
5981 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5985 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5986 Moving2Blocked(x, y, &newx, &newy);
5988 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5991 MovDir[x][y] = old_move_dir;
5995 else if (element == EL_EMC_ANDROID)
5997 static int check_pos[16] =
5999 -1, /* 0 => (invalid) */
6000 7, /* 1 => MV_LEFT */
6001 3, /* 2 => MV_RIGHT */
6002 -1, /* 3 => (invalid) */
6004 0, /* 5 => MV_LEFT | MV_UP */
6005 2, /* 6 => MV_RIGHT | MV_UP */
6006 -1, /* 7 => (invalid) */
6007 5, /* 8 => MV_DOWN */
6008 6, /* 9 => MV_LEFT | MV_DOWN */
6009 4, /* 10 => MV_RIGHT | MV_DOWN */
6010 -1, /* 11 => (invalid) */
6011 -1, /* 12 => (invalid) */
6012 -1, /* 13 => (invalid) */
6013 -1, /* 14 => (invalid) */
6014 -1, /* 15 => (invalid) */
6022 { -1, -1, MV_LEFT | MV_UP },
6024 { +1, -1, MV_RIGHT | MV_UP },
6025 { +1, 0, MV_RIGHT },
6026 { +1, +1, MV_RIGHT | MV_DOWN },
6028 { -1, +1, MV_LEFT | MV_DOWN },
6031 int start_pos, check_order;
6032 boolean can_clone = FALSE;
6035 /* check if there is any free field around current position */
6036 for (i = 0; i < 8; i++)
6038 int newx = x + check_xy[i].dx;
6039 int newy = y + check_xy[i].dy;
6041 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6049 if (can_clone) /* randomly find an element to clone */
6053 start_pos = check_pos[RND(8)];
6054 check_order = (RND(2) ? -1 : +1);
6056 for (i = 0; i < 8; i++)
6058 int pos_raw = start_pos + i * check_order;
6059 int pos = (pos_raw + 8) % 8;
6060 int newx = x + check_xy[pos].dx;
6061 int newy = y + check_xy[pos].dy;
6063 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6065 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6066 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6068 Store[x][y] = Feld[newx][newy];
6077 if (can_clone) /* randomly find a direction to move */
6081 start_pos = check_pos[RND(8)];
6082 check_order = (RND(2) ? -1 : +1);
6084 for (i = 0; i < 8; i++)
6086 int pos_raw = start_pos + i * check_order;
6087 int pos = (pos_raw + 8) % 8;
6088 int newx = x + check_xy[pos].dx;
6089 int newy = y + check_xy[pos].dy;
6090 int new_move_dir = check_xy[pos].dir;
6092 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6094 MovDir[x][y] = new_move_dir;
6095 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6104 if (can_clone) /* cloning and moving successful */
6107 /* cannot clone -- try to move towards player */
6109 start_pos = check_pos[MovDir[x][y] & 0x0f];
6110 check_order = (RND(2) ? -1 : +1);
6112 for (i = 0; i < 3; i++)
6114 /* first check start_pos, then previous/next or (next/previous) pos */
6115 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6116 int pos = (pos_raw + 8) % 8;
6117 int newx = x + check_xy[pos].dx;
6118 int newy = y + check_xy[pos].dy;
6119 int new_move_dir = check_xy[pos].dir;
6121 if (IS_PLAYER(newx, newy))
6124 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6126 MovDir[x][y] = new_move_dir;
6127 MovDelay[x][y] = level.android_move_time * 8 + 1;
6134 else if (move_pattern == MV_TURNING_LEFT ||
6135 move_pattern == MV_TURNING_RIGHT ||
6136 move_pattern == MV_TURNING_LEFT_RIGHT ||
6137 move_pattern == MV_TURNING_RIGHT_LEFT ||
6138 move_pattern == MV_TURNING_RANDOM ||
6139 move_pattern == MV_ALL_DIRECTIONS)
6141 boolean can_turn_left =
6142 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6143 boolean can_turn_right =
6144 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6146 if (element_info[element].move_stepsize == 0) /* "not moving" */
6149 if (move_pattern == MV_TURNING_LEFT)
6150 MovDir[x][y] = left_dir;
6151 else if (move_pattern == MV_TURNING_RIGHT)
6152 MovDir[x][y] = right_dir;
6153 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6154 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6155 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6156 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6157 else if (move_pattern == MV_TURNING_RANDOM)
6158 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6159 can_turn_right && !can_turn_left ? right_dir :
6160 RND(2) ? left_dir : right_dir);
6161 else if (can_turn_left && can_turn_right)
6162 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6163 else if (can_turn_left)
6164 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6165 else if (can_turn_right)
6166 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6168 MovDir[x][y] = back_dir;
6170 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6172 else if (move_pattern == MV_HORIZONTAL ||
6173 move_pattern == MV_VERTICAL)
6175 if (move_pattern & old_move_dir)
6176 MovDir[x][y] = back_dir;
6177 else if (move_pattern == MV_HORIZONTAL)
6178 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6179 else if (move_pattern == MV_VERTICAL)
6180 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6182 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6184 else if (move_pattern & MV_ANY_DIRECTION)
6186 MovDir[x][y] = move_pattern;
6187 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6189 else if (move_pattern & MV_WIND_DIRECTION)
6191 MovDir[x][y] = game.wind_direction;
6192 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6194 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6196 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6197 MovDir[x][y] = left_dir;
6198 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6199 MovDir[x][y] = right_dir;
6201 if (MovDir[x][y] != old_move_dir)
6202 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6204 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6206 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6207 MovDir[x][y] = right_dir;
6208 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6209 MovDir[x][y] = left_dir;
6211 if (MovDir[x][y] != old_move_dir)
6212 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6214 else if (move_pattern == MV_TOWARDS_PLAYER ||
6215 move_pattern == MV_AWAY_FROM_PLAYER)
6217 int attr_x = -1, attr_y = -1;
6219 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6230 for (i = 0; i < MAX_PLAYERS; i++)
6232 struct PlayerInfo *player = &stored_player[i];
6233 int jx = player->jx, jy = player->jy;
6235 if (!player->active)
6239 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6247 MovDir[x][y] = MV_NONE;
6249 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6250 else if (attr_x > x)
6251 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6253 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6254 else if (attr_y > y)
6255 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6257 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6259 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6261 boolean first_horiz = RND(2);
6262 int new_move_dir = MovDir[x][y];
6264 if (element_info[element].move_stepsize == 0) /* "not moving" */
6266 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6267 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6273 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6274 Moving2Blocked(x, y, &newx, &newy);
6276 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6280 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6281 Moving2Blocked(x, y, &newx, &newy);
6283 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6286 MovDir[x][y] = old_move_dir;
6289 else if (move_pattern == MV_WHEN_PUSHED ||
6290 move_pattern == MV_WHEN_DROPPED)
6292 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6293 MovDir[x][y] = MV_NONE;
6297 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6299 static int test_xy[7][2] =
6309 static int test_dir[7] =
6319 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6320 int move_preference = -1000000; /* start with very low preference */
6321 int new_move_dir = MV_NONE;
6322 int start_test = RND(4);
6325 for (i = 0; i < NUM_DIRECTIONS; i++)
6327 int move_dir = test_dir[start_test + i];
6328 int move_dir_preference;
6330 xx = x + test_xy[start_test + i][0];
6331 yy = y + test_xy[start_test + i][1];
6333 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6334 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6336 new_move_dir = move_dir;
6341 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6344 move_dir_preference = -1 * RunnerVisit[xx][yy];
6345 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6346 move_dir_preference = PlayerVisit[xx][yy];
6348 if (move_dir_preference > move_preference)
6350 /* prefer field that has not been visited for the longest time */
6351 move_preference = move_dir_preference;
6352 new_move_dir = move_dir;
6354 else if (move_dir_preference == move_preference &&
6355 move_dir == old_move_dir)
6357 /* prefer last direction when all directions are preferred equally */
6358 move_preference = move_dir_preference;
6359 new_move_dir = move_dir;
6363 MovDir[x][y] = new_move_dir;
6364 if (old_move_dir != new_move_dir)
6365 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6369 static void TurnRound(int x, int y)
6371 int direction = MovDir[x][y];
6375 GfxDir[x][y] = MovDir[x][y];
6377 if (direction != MovDir[x][y])
6381 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6383 ResetGfxFrame(x, y, FALSE);
6386 static boolean JustBeingPushed(int x, int y)
6390 for (i = 0; i < MAX_PLAYERS; i++)
6392 struct PlayerInfo *player = &stored_player[i];
6394 if (player->active && player->is_pushing && player->MovPos)
6396 int next_jx = player->jx + (player->jx - player->last_jx);
6397 int next_jy = player->jy + (player->jy - player->last_jy);
6399 if (x == next_jx && y == next_jy)
6407 void StartMoving(int x, int y)
6409 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6410 int element = Feld[x][y];
6415 if (MovDelay[x][y] == 0)
6416 GfxAction[x][y] = ACTION_DEFAULT;
6418 if (CAN_FALL(element) && y < lev_fieldy - 1)
6420 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6421 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6422 if (JustBeingPushed(x, y))
6425 if (element == EL_QUICKSAND_FULL)
6427 if (IS_FREE(x, y + 1))
6429 InitMovingField(x, y, MV_DOWN);
6430 started_moving = TRUE;
6432 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6433 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6434 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6435 Store[x][y] = EL_ROCK;
6437 Store[x][y] = EL_ROCK;
6440 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6442 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6444 if (!MovDelay[x][y])
6445 MovDelay[x][y] = TILEY + 1;
6454 Feld[x][y] = EL_QUICKSAND_EMPTY;
6455 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6456 Store[x][y + 1] = Store[x][y];
6459 PlayLevelSoundAction(x, y, ACTION_FILLING);
6462 else if (element == EL_QUICKSAND_FAST_FULL)
6464 if (IS_FREE(x, y + 1))
6466 InitMovingField(x, y, MV_DOWN);
6467 started_moving = TRUE;
6469 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6470 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6471 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6472 Store[x][y] = EL_ROCK;
6474 Store[x][y] = EL_ROCK;
6477 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6479 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6481 if (!MovDelay[x][y])
6482 MovDelay[x][y] = TILEY + 1;
6491 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6492 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6493 Store[x][y + 1] = Store[x][y];
6496 PlayLevelSoundAction(x, y, ACTION_FILLING);
6499 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6500 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6502 InitMovingField(x, y, MV_DOWN);
6503 started_moving = TRUE;
6505 Feld[x][y] = EL_QUICKSAND_FILLING;
6506 Store[x][y] = element;
6508 PlayLevelSoundAction(x, y, ACTION_FILLING);
6510 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6511 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6513 InitMovingField(x, y, MV_DOWN);
6514 started_moving = TRUE;
6516 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6517 Store[x][y] = element;
6519 PlayLevelSoundAction(x, y, ACTION_FILLING);
6521 else if (element == EL_MAGIC_WALL_FULL)
6523 if (IS_FREE(x, y + 1))
6525 InitMovingField(x, y, MV_DOWN);
6526 started_moving = TRUE;
6528 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6529 Store[x][y] = EL_CHANGED(Store[x][y]);
6531 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6533 if (!MovDelay[x][y])
6534 MovDelay[x][y] = TILEY/4 + 1;
6543 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6544 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6545 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6549 else if (element == EL_BD_MAGIC_WALL_FULL)
6551 if (IS_FREE(x, y + 1))
6553 InitMovingField(x, y, MV_DOWN);
6554 started_moving = TRUE;
6556 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6557 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6559 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6561 if (!MovDelay[x][y])
6562 MovDelay[x][y] = TILEY/4 + 1;
6571 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6572 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6573 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6577 else if (element == EL_DC_MAGIC_WALL_FULL)
6579 if (IS_FREE(x, y + 1))
6581 InitMovingField(x, y, MV_DOWN);
6582 started_moving = TRUE;
6584 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6585 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6587 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6589 if (!MovDelay[x][y])
6590 MovDelay[x][y] = TILEY/4 + 1;
6599 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6600 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6601 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6605 else if ((CAN_PASS_MAGIC_WALL(element) &&
6606 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6607 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6608 (CAN_PASS_DC_MAGIC_WALL(element) &&
6609 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6612 InitMovingField(x, y, MV_DOWN);
6613 started_moving = TRUE;
6616 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6617 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6618 EL_DC_MAGIC_WALL_FILLING);
6619 Store[x][y] = element;
6621 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6623 SplashAcid(x, y + 1);
6625 InitMovingField(x, y, MV_DOWN);
6626 started_moving = TRUE;
6628 Store[x][y] = EL_ACID;
6631 #if USE_FIX_IMPACT_COLLISION
6632 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6633 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6635 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6636 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6638 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6639 CAN_FALL(element) && WasJustFalling[x][y] &&
6640 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6642 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6643 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6644 (Feld[x][y + 1] == EL_BLOCKED)))
6646 /* this is needed for a special case not covered by calling "Impact()"
6647 from "ContinueMoving()": if an element moves to a tile directly below
6648 another element which was just falling on that tile (which was empty
6649 in the previous frame), the falling element above would just stop
6650 instead of smashing the element below (in previous version, the above
6651 element was just checked for "moving" instead of "falling", resulting
6652 in incorrect smashes caused by horizontal movement of the above
6653 element; also, the case of the player being the element to smash was
6654 simply not covered here... :-/ ) */
6656 CheckCollision[x][y] = 0;
6657 CheckImpact[x][y] = 0;
6661 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6663 if (MovDir[x][y] == MV_NONE)
6665 InitMovingField(x, y, MV_DOWN);
6666 started_moving = TRUE;
6669 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6671 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6672 MovDir[x][y] = MV_DOWN;
6674 InitMovingField(x, y, MV_DOWN);
6675 started_moving = TRUE;
6677 else if (element == EL_AMOEBA_DROP)
6679 Feld[x][y] = EL_AMOEBA_GROWING;
6680 Store[x][y] = EL_AMOEBA_WET;
6682 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6683 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6684 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6685 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6687 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6688 (IS_FREE(x - 1, y + 1) ||
6689 Feld[x - 1][y + 1] == EL_ACID));
6690 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6691 (IS_FREE(x + 1, y + 1) ||
6692 Feld[x + 1][y + 1] == EL_ACID));
6693 boolean can_fall_any = (can_fall_left || can_fall_right);
6694 boolean can_fall_both = (can_fall_left && can_fall_right);
6695 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6697 #if USE_NEW_ALL_SLIPPERY
6698 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6700 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6701 can_fall_right = FALSE;
6702 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6703 can_fall_left = FALSE;
6704 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6705 can_fall_right = FALSE;
6706 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6707 can_fall_left = FALSE;
6709 can_fall_any = (can_fall_left || can_fall_right);
6710 can_fall_both = FALSE;
6713 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6715 if (slippery_type == SLIPPERY_ONLY_LEFT)
6716 can_fall_right = FALSE;
6717 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6718 can_fall_left = FALSE;
6719 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6720 can_fall_right = FALSE;
6721 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6722 can_fall_left = FALSE;
6724 can_fall_any = (can_fall_left || can_fall_right);
6725 can_fall_both = (can_fall_left && can_fall_right);
6729 #if USE_NEW_ALL_SLIPPERY
6731 #if USE_NEW_SP_SLIPPERY
6732 /* !!! better use the same properties as for custom elements here !!! */
6733 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6734 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6736 can_fall_right = FALSE; /* slip down on left side */
6737 can_fall_both = FALSE;
6742 #if USE_NEW_ALL_SLIPPERY
6745 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6746 can_fall_right = FALSE; /* slip down on left side */
6748 can_fall_left = !(can_fall_right = RND(2));
6750 can_fall_both = FALSE;
6755 if (game.emulation == EMU_BOULDERDASH ||
6756 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6757 can_fall_right = FALSE; /* slip down on left side */
6759 can_fall_left = !(can_fall_right = RND(2));
6761 can_fall_both = FALSE;
6767 /* if not determined otherwise, prefer left side for slipping down */
6768 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6769 started_moving = TRUE;
6773 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6775 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6778 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6779 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6780 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6781 int belt_dir = game.belt_dir[belt_nr];
6783 if ((belt_dir == MV_LEFT && left_is_free) ||
6784 (belt_dir == MV_RIGHT && right_is_free))
6786 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6788 InitMovingField(x, y, belt_dir);
6789 started_moving = TRUE;
6791 Pushed[x][y] = TRUE;
6792 Pushed[nextx][y] = TRUE;
6794 GfxAction[x][y] = ACTION_DEFAULT;
6798 MovDir[x][y] = 0; /* if element was moving, stop it */
6803 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6805 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6807 if (CAN_MOVE(element) && !started_moving)
6810 int move_pattern = element_info[element].move_pattern;
6815 if (MovDir[x][y] == MV_NONE)
6817 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6818 x, y, element, element_info[element].token_name);
6819 printf("StartMoving(): This should never happen!\n");
6824 Moving2Blocked(x, y, &newx, &newy);
6826 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6829 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6830 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6832 WasJustMoving[x][y] = 0;
6833 CheckCollision[x][y] = 0;
6835 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6837 if (Feld[x][y] != element) /* element has changed */
6841 if (!MovDelay[x][y]) /* start new movement phase */
6843 /* all objects that can change their move direction after each step
6844 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6846 if (element != EL_YAMYAM &&
6847 element != EL_DARK_YAMYAM &&
6848 element != EL_PACMAN &&
6849 !(move_pattern & MV_ANY_DIRECTION) &&
6850 move_pattern != MV_TURNING_LEFT &&
6851 move_pattern != MV_TURNING_RIGHT &&
6852 move_pattern != MV_TURNING_LEFT_RIGHT &&
6853 move_pattern != MV_TURNING_RIGHT_LEFT &&
6854 move_pattern != MV_TURNING_RANDOM)
6858 if (MovDelay[x][y] && (element == EL_BUG ||
6859 element == EL_SPACESHIP ||
6860 element == EL_SP_SNIKSNAK ||
6861 element == EL_SP_ELECTRON ||
6862 element == EL_MOLE))
6863 DrawLevelField(x, y);
6867 if (MovDelay[x][y]) /* wait some time before next movement */
6871 if (element == EL_ROBOT ||
6872 element == EL_YAMYAM ||
6873 element == EL_DARK_YAMYAM)
6875 DrawLevelElementAnimationIfNeeded(x, y, element);
6876 PlayLevelSoundAction(x, y, ACTION_WAITING);
6878 else if (element == EL_SP_ELECTRON)
6879 DrawLevelElementAnimationIfNeeded(x, y, element);
6880 else if (element == EL_DRAGON)
6883 int dir = MovDir[x][y];
6884 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6885 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6886 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6887 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6888 dir == MV_UP ? IMG_FLAMES_1_UP :
6889 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6890 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6892 GfxAction[x][y] = ACTION_ATTACKING;
6894 if (IS_PLAYER(x, y))
6895 DrawPlayerField(x, y);
6897 DrawLevelField(x, y);
6899 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6901 for (i = 1; i <= 3; i++)
6903 int xx = x + i * dx;
6904 int yy = y + i * dy;
6905 int sx = SCREENX(xx);
6906 int sy = SCREENY(yy);
6907 int flame_graphic = graphic + (i - 1);
6909 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6914 int flamed = MovingOrBlocked2Element(xx, yy);
6918 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6920 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6921 RemoveMovingField(xx, yy);
6923 RemoveField(xx, yy);
6925 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6928 RemoveMovingField(xx, yy);
6931 ChangeDelay[xx][yy] = 0;
6933 Feld[xx][yy] = EL_FLAMES;
6935 if (IN_SCR_FIELD(sx, sy))
6937 DrawLevelFieldCrumbledSand(xx, yy);
6938 DrawGraphic(sx, sy, flame_graphic, frame);
6943 if (Feld[xx][yy] == EL_FLAMES)
6944 Feld[xx][yy] = EL_EMPTY;
6945 DrawLevelField(xx, yy);
6950 if (MovDelay[x][y]) /* element still has to wait some time */
6952 PlayLevelSoundAction(x, y, ACTION_WAITING);
6958 /* now make next step */
6960 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6962 if (DONT_COLLIDE_WITH(element) &&
6963 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6964 !PLAYER_ENEMY_PROTECTED(newx, newy))
6966 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6971 else if (CAN_MOVE_INTO_ACID(element) &&
6972 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6973 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6974 (MovDir[x][y] == MV_DOWN ||
6975 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6977 SplashAcid(newx, newy);
6978 Store[x][y] = EL_ACID;
6980 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6982 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6983 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6984 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6985 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6988 DrawLevelField(x, y);
6990 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6991 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6992 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6994 local_player->friends_still_needed--;
6995 if (!local_player->friends_still_needed &&
6996 !local_player->GameOver && AllPlayersGone)
6997 PlayerWins(local_player);
7001 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7003 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7004 DrawLevelField(newx, newy);
7006 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7008 else if (!IS_FREE(newx, newy))
7010 GfxAction[x][y] = ACTION_WAITING;
7012 if (IS_PLAYER(x, y))
7013 DrawPlayerField(x, y);
7015 DrawLevelField(x, y);
7020 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7022 if (IS_FOOD_PIG(Feld[newx][newy]))
7024 if (IS_MOVING(newx, newy))
7025 RemoveMovingField(newx, newy);
7028 Feld[newx][newy] = EL_EMPTY;
7029 DrawLevelField(newx, newy);
7032 PlayLevelSound(x, y, SND_PIG_DIGGING);
7034 else if (!IS_FREE(newx, newy))
7036 if (IS_PLAYER(x, y))
7037 DrawPlayerField(x, y);
7039 DrawLevelField(x, y);
7044 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7046 if (Store[x][y] != EL_EMPTY)
7048 boolean can_clone = FALSE;
7051 /* check if element to clone is still there */
7052 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7054 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7062 /* cannot clone or target field not free anymore -- do not clone */
7063 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7064 Store[x][y] = EL_EMPTY;
7067 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7069 if (IS_MV_DIAGONAL(MovDir[x][y]))
7071 int diagonal_move_dir = MovDir[x][y];
7072 int stored = Store[x][y];
7073 int change_delay = 8;
7076 /* android is moving diagonally */
7078 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7080 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7081 GfxElement[x][y] = EL_EMC_ANDROID;
7082 GfxAction[x][y] = ACTION_SHRINKING;
7083 GfxDir[x][y] = diagonal_move_dir;
7084 ChangeDelay[x][y] = change_delay;
7086 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7089 DrawLevelGraphicAnimation(x, y, graphic);
7090 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7092 if (Feld[newx][newy] == EL_ACID)
7094 SplashAcid(newx, newy);
7099 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7101 Store[newx][newy] = EL_EMC_ANDROID;
7102 GfxElement[newx][newy] = EL_EMC_ANDROID;
7103 GfxAction[newx][newy] = ACTION_GROWING;
7104 GfxDir[newx][newy] = diagonal_move_dir;
7105 ChangeDelay[newx][newy] = change_delay;
7107 graphic = el_act_dir2img(GfxElement[newx][newy],
7108 GfxAction[newx][newy], GfxDir[newx][newy]);
7110 DrawLevelGraphicAnimation(newx, newy, graphic);
7111 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7117 Feld[newx][newy] = EL_EMPTY;
7118 DrawLevelField(newx, newy);
7120 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7123 else if (!IS_FREE(newx, newy))
7126 if (IS_PLAYER(x, y))
7127 DrawPlayerField(x, y);
7129 DrawLevelField(x, y);
7135 else if (IS_CUSTOM_ELEMENT(element) &&
7136 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7138 int new_element = Feld[newx][newy];
7140 if (!IS_FREE(newx, newy))
7142 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7143 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7146 /* no element can dig solid indestructible elements */
7147 if (IS_INDESTRUCTIBLE(new_element) &&
7148 !IS_DIGGABLE(new_element) &&
7149 !IS_COLLECTIBLE(new_element))
7152 if (AmoebaNr[newx][newy] &&
7153 (new_element == EL_AMOEBA_FULL ||
7154 new_element == EL_BD_AMOEBA ||
7155 new_element == EL_AMOEBA_GROWING))
7157 AmoebaCnt[AmoebaNr[newx][newy]]--;
7158 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7161 if (IS_MOVING(newx, newy))
7162 RemoveMovingField(newx, newy);
7165 RemoveField(newx, newy);
7166 DrawLevelField(newx, newy);
7169 /* if digged element was about to explode, prevent the explosion */
7170 ExplodeField[newx][newy] = EX_TYPE_NONE;
7172 PlayLevelSoundAction(x, y, action);
7175 Store[newx][newy] = EL_EMPTY;
7177 /* this makes it possible to leave the removed element again */
7178 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7179 Store[newx][newy] = new_element;
7181 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7183 int move_leave_element = element_info[element].move_leave_element;
7185 /* this makes it possible to leave the removed element again */
7186 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7187 new_element : move_leave_element);
7191 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7193 RunnerVisit[x][y] = FrameCounter;
7194 PlayerVisit[x][y] /= 8; /* expire player visit path */
7197 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7199 if (!IS_FREE(newx, newy))
7201 if (IS_PLAYER(x, y))
7202 DrawPlayerField(x, y);
7204 DrawLevelField(x, y);
7210 boolean wanna_flame = !RND(10);
7211 int dx = newx - x, dy = newy - y;
7212 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7213 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7214 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7215 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7216 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7217 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7220 IS_CLASSIC_ENEMY(element1) ||
7221 IS_CLASSIC_ENEMY(element2)) &&
7222 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7223 element1 != EL_FLAMES && element2 != EL_FLAMES)
7225 ResetGfxAnimation(x, y);
7226 GfxAction[x][y] = ACTION_ATTACKING;
7228 if (IS_PLAYER(x, y))
7229 DrawPlayerField(x, y);
7231 DrawLevelField(x, y);
7233 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7235 MovDelay[x][y] = 50;
7239 RemoveField(newx, newy);
7241 Feld[newx][newy] = EL_FLAMES;
7242 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7245 RemoveField(newx1, newy1);
7247 Feld[newx1][newy1] = EL_FLAMES;
7249 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7252 RemoveField(newx2, newy2);
7254 Feld[newx2][newy2] = EL_FLAMES;
7261 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7262 Feld[newx][newy] == EL_DIAMOND)
7264 if (IS_MOVING(newx, newy))
7265 RemoveMovingField(newx, newy);
7268 Feld[newx][newy] = EL_EMPTY;
7269 DrawLevelField(newx, newy);
7272 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7274 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7275 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7277 if (AmoebaNr[newx][newy])
7279 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7280 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7281 Feld[newx][newy] == EL_BD_AMOEBA)
7282 AmoebaCnt[AmoebaNr[newx][newy]]--;
7287 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7289 RemoveMovingField(newx, newy);
7292 if (IS_MOVING(newx, newy))
7294 RemoveMovingField(newx, newy);
7299 Feld[newx][newy] = EL_EMPTY;
7300 DrawLevelField(newx, newy);
7303 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7305 else if ((element == EL_PACMAN || element == EL_MOLE)
7306 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7308 if (AmoebaNr[newx][newy])
7310 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7311 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7312 Feld[newx][newy] == EL_BD_AMOEBA)
7313 AmoebaCnt[AmoebaNr[newx][newy]]--;
7316 if (element == EL_MOLE)
7318 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7319 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7321 ResetGfxAnimation(x, y);
7322 GfxAction[x][y] = ACTION_DIGGING;
7323 DrawLevelField(x, y);
7325 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7327 return; /* wait for shrinking amoeba */
7329 else /* element == EL_PACMAN */
7331 Feld[newx][newy] = EL_EMPTY;
7332 DrawLevelField(newx, newy);
7333 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7336 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7337 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7338 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7340 /* wait for shrinking amoeba to completely disappear */
7343 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7345 /* object was running against a wall */
7350 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7351 if (move_pattern & MV_ANY_DIRECTION &&
7352 move_pattern == MovDir[x][y])
7354 int blocking_element =
7355 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7357 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7360 element = Feld[x][y]; /* element might have changed */
7364 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7365 DrawLevelElementAnimation(x, y, element);
7367 if (DONT_TOUCH(element))
7368 TestIfBadThingTouchesPlayer(x, y);
7373 InitMovingField(x, y, MovDir[x][y]);
7375 PlayLevelSoundAction(x, y, ACTION_MOVING);
7379 ContinueMoving(x, y);
7382 void ContinueMoving(int x, int y)
7384 int element = Feld[x][y];
7385 struct ElementInfo *ei = &element_info[element];
7386 int direction = MovDir[x][y];
7387 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7388 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7389 int newx = x + dx, newy = y + dy;
7390 int stored = Store[x][y];
7391 int stored_new = Store[newx][newy];
7392 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7393 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7394 boolean last_line = (newy == lev_fieldy - 1);
7396 MovPos[x][y] += getElementMoveStepsize(x, y);
7398 if (pushed_by_player) /* special case: moving object pushed by player */
7399 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7401 if (ABS(MovPos[x][y]) < TILEX)
7404 int ee = Feld[x][y];
7405 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7406 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7408 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7409 x, y, ABS(MovPos[x][y]),
7411 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7414 DrawLevelField(x, y);
7416 return; /* element is still moving */
7419 /* element reached destination field */
7421 Feld[x][y] = EL_EMPTY;
7422 Feld[newx][newy] = element;
7423 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7425 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7427 element = Feld[newx][newy] = EL_ACID;
7429 else if (element == EL_MOLE)
7431 Feld[x][y] = EL_SAND;
7433 DrawLevelFieldCrumbledSandNeighbours(x, y);
7435 else if (element == EL_QUICKSAND_FILLING)
7437 element = Feld[newx][newy] = get_next_element(element);
7438 Store[newx][newy] = Store[x][y];
7440 else if (element == EL_QUICKSAND_EMPTYING)
7442 Feld[x][y] = get_next_element(element);
7443 element = Feld[newx][newy] = Store[x][y];
7445 else if (element == EL_QUICKSAND_FAST_FILLING)
7447 element = Feld[newx][newy] = get_next_element(element);
7448 Store[newx][newy] = Store[x][y];
7450 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7452 Feld[x][y] = get_next_element(element);
7453 element = Feld[newx][newy] = Store[x][y];
7455 else if (element == EL_MAGIC_WALL_FILLING)
7457 element = Feld[newx][newy] = get_next_element(element);
7458 if (!game.magic_wall_active)
7459 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7460 Store[newx][newy] = Store[x][y];
7462 else if (element == EL_MAGIC_WALL_EMPTYING)
7464 Feld[x][y] = get_next_element(element);
7465 if (!game.magic_wall_active)
7466 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7467 element = Feld[newx][newy] = Store[x][y];
7469 #if USE_NEW_CUSTOM_VALUE
7470 InitField(newx, newy, FALSE);
7473 else if (element == EL_BD_MAGIC_WALL_FILLING)
7475 element = Feld[newx][newy] = get_next_element(element);
7476 if (!game.magic_wall_active)
7477 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7478 Store[newx][newy] = Store[x][y];
7480 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7482 Feld[x][y] = get_next_element(element);
7483 if (!game.magic_wall_active)
7484 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7485 element = Feld[newx][newy] = Store[x][y];
7487 #if USE_NEW_CUSTOM_VALUE
7488 InitField(newx, newy, FALSE);
7491 else if (element == EL_DC_MAGIC_WALL_FILLING)
7493 element = Feld[newx][newy] = get_next_element(element);
7494 if (!game.magic_wall_active)
7495 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7496 Store[newx][newy] = Store[x][y];
7498 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7500 Feld[x][y] = get_next_element(element);
7501 if (!game.magic_wall_active)
7502 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7503 element = Feld[newx][newy] = Store[x][y];
7505 #if USE_NEW_CUSTOM_VALUE
7506 InitField(newx, newy, FALSE);
7509 else if (element == EL_AMOEBA_DROPPING)
7511 Feld[x][y] = get_next_element(element);
7512 element = Feld[newx][newy] = Store[x][y];
7514 else if (element == EL_SOKOBAN_OBJECT)
7517 Feld[x][y] = Back[x][y];
7519 if (Back[newx][newy])
7520 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7522 Back[x][y] = Back[newx][newy] = 0;
7525 Store[x][y] = EL_EMPTY;
7530 MovDelay[newx][newy] = 0;
7532 if (CAN_CHANGE_OR_HAS_ACTION(element))
7534 /* copy element change control values to new field */
7535 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7536 ChangePage[newx][newy] = ChangePage[x][y];
7537 ChangeCount[newx][newy] = ChangeCount[x][y];
7538 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7541 #if USE_NEW_CUSTOM_VALUE
7542 CustomValue[newx][newy] = CustomValue[x][y];
7545 ChangeDelay[x][y] = 0;
7546 ChangePage[x][y] = -1;
7547 ChangeCount[x][y] = 0;
7548 ChangeEvent[x][y] = -1;
7550 #if USE_NEW_CUSTOM_VALUE
7551 CustomValue[x][y] = 0;
7554 /* copy animation control values to new field */
7555 GfxFrame[newx][newy] = GfxFrame[x][y];
7556 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7557 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7558 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7560 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7562 /* some elements can leave other elements behind after moving */
7564 if (ei->move_leave_element != EL_EMPTY &&
7565 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7566 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7568 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7569 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7570 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7573 int move_leave_element = ei->move_leave_element;
7577 /* this makes it possible to leave the removed element again */
7578 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7579 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7581 /* this makes it possible to leave the removed element again */
7582 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7583 move_leave_element = stored;
7586 /* this makes it possible to leave the removed element again */
7587 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7588 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7589 move_leave_element = stored;
7592 Feld[x][y] = move_leave_element;
7594 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7595 MovDir[x][y] = direction;
7597 InitField(x, y, FALSE);
7599 if (GFX_CRUMBLED(Feld[x][y]))
7600 DrawLevelFieldCrumbledSandNeighbours(x, y);
7602 if (ELEM_IS_PLAYER(move_leave_element))
7603 RelocatePlayer(x, y, move_leave_element);
7606 /* do this after checking for left-behind element */
7607 ResetGfxAnimation(x, y); /* reset animation values for old field */
7609 if (!CAN_MOVE(element) ||
7610 (CAN_FALL(element) && direction == MV_DOWN &&
7611 (element == EL_SPRING ||
7612 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7613 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7614 GfxDir[x][y] = MovDir[newx][newy] = 0;
7616 DrawLevelField(x, y);
7617 DrawLevelField(newx, newy);
7619 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7621 /* prevent pushed element from moving on in pushed direction */
7622 if (pushed_by_player && CAN_MOVE(element) &&
7623 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7624 !(element_info[element].move_pattern & direction))
7625 TurnRound(newx, newy);
7627 /* prevent elements on conveyor belt from moving on in last direction */
7628 if (pushed_by_conveyor && CAN_FALL(element) &&
7629 direction & MV_HORIZONTAL)
7630 MovDir[newx][newy] = 0;
7632 if (!pushed_by_player)
7634 int nextx = newx + dx, nexty = newy + dy;
7635 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7637 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7639 if (CAN_FALL(element) && direction == MV_DOWN)
7640 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7642 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7643 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7645 #if USE_FIX_IMPACT_COLLISION
7646 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7647 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7651 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7653 TestIfBadThingTouchesPlayer(newx, newy);
7654 TestIfBadThingTouchesFriend(newx, newy);
7656 if (!IS_CUSTOM_ELEMENT(element))
7657 TestIfBadThingTouchesOtherBadThing(newx, newy);
7659 else if (element == EL_PENGUIN)
7660 TestIfFriendTouchesBadThing(newx, newy);
7662 /* give the player one last chance (one more frame) to move away */
7663 if (CAN_FALL(element) && direction == MV_DOWN &&
7664 (last_line || (!IS_FREE(x, newy + 1) &&
7665 (!IS_PLAYER(x, newy + 1) ||
7666 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7669 if (pushed_by_player && !game.use_change_when_pushing_bug)
7671 int push_side = MV_DIR_OPPOSITE(direction);
7672 struct PlayerInfo *player = PLAYERINFO(x, y);
7674 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7675 player->index_bit, push_side);
7676 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7677 player->index_bit, push_side);
7680 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7681 MovDelay[newx][newy] = 1;
7683 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7685 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7688 if (ChangePage[newx][newy] != -1) /* delayed change */
7690 int page = ChangePage[newx][newy];
7691 struct ElementChangeInfo *change = &ei->change_page[page];
7693 ChangePage[newx][newy] = -1;
7695 if (change->can_change)
7697 if (ChangeElement(newx, newy, element, page))
7699 if (change->post_change_function)
7700 change->post_change_function(newx, newy);
7704 if (change->has_action)
7705 ExecuteCustomElementAction(newx, newy, element, page);
7709 TestIfElementHitsCustomElement(newx, newy, direction);
7710 TestIfPlayerTouchesCustomElement(newx, newy);
7711 TestIfElementTouchesCustomElement(newx, newy);
7713 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7714 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7715 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7716 MV_DIR_OPPOSITE(direction));
7719 int AmoebeNachbarNr(int ax, int ay)
7722 int element = Feld[ax][ay];
7724 static int xy[4][2] =
7732 for (i = 0; i < NUM_DIRECTIONS; i++)
7734 int x = ax + xy[i][0];
7735 int y = ay + xy[i][1];
7737 if (!IN_LEV_FIELD(x, y))
7740 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7741 group_nr = AmoebaNr[x][y];
7747 void AmoebenVereinigen(int ax, int ay)
7749 int i, x, y, xx, yy;
7750 int new_group_nr = AmoebaNr[ax][ay];
7751 static int xy[4][2] =
7759 if (new_group_nr == 0)
7762 for (i = 0; i < NUM_DIRECTIONS; i++)
7767 if (!IN_LEV_FIELD(x, y))
7770 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7771 Feld[x][y] == EL_BD_AMOEBA ||
7772 Feld[x][y] == EL_AMOEBA_DEAD) &&
7773 AmoebaNr[x][y] != new_group_nr)
7775 int old_group_nr = AmoebaNr[x][y];
7777 if (old_group_nr == 0)
7780 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7781 AmoebaCnt[old_group_nr] = 0;
7782 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7783 AmoebaCnt2[old_group_nr] = 0;
7785 SCAN_PLAYFIELD(xx, yy)
7787 if (AmoebaNr[xx][yy] == old_group_nr)
7788 AmoebaNr[xx][yy] = new_group_nr;
7794 void AmoebeUmwandeln(int ax, int ay)
7798 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7800 int group_nr = AmoebaNr[ax][ay];
7805 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7806 printf("AmoebeUmwandeln(): This should never happen!\n");
7811 SCAN_PLAYFIELD(x, y)
7813 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7816 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7820 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7821 SND_AMOEBA_TURNING_TO_GEM :
7822 SND_AMOEBA_TURNING_TO_ROCK));
7827 static int xy[4][2] =
7835 for (i = 0; i < NUM_DIRECTIONS; i++)
7840 if (!IN_LEV_FIELD(x, y))
7843 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7845 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7846 SND_AMOEBA_TURNING_TO_GEM :
7847 SND_AMOEBA_TURNING_TO_ROCK));
7854 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7857 int group_nr = AmoebaNr[ax][ay];
7858 boolean done = FALSE;
7863 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7864 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7869 SCAN_PLAYFIELD(x, y)
7871 if (AmoebaNr[x][y] == group_nr &&
7872 (Feld[x][y] == EL_AMOEBA_DEAD ||
7873 Feld[x][y] == EL_BD_AMOEBA ||
7874 Feld[x][y] == EL_AMOEBA_GROWING))
7877 Feld[x][y] = new_element;
7878 InitField(x, y, FALSE);
7879 DrawLevelField(x, y);
7885 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7886 SND_BD_AMOEBA_TURNING_TO_ROCK :
7887 SND_BD_AMOEBA_TURNING_TO_GEM));
7890 void AmoebeWaechst(int x, int y)
7892 static unsigned long sound_delay = 0;
7893 static unsigned long sound_delay_value = 0;
7895 if (!MovDelay[x][y]) /* start new growing cycle */
7899 if (DelayReached(&sound_delay, sound_delay_value))
7901 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7902 sound_delay_value = 30;
7906 if (MovDelay[x][y]) /* wait some time before growing bigger */
7909 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7911 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7912 6 - MovDelay[x][y]);
7914 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7917 if (!MovDelay[x][y])
7919 Feld[x][y] = Store[x][y];
7921 DrawLevelField(x, y);
7926 void AmoebaDisappearing(int x, int y)
7928 static unsigned long sound_delay = 0;
7929 static unsigned long sound_delay_value = 0;
7931 if (!MovDelay[x][y]) /* start new shrinking cycle */
7935 if (DelayReached(&sound_delay, sound_delay_value))
7936 sound_delay_value = 30;
7939 if (MovDelay[x][y]) /* wait some time before shrinking */
7942 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7944 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7945 6 - MovDelay[x][y]);
7947 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7950 if (!MovDelay[x][y])
7952 Feld[x][y] = EL_EMPTY;
7953 DrawLevelField(x, y);
7955 /* don't let mole enter this field in this cycle;
7956 (give priority to objects falling to this field from above) */
7962 void AmoebeAbleger(int ax, int ay)
7965 int element = Feld[ax][ay];
7966 int graphic = el2img(element);
7967 int newax = ax, neway = ay;
7968 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7969 static int xy[4][2] =
7977 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7979 Feld[ax][ay] = EL_AMOEBA_DEAD;
7980 DrawLevelField(ax, ay);
7984 if (IS_ANIMATED(graphic))
7985 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7987 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7988 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7990 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7993 if (MovDelay[ax][ay])
7997 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8000 int x = ax + xy[start][0];
8001 int y = ay + xy[start][1];
8003 if (!IN_LEV_FIELD(x, y))
8006 if (IS_FREE(x, y) ||
8007 CAN_GROW_INTO(Feld[x][y]) ||
8008 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8009 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8015 if (newax == ax && neway == ay)
8018 else /* normal or "filled" (BD style) amoeba */
8021 boolean waiting_for_player = FALSE;
8023 for (i = 0; i < NUM_DIRECTIONS; i++)
8025 int j = (start + i) % 4;
8026 int x = ax + xy[j][0];
8027 int y = ay + xy[j][1];
8029 if (!IN_LEV_FIELD(x, y))
8032 if (IS_FREE(x, y) ||
8033 CAN_GROW_INTO(Feld[x][y]) ||
8034 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8035 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8041 else if (IS_PLAYER(x, y))
8042 waiting_for_player = TRUE;
8045 if (newax == ax && neway == ay) /* amoeba cannot grow */
8047 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8049 Feld[ax][ay] = EL_AMOEBA_DEAD;
8050 DrawLevelField(ax, ay);
8051 AmoebaCnt[AmoebaNr[ax][ay]]--;
8053 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8055 if (element == EL_AMOEBA_FULL)
8056 AmoebeUmwandeln(ax, ay);
8057 else if (element == EL_BD_AMOEBA)
8058 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8063 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8065 /* amoeba gets larger by growing in some direction */
8067 int new_group_nr = AmoebaNr[ax][ay];
8070 if (new_group_nr == 0)
8072 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8073 printf("AmoebeAbleger(): This should never happen!\n");
8078 AmoebaNr[newax][neway] = new_group_nr;
8079 AmoebaCnt[new_group_nr]++;
8080 AmoebaCnt2[new_group_nr]++;
8082 /* if amoeba touches other amoeba(s) after growing, unify them */
8083 AmoebenVereinigen(newax, neway);
8085 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8087 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8093 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8094 (neway == lev_fieldy - 1 && newax != ax))
8096 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8097 Store[newax][neway] = element;
8099 else if (neway == ay || element == EL_EMC_DRIPPER)
8101 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8103 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8107 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8108 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8109 Store[ax][ay] = EL_AMOEBA_DROP;
8110 ContinueMoving(ax, ay);
8114 DrawLevelField(newax, neway);
8117 void Life(int ax, int ay)
8121 int element = Feld[ax][ay];
8122 int graphic = el2img(element);
8123 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8125 boolean changed = FALSE;
8127 if (IS_ANIMATED(graphic))
8128 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8133 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8134 MovDelay[ax][ay] = life_time;
8136 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8139 if (MovDelay[ax][ay])
8143 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8145 int xx = ax+x1, yy = ay+y1;
8148 if (!IN_LEV_FIELD(xx, yy))
8151 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8153 int x = xx+x2, y = yy+y2;
8155 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8158 if (((Feld[x][y] == element ||
8159 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8161 (IS_FREE(x, y) && Stop[x][y]))
8165 if (xx == ax && yy == ay) /* field in the middle */
8167 if (nachbarn < life_parameter[0] ||
8168 nachbarn > life_parameter[1])
8170 Feld[xx][yy] = EL_EMPTY;
8172 DrawLevelField(xx, yy);
8173 Stop[xx][yy] = TRUE;
8177 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8178 { /* free border field */
8179 if (nachbarn >= life_parameter[2] &&
8180 nachbarn <= life_parameter[3])
8182 Feld[xx][yy] = element;
8183 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8185 DrawLevelField(xx, yy);
8186 Stop[xx][yy] = TRUE;
8193 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8194 SND_GAME_OF_LIFE_GROWING);
8197 static void InitRobotWheel(int x, int y)
8199 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8202 static void RunRobotWheel(int x, int y)
8204 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8207 static void StopRobotWheel(int x, int y)
8209 if (ZX == x && ZY == y)
8213 static void InitTimegateWheel(int x, int y)
8215 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8218 static void RunTimegateWheel(int x, int y)
8220 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8223 static void InitMagicBallDelay(int x, int y)
8226 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8228 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8232 static void ActivateMagicBall(int bx, int by)
8236 if (level.ball_random)
8238 int pos_border = RND(8); /* select one of the eight border elements */
8239 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8240 int xx = pos_content % 3;
8241 int yy = pos_content / 3;
8246 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8247 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8251 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8253 int xx = x - bx + 1;
8254 int yy = y - by + 1;
8256 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8257 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8261 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8264 void CheckExit(int x, int y)
8266 if (local_player->gems_still_needed > 0 ||
8267 local_player->sokobanfields_still_needed > 0 ||
8268 local_player->lights_still_needed > 0)
8270 int element = Feld[x][y];
8271 int graphic = el2img(element);
8273 if (IS_ANIMATED(graphic))
8274 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8279 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8282 Feld[x][y] = EL_EXIT_OPENING;
8284 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8287 void CheckExitEM(int x, int y)
8289 if (local_player->gems_still_needed > 0 ||
8290 local_player->sokobanfields_still_needed > 0 ||
8291 local_player->lights_still_needed > 0)
8293 int element = Feld[x][y];
8294 int graphic = el2img(element);
8296 if (IS_ANIMATED(graphic))
8297 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8302 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8305 Feld[x][y] = EL_EM_EXIT_OPENING;
8307 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8310 void CheckExitSteel(int x, int y)
8312 if (local_player->gems_still_needed > 0 ||
8313 local_player->sokobanfields_still_needed > 0 ||
8314 local_player->lights_still_needed > 0)
8316 int element = Feld[x][y];
8317 int graphic = el2img(element);
8319 if (IS_ANIMATED(graphic))
8320 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8325 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8328 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8330 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8333 void CheckExitSteelEM(int x, int y)
8335 if (local_player->gems_still_needed > 0 ||
8336 local_player->sokobanfields_still_needed > 0 ||
8337 local_player->lights_still_needed > 0)
8339 int element = Feld[x][y];
8340 int graphic = el2img(element);
8342 if (IS_ANIMATED(graphic))
8343 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8348 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8351 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8353 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8356 void CheckExitSP(int x, int y)
8358 if (local_player->gems_still_needed > 0)
8360 int element = Feld[x][y];
8361 int graphic = el2img(element);
8363 if (IS_ANIMATED(graphic))
8364 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8369 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8372 Feld[x][y] = EL_SP_EXIT_OPENING;
8374 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8377 static void CloseAllOpenTimegates()
8381 SCAN_PLAYFIELD(x, y)
8383 int element = Feld[x][y];
8385 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8387 Feld[x][y] = EL_TIMEGATE_CLOSING;
8389 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8394 void DrawTwinkleOnField(int x, int y)
8396 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8399 if (Feld[x][y] == EL_BD_DIAMOND)
8402 if (MovDelay[x][y] == 0) /* next animation frame */
8403 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8405 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8409 if (setup.direct_draw && MovDelay[x][y])
8410 SetDrawtoField(DRAW_BUFFERED);
8412 DrawLevelElementAnimation(x, y, Feld[x][y]);
8414 if (MovDelay[x][y] != 0)
8416 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8417 10 - MovDelay[x][y]);
8419 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8421 if (setup.direct_draw)
8425 dest_x = FX + SCREENX(x) * TILEX;
8426 dest_y = FY + SCREENY(y) * TILEY;
8428 BlitBitmap(drawto_field, window,
8429 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8430 SetDrawtoField(DRAW_DIRECT);
8436 void MauerWaechst(int x, int y)
8440 if (!MovDelay[x][y]) /* next animation frame */
8441 MovDelay[x][y] = 3 * delay;
8443 if (MovDelay[x][y]) /* wait some time before next frame */
8447 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8449 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8450 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8452 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8455 if (!MovDelay[x][y])
8457 if (MovDir[x][y] == MV_LEFT)
8459 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8460 DrawLevelField(x - 1, y);
8462 else if (MovDir[x][y] == MV_RIGHT)
8464 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8465 DrawLevelField(x + 1, y);
8467 else if (MovDir[x][y] == MV_UP)
8469 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8470 DrawLevelField(x, y - 1);
8474 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8475 DrawLevelField(x, y + 1);
8478 Feld[x][y] = Store[x][y];
8480 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8481 DrawLevelField(x, y);
8486 void MauerAbleger(int ax, int ay)
8488 int element = Feld[ax][ay];
8489 int graphic = el2img(element);
8490 boolean oben_frei = FALSE, unten_frei = FALSE;
8491 boolean links_frei = FALSE, rechts_frei = FALSE;
8492 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8493 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8494 boolean new_wall = FALSE;
8496 if (IS_ANIMATED(graphic))
8497 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8499 if (!MovDelay[ax][ay]) /* start building new wall */
8500 MovDelay[ax][ay] = 6;
8502 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8505 if (MovDelay[ax][ay])
8509 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8511 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8513 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8515 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8518 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8519 element == EL_EXPANDABLE_WALL_ANY)
8523 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8524 Store[ax][ay-1] = element;
8525 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8526 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8527 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8528 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8533 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8534 Store[ax][ay+1] = element;
8535 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8536 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8537 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8538 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8543 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8544 element == EL_EXPANDABLE_WALL_ANY ||
8545 element == EL_EXPANDABLE_WALL ||
8546 element == EL_BD_EXPANDABLE_WALL)
8550 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8551 Store[ax-1][ay] = element;
8552 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8553 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8554 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8555 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8561 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8562 Store[ax+1][ay] = element;
8563 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8564 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8565 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8566 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8571 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8572 DrawLevelField(ax, ay);
8574 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8576 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8577 unten_massiv = TRUE;
8578 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8579 links_massiv = TRUE;
8580 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8581 rechts_massiv = TRUE;
8583 if (((oben_massiv && unten_massiv) ||
8584 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8585 element == EL_EXPANDABLE_WALL) &&
8586 ((links_massiv && rechts_massiv) ||
8587 element == EL_EXPANDABLE_WALL_VERTICAL))
8588 Feld[ax][ay] = EL_WALL;
8591 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8594 void MauerAblegerStahl(int ax, int ay)
8596 int element = Feld[ax][ay];
8597 int graphic = el2img(element);
8598 boolean oben_frei = FALSE, unten_frei = FALSE;
8599 boolean links_frei = FALSE, rechts_frei = FALSE;
8600 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8601 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8602 boolean new_wall = FALSE;
8604 if (IS_ANIMATED(graphic))
8605 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8607 if (!MovDelay[ax][ay]) /* start building new wall */
8608 MovDelay[ax][ay] = 6;
8610 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8613 if (MovDelay[ax][ay])
8617 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8619 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8621 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8623 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8626 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8627 element == EL_EXPANDABLE_STEELWALL_ANY)
8631 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8632 Store[ax][ay-1] = element;
8633 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8634 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8635 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8636 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8641 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8642 Store[ax][ay+1] = element;
8643 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8644 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8645 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8646 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8651 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8652 element == EL_EXPANDABLE_STEELWALL_ANY)
8656 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8657 Store[ax-1][ay] = element;
8658 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8659 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8660 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8661 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8667 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8668 Store[ax+1][ay] = element;
8669 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8670 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8671 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8672 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8677 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8679 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8680 unten_massiv = TRUE;
8681 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8682 links_massiv = TRUE;
8683 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8684 rechts_massiv = TRUE;
8686 if (((oben_massiv && unten_massiv) ||
8687 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8688 ((links_massiv && rechts_massiv) ||
8689 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8690 Feld[ax][ay] = EL_WALL;
8693 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8696 void CheckForDragon(int x, int y)
8699 boolean dragon_found = FALSE;
8700 static int xy[4][2] =
8708 for (i = 0; i < NUM_DIRECTIONS; i++)
8710 for (j = 0; j < 4; j++)
8712 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8714 if (IN_LEV_FIELD(xx, yy) &&
8715 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8717 if (Feld[xx][yy] == EL_DRAGON)
8718 dragon_found = TRUE;
8727 for (i = 0; i < NUM_DIRECTIONS; i++)
8729 for (j = 0; j < 3; j++)
8731 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8733 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8735 Feld[xx][yy] = EL_EMPTY;
8736 DrawLevelField(xx, yy);
8745 static void InitBuggyBase(int x, int y)
8747 int element = Feld[x][y];
8748 int activating_delay = FRAMES_PER_SECOND / 4;
8751 (element == EL_SP_BUGGY_BASE ?
8752 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8753 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8755 element == EL_SP_BUGGY_BASE_ACTIVE ?
8756 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8759 static void WarnBuggyBase(int x, int y)
8762 static int xy[4][2] =
8770 for (i = 0; i < NUM_DIRECTIONS; i++)
8772 int xx = x + xy[i][0];
8773 int yy = y + xy[i][1];
8775 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8777 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8784 static void InitTrap(int x, int y)
8786 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8789 static void ActivateTrap(int x, int y)
8791 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8794 static void ChangeActiveTrap(int x, int y)
8796 int graphic = IMG_TRAP_ACTIVE;
8798 /* if new animation frame was drawn, correct crumbled sand border */
8799 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8800 DrawLevelFieldCrumbledSand(x, y);
8803 static int getSpecialActionElement(int element, int number, int base_element)
8805 return (element != EL_EMPTY ? element :
8806 number != -1 ? base_element + number - 1 :
8810 static int getModifiedActionNumber(int value_old, int operator, int operand,
8811 int value_min, int value_max)
8813 int value_new = (operator == CA_MODE_SET ? operand :
8814 operator == CA_MODE_ADD ? value_old + operand :
8815 operator == CA_MODE_SUBTRACT ? value_old - operand :
8816 operator == CA_MODE_MULTIPLY ? value_old * operand :
8817 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8818 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8821 return (value_new < value_min ? value_min :
8822 value_new > value_max ? value_max :
8826 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8828 struct ElementInfo *ei = &element_info[element];
8829 struct ElementChangeInfo *change = &ei->change_page[page];
8830 int target_element = change->target_element;
8831 int action_type = change->action_type;
8832 int action_mode = change->action_mode;
8833 int action_arg = change->action_arg;
8836 if (!change->has_action)
8839 /* ---------- determine action paramater values -------------------------- */
8841 int level_time_value =
8842 (level.time > 0 ? TimeLeft :
8845 int action_arg_element =
8846 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8847 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8848 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8851 int action_arg_direction =
8852 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8853 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8854 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8855 change->actual_trigger_side :
8856 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8857 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8860 int action_arg_number_min =
8861 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8864 int action_arg_number_max =
8865 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8866 action_type == CA_SET_LEVEL_GEMS ? 999 :
8867 action_type == CA_SET_LEVEL_TIME ? 9999 :
8868 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8869 action_type == CA_SET_CE_VALUE ? 9999 :
8870 action_type == CA_SET_CE_SCORE ? 9999 :
8873 int action_arg_number_reset =
8874 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8875 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8876 action_type == CA_SET_LEVEL_TIME ? level.time :
8877 action_type == CA_SET_LEVEL_SCORE ? 0 :
8878 #if USE_NEW_CUSTOM_VALUE
8879 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8881 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8883 action_type == CA_SET_CE_SCORE ? 0 :
8886 int action_arg_number =
8887 (action_arg <= CA_ARG_MAX ? action_arg :
8888 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8889 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8890 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8891 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8892 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8893 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8894 #if USE_NEW_CUSTOM_VALUE
8895 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8897 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8899 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8900 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8901 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8902 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8903 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8904 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8905 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8906 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8907 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8908 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8909 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8912 int action_arg_number_old =
8913 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8914 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8915 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8916 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8917 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8920 int action_arg_number_new =
8921 getModifiedActionNumber(action_arg_number_old,
8922 action_mode, action_arg_number,
8923 action_arg_number_min, action_arg_number_max);
8925 int trigger_player_bits =
8926 (change->actual_trigger_player >= EL_PLAYER_1 &&
8927 change->actual_trigger_player <= EL_PLAYER_4 ?
8928 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8931 int action_arg_player_bits =
8932 (action_arg >= CA_ARG_PLAYER_1 &&
8933 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8934 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8937 /* ---------- execute action -------------------------------------------- */
8939 switch (action_type)
8946 /* ---------- level actions ------------------------------------------- */
8948 case CA_RESTART_LEVEL:
8950 game.restart_level = TRUE;
8955 case CA_SHOW_ENVELOPE:
8957 int element = getSpecialActionElement(action_arg_element,
8958 action_arg_number, EL_ENVELOPE_1);
8960 if (IS_ENVELOPE(element))
8961 local_player->show_envelope = element;
8966 case CA_SET_LEVEL_TIME:
8968 if (level.time > 0) /* only modify limited time value */
8970 TimeLeft = action_arg_number_new;
8972 DrawGameValue_Time(TimeLeft);
8974 if (!TimeLeft && setup.time_limit)
8975 for (i = 0; i < MAX_PLAYERS; i++)
8976 KillPlayer(&stored_player[i]);
8982 case CA_SET_LEVEL_SCORE:
8984 local_player->score = action_arg_number_new;
8986 DrawGameValue_Score(local_player->score);
8991 case CA_SET_LEVEL_GEMS:
8993 local_player->gems_still_needed = action_arg_number_new;
8995 DrawGameValue_Emeralds(local_player->gems_still_needed);
9000 #if !USE_PLAYER_GRAVITY
9001 case CA_SET_LEVEL_GRAVITY:
9003 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9004 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9005 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9011 case CA_SET_LEVEL_WIND:
9013 game.wind_direction = action_arg_direction;
9018 /* ---------- player actions ------------------------------------------ */
9020 case CA_MOVE_PLAYER:
9022 /* automatically move to the next field in specified direction */
9023 for (i = 0; i < MAX_PLAYERS; i++)
9024 if (trigger_player_bits & (1 << i))
9025 stored_player[i].programmed_action = action_arg_direction;
9030 case CA_EXIT_PLAYER:
9032 for (i = 0; i < MAX_PLAYERS; i++)
9033 if (action_arg_player_bits & (1 << i))
9034 PlayerWins(&stored_player[i]);
9039 case CA_KILL_PLAYER:
9041 for (i = 0; i < MAX_PLAYERS; i++)
9042 if (action_arg_player_bits & (1 << i))
9043 KillPlayer(&stored_player[i]);
9048 case CA_SET_PLAYER_KEYS:
9050 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9051 int element = getSpecialActionElement(action_arg_element,
9052 action_arg_number, EL_KEY_1);
9054 if (IS_KEY(element))
9056 for (i = 0; i < MAX_PLAYERS; i++)
9058 if (trigger_player_bits & (1 << i))
9060 stored_player[i].key[KEY_NR(element)] = key_state;
9062 DrawGameDoorValues();
9070 case CA_SET_PLAYER_SPEED:
9072 for (i = 0; i < MAX_PLAYERS; i++)
9074 if (trigger_player_bits & (1 << i))
9076 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9078 if (action_arg == CA_ARG_SPEED_FASTER &&
9079 stored_player[i].cannot_move)
9081 action_arg_number = STEPSIZE_VERY_SLOW;
9083 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9084 action_arg == CA_ARG_SPEED_FASTER)
9086 action_arg_number = 2;
9087 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9090 else if (action_arg == CA_ARG_NUMBER_RESET)
9092 action_arg_number = level.initial_player_stepsize[i];
9096 getModifiedActionNumber(move_stepsize,
9099 action_arg_number_min,
9100 action_arg_number_max);
9102 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9109 case CA_SET_PLAYER_SHIELD:
9111 for (i = 0; i < MAX_PLAYERS; i++)
9113 if (trigger_player_bits & (1 << i))
9115 if (action_arg == CA_ARG_SHIELD_OFF)
9117 stored_player[i].shield_normal_time_left = 0;
9118 stored_player[i].shield_deadly_time_left = 0;
9120 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9122 stored_player[i].shield_normal_time_left = 999999;
9124 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9126 stored_player[i].shield_normal_time_left = 999999;
9127 stored_player[i].shield_deadly_time_left = 999999;
9135 #if USE_PLAYER_GRAVITY
9136 case CA_SET_PLAYER_GRAVITY:
9138 for (i = 0; i < MAX_PLAYERS; i++)
9140 if (trigger_player_bits & (1 << i))
9142 stored_player[i].gravity =
9143 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9144 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9145 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9146 stored_player[i].gravity);
9154 case CA_SET_PLAYER_ARTWORK:
9156 for (i = 0; i < MAX_PLAYERS; i++)
9158 if (trigger_player_bits & (1 << i))
9160 int artwork_element = action_arg_element;
9162 if (action_arg == CA_ARG_ELEMENT_RESET)
9164 (level.use_artwork_element[i] ? level.artwork_element[i] :
9165 stored_player[i].element_nr);
9167 #if USE_GFX_RESET_PLAYER_ARTWORK
9168 if (stored_player[i].artwork_element != artwork_element)
9169 stored_player[i].Frame = 0;
9172 stored_player[i].artwork_element = artwork_element;
9174 SetPlayerWaiting(&stored_player[i], FALSE);
9176 /* set number of special actions for bored and sleeping animation */
9177 stored_player[i].num_special_action_bored =
9178 get_num_special_action(artwork_element,
9179 ACTION_BORING_1, ACTION_BORING_LAST);
9180 stored_player[i].num_special_action_sleeping =
9181 get_num_special_action(artwork_element,
9182 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9189 /* ---------- CE actions ---------------------------------------------- */
9191 case CA_SET_CE_VALUE:
9193 #if USE_NEW_CUSTOM_VALUE
9194 int last_ce_value = CustomValue[x][y];
9196 CustomValue[x][y] = action_arg_number_new;
9198 if (CustomValue[x][y] != last_ce_value)
9200 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9201 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9203 if (CustomValue[x][y] == 0)
9205 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9206 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9214 case CA_SET_CE_SCORE:
9216 #if USE_NEW_CUSTOM_VALUE
9217 int last_ce_score = ei->collect_score;
9219 ei->collect_score = action_arg_number_new;
9221 if (ei->collect_score != last_ce_score)
9223 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9224 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9226 if (ei->collect_score == 0)
9230 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9231 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9234 This is a very special case that seems to be a mixture between
9235 CheckElementChange() and CheckTriggeredElementChange(): while
9236 the first one only affects single elements that are triggered
9237 directly, the second one affects multiple elements in the playfield
9238 that are triggered indirectly by another element. This is a third
9239 case: Changing the CE score always affects multiple identical CEs,
9240 so every affected CE must be checked, not only the single CE for
9241 which the CE score was changed in the first place (as every instance
9242 of that CE shares the same CE score, and therefore also can change)!
9244 SCAN_PLAYFIELD(xx, yy)
9246 if (Feld[xx][yy] == element)
9247 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9248 CE_SCORE_GETS_ZERO);
9257 /* ---------- engine actions ------------------------------------------ */
9259 case CA_SET_ENGINE_SCAN_MODE:
9261 InitPlayfieldScanMode(action_arg);
9271 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9273 int old_element = Feld[x][y];
9274 int new_element = GetElementFromGroupElement(element);
9275 int previous_move_direction = MovDir[x][y];
9276 #if USE_NEW_CUSTOM_VALUE
9277 int last_ce_value = CustomValue[x][y];
9279 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9280 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9281 boolean add_player_onto_element = (new_element_is_player &&
9282 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9283 /* this breaks SnakeBite when a snake is
9284 halfway through a door that closes */
9285 /* NOW FIXED AT LEVEL INIT IN files.c */
9286 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9288 IS_WALKABLE(old_element));
9291 /* check if element under the player changes from accessible to unaccessible
9292 (needed for special case of dropping element which then changes) */
9293 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9294 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9302 if (!add_player_onto_element)
9304 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9305 RemoveMovingField(x, y);
9309 Feld[x][y] = new_element;
9311 #if !USE_GFX_RESET_GFX_ANIMATION
9312 ResetGfxAnimation(x, y);
9313 ResetRandomAnimationValue(x, y);
9316 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9317 MovDir[x][y] = previous_move_direction;
9319 #if USE_NEW_CUSTOM_VALUE
9320 if (element_info[new_element].use_last_ce_value)
9321 CustomValue[x][y] = last_ce_value;
9324 InitField_WithBug1(x, y, FALSE);
9326 new_element = Feld[x][y]; /* element may have changed */
9328 #if USE_GFX_RESET_GFX_ANIMATION
9329 ResetGfxAnimation(x, y);
9330 ResetRandomAnimationValue(x, y);
9333 DrawLevelField(x, y);
9335 if (GFX_CRUMBLED(new_element))
9336 DrawLevelFieldCrumbledSandNeighbours(x, y);
9340 /* check if element under the player changes from accessible to unaccessible
9341 (needed for special case of dropping element which then changes) */
9342 /* (must be checked after creating new element for walkable group elements) */
9343 #if USE_FIX_KILLED_BY_NON_WALKABLE
9344 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9345 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9352 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9353 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9362 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9363 if (new_element_is_player)
9364 RelocatePlayer(x, y, new_element);
9367 ChangeCount[x][y]++; /* count number of changes in the same frame */
9369 TestIfBadThingTouchesPlayer(x, y);
9370 TestIfPlayerTouchesCustomElement(x, y);
9371 TestIfElementTouchesCustomElement(x, y);
9374 static void CreateField(int x, int y, int element)
9376 CreateFieldExt(x, y, element, FALSE);
9379 static void CreateElementFromChange(int x, int y, int element)
9381 element = GET_VALID_RUNTIME_ELEMENT(element);
9383 #if USE_STOP_CHANGED_ELEMENTS
9384 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9386 int old_element = Feld[x][y];
9388 /* prevent changed element from moving in same engine frame
9389 unless both old and new element can either fall or move */
9390 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9391 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9396 CreateFieldExt(x, y, element, TRUE);
9399 static boolean ChangeElement(int x, int y, int element, int page)
9401 struct ElementInfo *ei = &element_info[element];
9402 struct ElementChangeInfo *change = &ei->change_page[page];
9403 int ce_value = CustomValue[x][y];
9404 int ce_score = ei->collect_score;
9406 int old_element = Feld[x][y];
9408 /* always use default change event to prevent running into a loop */
9409 if (ChangeEvent[x][y] == -1)
9410 ChangeEvent[x][y] = CE_DELAY;
9412 if (ChangeEvent[x][y] == CE_DELAY)
9414 /* reset actual trigger element, trigger player and action element */
9415 change->actual_trigger_element = EL_EMPTY;
9416 change->actual_trigger_player = EL_PLAYER_1;
9417 change->actual_trigger_side = CH_SIDE_NONE;
9418 change->actual_trigger_ce_value = 0;
9419 change->actual_trigger_ce_score = 0;
9422 /* do not change elements more than a specified maximum number of changes */
9423 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9426 ChangeCount[x][y]++; /* count number of changes in the same frame */
9428 if (change->explode)
9435 if (change->use_target_content)
9437 boolean complete_replace = TRUE;
9438 boolean can_replace[3][3];
9441 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9444 boolean is_walkable;
9445 boolean is_diggable;
9446 boolean is_collectible;
9447 boolean is_removable;
9448 boolean is_destructible;
9449 int ex = x + xx - 1;
9450 int ey = y + yy - 1;
9451 int content_element = change->target_content.e[xx][yy];
9454 can_replace[xx][yy] = TRUE;
9456 if (ex == x && ey == y) /* do not check changing element itself */
9459 if (content_element == EL_EMPTY_SPACE)
9461 can_replace[xx][yy] = FALSE; /* do not replace border with space */
9466 if (!IN_LEV_FIELD(ex, ey))
9468 can_replace[xx][yy] = FALSE;
9469 complete_replace = FALSE;
9476 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9477 e = MovingOrBlocked2Element(ex, ey);
9479 is_empty = (IS_FREE(ex, ey) ||
9480 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9482 is_walkable = (is_empty || IS_WALKABLE(e));
9483 is_diggable = (is_empty || IS_DIGGABLE(e));
9484 is_collectible = (is_empty || IS_COLLECTIBLE(e));
9485 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9486 is_removable = (is_diggable || is_collectible);
9488 can_replace[xx][yy] =
9489 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9490 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9491 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9492 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9493 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9494 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9495 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9497 if (!can_replace[xx][yy])
9498 complete_replace = FALSE;
9501 if (!change->only_if_complete || complete_replace)
9503 boolean something_has_changed = FALSE;
9505 if (change->only_if_complete && change->use_random_replace &&
9506 RND(100) < change->random_percentage)
9509 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9511 int ex = x + xx - 1;
9512 int ey = y + yy - 1;
9513 int content_element;
9515 if (can_replace[xx][yy] && (!change->use_random_replace ||
9516 RND(100) < change->random_percentage))
9518 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9519 RemoveMovingField(ex, ey);
9521 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9523 content_element = change->target_content.e[xx][yy];
9524 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9525 ce_value, ce_score);
9527 CreateElementFromChange(ex, ey, target_element);
9529 something_has_changed = TRUE;
9531 /* for symmetry reasons, freeze newly created border elements */
9532 if (ex != x || ey != y)
9533 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9537 if (something_has_changed)
9539 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9540 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9546 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9547 ce_value, ce_score);
9549 if (element == EL_DIAGONAL_GROWING ||
9550 element == EL_DIAGONAL_SHRINKING)
9552 target_element = Store[x][y];
9554 Store[x][y] = EL_EMPTY;
9557 CreateElementFromChange(x, y, target_element);
9559 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9560 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9563 /* this uses direct change before indirect change */
9564 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9569 #if USE_NEW_DELAYED_ACTION
9571 static void HandleElementChange(int x, int y, int page)
9573 int element = MovingOrBlocked2Element(x, y);
9574 struct ElementInfo *ei = &element_info[element];
9575 struct ElementChangeInfo *change = &ei->change_page[page];
9578 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9579 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9582 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9583 x, y, element, element_info[element].token_name);
9584 printf("HandleElementChange(): This should never happen!\n");
9589 /* this can happen with classic bombs on walkable, changing elements */
9590 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9593 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9594 ChangeDelay[x][y] = 0;
9600 if (ChangeDelay[x][y] == 0) /* initialize element change */
9602 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9604 if (change->can_change)
9607 /* !!! not clear why graphic animation should be reset at all here !!! */
9608 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9609 #if USE_GFX_RESET_WHEN_NOT_MOVING
9610 /* when a custom element is about to change (for example by change delay),
9611 do not reset graphic animation when the custom element is moving */
9612 if (!IS_MOVING(x, y))
9615 ResetGfxAnimation(x, y);
9616 ResetRandomAnimationValue(x, y);
9620 if (change->pre_change_function)
9621 change->pre_change_function(x, y);
9625 ChangeDelay[x][y]--;
9627 if (ChangeDelay[x][y] != 0) /* continue element change */
9629 if (change->can_change)
9631 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9633 if (IS_ANIMATED(graphic))
9634 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9636 if (change->change_function)
9637 change->change_function(x, y);
9640 else /* finish element change */
9642 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9644 page = ChangePage[x][y];
9645 ChangePage[x][y] = -1;
9647 change = &ei->change_page[page];
9650 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9652 ChangeDelay[x][y] = 1; /* try change after next move step */
9653 ChangePage[x][y] = page; /* remember page to use for change */
9658 if (change->can_change)
9660 if (ChangeElement(x, y, element, page))
9662 if (change->post_change_function)
9663 change->post_change_function(x, y);
9667 if (change->has_action)
9668 ExecuteCustomElementAction(x, y, element, page);
9674 static void HandleElementChange(int x, int y, int page)
9676 int element = MovingOrBlocked2Element(x, y);
9677 struct ElementInfo *ei = &element_info[element];
9678 struct ElementChangeInfo *change = &ei->change_page[page];
9681 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9684 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9685 x, y, element, element_info[element].token_name);
9686 printf("HandleElementChange(): This should never happen!\n");
9691 /* this can happen with classic bombs on walkable, changing elements */
9692 if (!CAN_CHANGE(element))
9695 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9696 ChangeDelay[x][y] = 0;
9702 if (ChangeDelay[x][y] == 0) /* initialize element change */
9704 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9706 ResetGfxAnimation(x, y);
9707 ResetRandomAnimationValue(x, y);
9709 if (change->pre_change_function)
9710 change->pre_change_function(x, y);
9713 ChangeDelay[x][y]--;
9715 if (ChangeDelay[x][y] != 0) /* continue element change */
9717 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9719 if (IS_ANIMATED(graphic))
9720 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9722 if (change->change_function)
9723 change->change_function(x, y);
9725 else /* finish element change */
9727 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9729 page = ChangePage[x][y];
9730 ChangePage[x][y] = -1;
9732 change = &ei->change_page[page];
9735 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9737 ChangeDelay[x][y] = 1; /* try change after next move step */
9738 ChangePage[x][y] = page; /* remember page to use for change */
9743 if (ChangeElement(x, y, element, page))
9745 if (change->post_change_function)
9746 change->post_change_function(x, y);
9753 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9754 int trigger_element,
9760 boolean change_done_any = FALSE;
9761 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9764 if (!(trigger_events[trigger_element][trigger_event]))
9768 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9769 trigger_event, recursion_loop_depth, recursion_loop_detected,
9770 recursion_loop_element, EL_NAME(recursion_loop_element));
9773 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9775 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9777 int element = EL_CUSTOM_START + i;
9778 boolean change_done = FALSE;
9781 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9782 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9785 for (p = 0; p < element_info[element].num_change_pages; p++)
9787 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9789 if (change->can_change_or_has_action &&
9790 change->has_event[trigger_event] &&
9791 change->trigger_side & trigger_side &&
9792 change->trigger_player & trigger_player &&
9793 change->trigger_page & trigger_page_bits &&
9794 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9796 change->actual_trigger_element = trigger_element;
9797 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9798 change->actual_trigger_side = trigger_side;
9799 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9800 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9802 if ((change->can_change && !change_done) || change->has_action)
9806 SCAN_PLAYFIELD(x, y)
9808 if (Feld[x][y] == element)
9810 if (change->can_change && !change_done)
9812 ChangeDelay[x][y] = 1;
9813 ChangeEvent[x][y] = trigger_event;
9815 HandleElementChange(x, y, p);
9817 #if USE_NEW_DELAYED_ACTION
9818 else if (change->has_action)
9820 ExecuteCustomElementAction(x, y, element, p);
9821 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9824 if (change->has_action)
9826 ExecuteCustomElementAction(x, y, element, p);
9827 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9833 if (change->can_change)
9836 change_done_any = TRUE;
9843 RECURSION_LOOP_DETECTION_END();
9845 return change_done_any;
9848 static boolean CheckElementChangeExt(int x, int y,
9850 int trigger_element,
9855 boolean change_done = FALSE;
9858 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9859 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9862 if (Feld[x][y] == EL_BLOCKED)
9864 Blocked2Moving(x, y, &x, &y);
9865 element = Feld[x][y];
9869 /* check if element has already changed */
9870 if (Feld[x][y] != element)
9873 /* check if element has already changed or is about to change after moving */
9874 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9875 Feld[x][y] != element) ||
9877 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9878 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9879 ChangePage[x][y] != -1)))
9884 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9885 trigger_event, recursion_loop_depth, recursion_loop_detected,
9886 recursion_loop_element, EL_NAME(recursion_loop_element));
9889 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9891 for (p = 0; p < element_info[element].num_change_pages; p++)
9893 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9895 /* check trigger element for all events where the element that is checked
9896 for changing interacts with a directly adjacent element -- this is
9897 different to element changes that affect other elements to change on the
9898 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9899 boolean check_trigger_element =
9900 (trigger_event == CE_TOUCHING_X ||
9901 trigger_event == CE_HITTING_X ||
9902 trigger_event == CE_HIT_BY_X ||
9904 /* this one was forgotten until 3.2.3 */
9905 trigger_event == CE_DIGGING_X);
9908 if (change->can_change_or_has_action &&
9909 change->has_event[trigger_event] &&
9910 change->trigger_side & trigger_side &&
9911 change->trigger_player & trigger_player &&
9912 (!check_trigger_element ||
9913 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9915 change->actual_trigger_element = trigger_element;
9916 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9917 change->actual_trigger_side = trigger_side;
9918 change->actual_trigger_ce_value = CustomValue[x][y];
9919 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9921 /* special case: trigger element not at (x,y) position for some events */
9922 if (check_trigger_element)
9934 { 0, 0 }, { 0, 0 }, { 0, 0 },
9938 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9939 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9941 change->actual_trigger_ce_value = CustomValue[xx][yy];
9942 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9945 if (change->can_change && !change_done)
9947 ChangeDelay[x][y] = 1;
9948 ChangeEvent[x][y] = trigger_event;
9950 HandleElementChange(x, y, p);
9954 #if USE_NEW_DELAYED_ACTION
9955 else if (change->has_action)
9957 ExecuteCustomElementAction(x, y, element, p);
9958 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9961 if (change->has_action)
9963 ExecuteCustomElementAction(x, y, element, p);
9964 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9970 RECURSION_LOOP_DETECTION_END();
9975 static void PlayPlayerSound(struct PlayerInfo *player)
9977 int jx = player->jx, jy = player->jy;
9978 int sound_element = player->artwork_element;
9979 int last_action = player->last_action_waiting;
9980 int action = player->action_waiting;
9982 if (player->is_waiting)
9984 if (action != last_action)
9985 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9987 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9991 if (action != last_action)
9992 StopSound(element_info[sound_element].sound[last_action]);
9994 if (last_action == ACTION_SLEEPING)
9995 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9999 static void PlayAllPlayersSound()
10003 for (i = 0; i < MAX_PLAYERS; i++)
10004 if (stored_player[i].active)
10005 PlayPlayerSound(&stored_player[i]);
10008 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10010 boolean last_waiting = player->is_waiting;
10011 int move_dir = player->MovDir;
10013 player->dir_waiting = move_dir;
10014 player->last_action_waiting = player->action_waiting;
10018 if (!last_waiting) /* not waiting -> waiting */
10020 player->is_waiting = TRUE;
10022 player->frame_counter_bored =
10024 game.player_boring_delay_fixed +
10025 GetSimpleRandom(game.player_boring_delay_random);
10026 player->frame_counter_sleeping =
10028 game.player_sleeping_delay_fixed +
10029 GetSimpleRandom(game.player_sleeping_delay_random);
10031 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10034 if (game.player_sleeping_delay_fixed +
10035 game.player_sleeping_delay_random > 0 &&
10036 player->anim_delay_counter == 0 &&
10037 player->post_delay_counter == 0 &&
10038 FrameCounter >= player->frame_counter_sleeping)
10039 player->is_sleeping = TRUE;
10040 else if (game.player_boring_delay_fixed +
10041 game.player_boring_delay_random > 0 &&
10042 FrameCounter >= player->frame_counter_bored)
10043 player->is_bored = TRUE;
10045 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10046 player->is_bored ? ACTION_BORING :
10049 if (player->is_sleeping && player->use_murphy)
10051 /* special case for sleeping Murphy when leaning against non-free tile */
10053 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10054 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10055 !IS_MOVING(player->jx - 1, player->jy)))
10056 move_dir = MV_LEFT;
10057 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10058 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10059 !IS_MOVING(player->jx + 1, player->jy)))
10060 move_dir = MV_RIGHT;
10062 player->is_sleeping = FALSE;
10064 player->dir_waiting = move_dir;
10067 if (player->is_sleeping)
10069 if (player->num_special_action_sleeping > 0)
10071 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10073 int last_special_action = player->special_action_sleeping;
10074 int num_special_action = player->num_special_action_sleeping;
10075 int special_action =
10076 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10077 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10078 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10079 last_special_action + 1 : ACTION_SLEEPING);
10080 int special_graphic =
10081 el_act_dir2img(player->artwork_element, special_action, move_dir);
10083 player->anim_delay_counter =
10084 graphic_info[special_graphic].anim_delay_fixed +
10085 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10086 player->post_delay_counter =
10087 graphic_info[special_graphic].post_delay_fixed +
10088 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10090 player->special_action_sleeping = special_action;
10093 if (player->anim_delay_counter > 0)
10095 player->action_waiting = player->special_action_sleeping;
10096 player->anim_delay_counter--;
10098 else if (player->post_delay_counter > 0)
10100 player->post_delay_counter--;
10104 else if (player->is_bored)
10106 if (player->num_special_action_bored > 0)
10108 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10110 int special_action =
10111 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10112 int special_graphic =
10113 el_act_dir2img(player->artwork_element, special_action, move_dir);
10115 player->anim_delay_counter =
10116 graphic_info[special_graphic].anim_delay_fixed +
10117 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10118 player->post_delay_counter =
10119 graphic_info[special_graphic].post_delay_fixed +
10120 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10122 player->special_action_bored = special_action;
10125 if (player->anim_delay_counter > 0)
10127 player->action_waiting = player->special_action_bored;
10128 player->anim_delay_counter--;
10130 else if (player->post_delay_counter > 0)
10132 player->post_delay_counter--;
10137 else if (last_waiting) /* waiting -> not waiting */
10139 player->is_waiting = FALSE;
10140 player->is_bored = FALSE;
10141 player->is_sleeping = FALSE;
10143 player->frame_counter_bored = -1;
10144 player->frame_counter_sleeping = -1;
10146 player->anim_delay_counter = 0;
10147 player->post_delay_counter = 0;
10149 player->dir_waiting = player->MovDir;
10150 player->action_waiting = ACTION_DEFAULT;
10152 player->special_action_bored = ACTION_DEFAULT;
10153 player->special_action_sleeping = ACTION_DEFAULT;
10157 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10159 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10160 int left = player_action & JOY_LEFT;
10161 int right = player_action & JOY_RIGHT;
10162 int up = player_action & JOY_UP;
10163 int down = player_action & JOY_DOWN;
10164 int button1 = player_action & JOY_BUTTON_1;
10165 int button2 = player_action & JOY_BUTTON_2;
10166 int dx = (left ? -1 : right ? 1 : 0);
10167 int dy = (up ? -1 : down ? 1 : 0);
10169 if (!player->active || tape.pausing)
10175 snapped = SnapField(player, dx, dy);
10179 dropped = DropElement(player);
10181 moved = MovePlayer(player, dx, dy);
10184 if (tape.single_step && tape.recording && !tape.pausing)
10186 if (button1 || (dropped && !moved))
10188 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10189 SnapField(player, 0, 0); /* stop snapping */
10193 SetPlayerWaiting(player, FALSE);
10195 return player_action;
10199 /* no actions for this player (no input at player's configured device) */
10201 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10202 SnapField(player, 0, 0);
10203 CheckGravityMovementWhenNotMoving(player);
10205 if (player->MovPos == 0)
10206 SetPlayerWaiting(player, TRUE);
10208 if (player->MovPos == 0) /* needed for tape.playing */
10209 player->is_moving = FALSE;
10211 player->is_dropping = FALSE;
10212 player->is_dropping_pressed = FALSE;
10213 player->drop_pressed_delay = 0;
10219 static void CheckLevelTime()
10223 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10225 if (level.native_em_level->lev->home == 0) /* all players at home */
10227 PlayerWins(local_player);
10229 AllPlayersGone = TRUE;
10231 level.native_em_level->lev->home = -1;
10234 if (level.native_em_level->ply[0]->alive == 0 &&
10235 level.native_em_level->ply[1]->alive == 0 &&
10236 level.native_em_level->ply[2]->alive == 0 &&
10237 level.native_em_level->ply[3]->alive == 0) /* all dead */
10238 AllPlayersGone = TRUE;
10241 if (TimeFrames >= FRAMES_PER_SECOND)
10246 for (i = 0; i < MAX_PLAYERS; i++)
10248 struct PlayerInfo *player = &stored_player[i];
10250 if (SHIELD_ON(player))
10252 player->shield_normal_time_left--;
10254 if (player->shield_deadly_time_left > 0)
10255 player->shield_deadly_time_left--;
10259 if (!local_player->LevelSolved && !level.use_step_counter)
10267 if (TimeLeft <= 10 && setup.time_limit)
10268 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10270 DrawGameValue_Time(TimeLeft);
10272 if (!TimeLeft && setup.time_limit)
10274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10275 level.native_em_level->lev->killed_out_of_time = TRUE;
10277 for (i = 0; i < MAX_PLAYERS; i++)
10278 KillPlayer(&stored_player[i]);
10281 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10282 DrawGameValue_Time(TimePlayed);
10284 level.native_em_level->lev->time =
10285 (level.time == 0 ? TimePlayed : TimeLeft);
10288 if (tape.recording || tape.playing)
10289 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10293 void AdvanceFrameAndPlayerCounters(int player_nr)
10297 /* advance frame counters (global frame counter and time frame counter) */
10301 /* advance player counters (counters for move delay, move animation etc.) */
10302 for (i = 0; i < MAX_PLAYERS; i++)
10304 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10305 int move_delay_value = stored_player[i].move_delay_value;
10306 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10308 if (!advance_player_counters) /* not all players may be affected */
10311 #if USE_NEW_PLAYER_ANIM
10312 if (move_frames == 0) /* less than one move per game frame */
10314 int stepsize = TILEX / move_delay_value;
10315 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10316 int count = (stored_player[i].is_moving ?
10317 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10319 if (count % delay == 0)
10324 stored_player[i].Frame += move_frames;
10326 if (stored_player[i].MovPos != 0)
10327 stored_player[i].StepFrame += move_frames;
10329 if (stored_player[i].move_delay > 0)
10330 stored_player[i].move_delay--;
10332 /* due to bugs in previous versions, counter must count up, not down */
10333 if (stored_player[i].push_delay != -1)
10334 stored_player[i].push_delay++;
10336 if (stored_player[i].drop_delay > 0)
10337 stored_player[i].drop_delay--;
10339 if (stored_player[i].is_dropping_pressed)
10340 stored_player[i].drop_pressed_delay++;
10344 void StartGameActions(boolean init_network_game, boolean record_tape,
10347 unsigned long new_random_seed = InitRND(random_seed);
10350 TapeStartRecording(new_random_seed);
10352 #if defined(NETWORK_AVALIABLE)
10353 if (init_network_game)
10355 SendToServer_StartPlaying();
10366 static unsigned long game_frame_delay = 0;
10367 unsigned long game_frame_delay_value;
10368 byte *recorded_player_action;
10369 byte summarized_player_action = 0;
10370 byte tape_action[MAX_PLAYERS];
10373 /* detect endless loops, caused by custom element programming */
10374 if (recursion_loop_detected && recursion_loop_depth == 0)
10376 char *message = getStringCat3("Internal Error ! Element ",
10377 EL_NAME(recursion_loop_element),
10378 " caused endless loop ! Quit the game ?");
10380 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10381 EL_NAME(recursion_loop_element));
10383 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10385 recursion_loop_detected = FALSE; /* if game should be continued */
10392 if (game.restart_level)
10393 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10395 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10397 if (level.native_em_level->lev->home == 0) /* all players at home */
10399 PlayerWins(local_player);
10401 AllPlayersGone = TRUE;
10403 level.native_em_level->lev->home = -1;
10406 if (level.native_em_level->ply[0]->alive == 0 &&
10407 level.native_em_level->ply[1]->alive == 0 &&
10408 level.native_em_level->ply[2]->alive == 0 &&
10409 level.native_em_level->ply[3]->alive == 0) /* all dead */
10410 AllPlayersGone = TRUE;
10413 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10416 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10419 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10422 game_frame_delay_value =
10423 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10425 if (tape.playing && tape.warp_forward && !tape.pausing)
10426 game_frame_delay_value = 0;
10428 /* ---------- main game synchronization point ---------- */
10430 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10432 if (network_playing && !network_player_action_received)
10434 /* try to get network player actions in time */
10436 #if defined(NETWORK_AVALIABLE)
10437 /* last chance to get network player actions without main loop delay */
10438 HandleNetworking();
10441 /* game was quit by network peer */
10442 if (game_status != GAME_MODE_PLAYING)
10445 if (!network_player_action_received)
10446 return; /* failed to get network player actions in time */
10448 /* do not yet reset "network_player_action_received" (for tape.pausing) */
10454 /* at this point we know that we really continue executing the game */
10456 network_player_action_received = FALSE;
10458 /* when playing tape, read previously recorded player input from tape data */
10459 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10462 /* TapePlayAction() may return NULL when toggling to "pause before death" */
10467 if (tape.set_centered_player)
10469 game.centered_player_nr_next = tape.centered_player_nr_next;
10470 game.set_centered_player = TRUE;
10473 for (i = 0; i < MAX_PLAYERS; i++)
10475 summarized_player_action |= stored_player[i].action;
10477 if (!network_playing)
10478 stored_player[i].effective_action = stored_player[i].action;
10481 #if defined(NETWORK_AVALIABLE)
10482 if (network_playing)
10483 SendToServer_MovePlayer(summarized_player_action);
10486 if (!options.network && !setup.team_mode)
10487 local_player->effective_action = summarized_player_action;
10489 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10491 for (i = 0; i < MAX_PLAYERS; i++)
10492 stored_player[i].effective_action =
10493 (i == game.centered_player_nr ? summarized_player_action : 0);
10496 if (recorded_player_action != NULL)
10497 for (i = 0; i < MAX_PLAYERS; i++)
10498 stored_player[i].effective_action = recorded_player_action[i];
10500 for (i = 0; i < MAX_PLAYERS; i++)
10502 tape_action[i] = stored_player[i].effective_action;
10504 /* (this can only happen in the R'n'D game engine) */
10505 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10506 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10509 /* only record actions from input devices, but not programmed actions */
10510 if (tape.recording)
10511 TapeRecordAction(tape_action);
10513 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10515 GameActions_EM_Main();
10523 void GameActions_EM_Main()
10525 byte effective_action[MAX_PLAYERS];
10526 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10529 for (i = 0; i < MAX_PLAYERS; i++)
10530 effective_action[i] = stored_player[i].effective_action;
10532 GameActions_EM(effective_action, warp_mode);
10536 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10539 void GameActions_RND()
10541 int magic_wall_x = 0, magic_wall_y = 0;
10542 int i, x, y, element, graphic;
10544 InitPlayfieldScanModeVars();
10546 #if USE_ONE_MORE_CHANGE_PER_FRAME
10547 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10549 SCAN_PLAYFIELD(x, y)
10551 ChangeCount[x][y] = 0;
10552 ChangeEvent[x][y] = -1;
10557 if (game.set_centered_player)
10559 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10561 /* switching to "all players" only possible if all players fit to screen */
10562 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10564 game.centered_player_nr_next = game.centered_player_nr;
10565 game.set_centered_player = FALSE;
10568 /* do not switch focus to non-existing (or non-active) player */
10569 if (game.centered_player_nr_next >= 0 &&
10570 !stored_player[game.centered_player_nr_next].active)
10572 game.centered_player_nr_next = game.centered_player_nr;
10573 game.set_centered_player = FALSE;
10577 if (game.set_centered_player &&
10578 ScreenMovPos == 0) /* screen currently aligned at tile position */
10582 if (game.centered_player_nr_next == -1)
10584 setScreenCenteredToAllPlayers(&sx, &sy);
10588 sx = stored_player[game.centered_player_nr_next].jx;
10589 sy = stored_player[game.centered_player_nr_next].jy;
10592 game.centered_player_nr = game.centered_player_nr_next;
10593 game.set_centered_player = FALSE;
10595 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10596 DrawGameDoorValues();
10599 for (i = 0; i < MAX_PLAYERS; i++)
10601 int actual_player_action = stored_player[i].effective_action;
10604 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10605 - rnd_equinox_tetrachloride 048
10606 - rnd_equinox_tetrachloride_ii 096
10607 - rnd_emanuel_schmieg 002
10608 - doctor_sloan_ww 001, 020
10610 if (stored_player[i].MovPos == 0)
10611 CheckGravityMovement(&stored_player[i]);
10614 /* overwrite programmed action with tape action */
10615 if (stored_player[i].programmed_action)
10616 actual_player_action = stored_player[i].programmed_action;
10618 PlayerActions(&stored_player[i], actual_player_action);
10620 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10623 ScrollScreen(NULL, SCROLL_GO_ON);
10625 /* for backwards compatibility, the following code emulates a fixed bug that
10626 occured when pushing elements (causing elements that just made their last
10627 pushing step to already (if possible) make their first falling step in the
10628 same game frame, which is bad); this code is also needed to use the famous
10629 "spring push bug" which is used in older levels and might be wanted to be
10630 used also in newer levels, but in this case the buggy pushing code is only
10631 affecting the "spring" element and no other elements */
10633 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10635 for (i = 0; i < MAX_PLAYERS; i++)
10637 struct PlayerInfo *player = &stored_player[i];
10638 int x = player->jx;
10639 int y = player->jy;
10641 if (player->active && player->is_pushing && player->is_moving &&
10643 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10644 Feld[x][y] == EL_SPRING))
10646 ContinueMoving(x, y);
10648 /* continue moving after pushing (this is actually a bug) */
10649 if (!IS_MOVING(x, y))
10650 Stop[x][y] = FALSE;
10656 debug_print_timestamp(0, "start main loop profiling");
10659 SCAN_PLAYFIELD(x, y)
10661 ChangeCount[x][y] = 0;
10662 ChangeEvent[x][y] = -1;
10664 /* this must be handled before main playfield loop */
10665 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10668 if (MovDelay[x][y] <= 0)
10672 #if USE_NEW_SNAP_DELAY
10673 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10676 if (MovDelay[x][y] <= 0)
10679 DrawLevelField(x, y);
10681 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10687 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10689 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10690 printf("GameActions(): This should never happen!\n");
10692 ChangePage[x][y] = -1;
10696 Stop[x][y] = FALSE;
10697 if (WasJustMoving[x][y] > 0)
10698 WasJustMoving[x][y]--;
10699 if (WasJustFalling[x][y] > 0)
10700 WasJustFalling[x][y]--;
10701 if (CheckCollision[x][y] > 0)
10702 CheckCollision[x][y]--;
10703 if (CheckImpact[x][y] > 0)
10704 CheckImpact[x][y]--;
10708 /* reset finished pushing action (not done in ContinueMoving() to allow
10709 continuous pushing animation for elements with zero push delay) */
10710 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10712 ResetGfxAnimation(x, y);
10713 DrawLevelField(x, y);
10717 if (IS_BLOCKED(x, y))
10721 Blocked2Moving(x, y, &oldx, &oldy);
10722 if (!IS_MOVING(oldx, oldy))
10724 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10725 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10726 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10727 printf("GameActions(): This should never happen!\n");
10734 debug_print_timestamp(0, "- time for pre-main loop:");
10737 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
10738 SCAN_PLAYFIELD(x, y)
10740 element = Feld[x][y];
10741 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10746 int element2 = element;
10747 int graphic2 = graphic;
10749 int element2 = Feld[x][y];
10750 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10752 int last_gfx_frame = GfxFrame[x][y];
10754 if (graphic_info[graphic2].anim_global_sync)
10755 GfxFrame[x][y] = FrameCounter;
10756 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10757 GfxFrame[x][y] = CustomValue[x][y];
10758 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10759 GfxFrame[x][y] = element_info[element2].collect_score;
10760 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10761 GfxFrame[x][y] = ChangeDelay[x][y];
10763 if (redraw && GfxFrame[x][y] != last_gfx_frame)
10764 DrawLevelGraphicAnimation(x, y, graphic2);
10767 ResetGfxFrame(x, y, TRUE);
10771 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10772 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10773 ResetRandomAnimationValue(x, y);
10777 SetRandomAnimationValue(x, y);
10781 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10784 #endif // -------------------- !!! TEST ONLY !!! --------------------
10787 debug_print_timestamp(0, "- time for TEST loop: -->");
10790 SCAN_PLAYFIELD(x, y)
10792 element = Feld[x][y];
10793 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10795 ResetGfxFrame(x, y, TRUE);
10797 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10798 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10799 ResetRandomAnimationValue(x, y);
10801 SetRandomAnimationValue(x, y);
10803 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10805 if (IS_INACTIVE(element))
10807 if (IS_ANIMATED(graphic))
10808 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10813 /* this may take place after moving, so 'element' may have changed */
10814 if (IS_CHANGING(x, y) &&
10815 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10817 int page = element_info[element].event_page_nr[CE_DELAY];
10820 HandleElementChange(x, y, page);
10822 if (CAN_CHANGE(element))
10823 HandleElementChange(x, y, page);
10825 if (HAS_ACTION(element))
10826 ExecuteCustomElementAction(x, y, element, page);
10829 element = Feld[x][y];
10830 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10833 #if 0 // ---------------------------------------------------------------------
10835 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10839 element = Feld[x][y];
10840 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10842 if (IS_ANIMATED(graphic) &&
10843 !IS_MOVING(x, y) &&
10845 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10847 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10848 DrawTwinkleOnField(x, y);
10850 else if (IS_MOVING(x, y))
10851 ContinueMoving(x, y);
10858 case EL_EM_EXIT_OPEN:
10859 case EL_SP_EXIT_OPEN:
10860 case EL_STEEL_EXIT_OPEN:
10861 case EL_EM_STEEL_EXIT_OPEN:
10862 case EL_SP_TERMINAL:
10863 case EL_SP_TERMINAL_ACTIVE:
10864 case EL_EXTRA_TIME:
10865 case EL_SHIELD_NORMAL:
10866 case EL_SHIELD_DEADLY:
10867 if (IS_ANIMATED(graphic))
10868 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10871 case EL_DYNAMITE_ACTIVE:
10872 case EL_EM_DYNAMITE_ACTIVE:
10873 case EL_DYNABOMB_PLAYER_1_ACTIVE:
10874 case EL_DYNABOMB_PLAYER_2_ACTIVE:
10875 case EL_DYNABOMB_PLAYER_3_ACTIVE:
10876 case EL_DYNABOMB_PLAYER_4_ACTIVE:
10877 case EL_SP_DISK_RED_ACTIVE:
10878 CheckDynamite(x, y);
10881 case EL_AMOEBA_GROWING:
10882 AmoebeWaechst(x, y);
10885 case EL_AMOEBA_SHRINKING:
10886 AmoebaDisappearing(x, y);
10889 #if !USE_NEW_AMOEBA_CODE
10890 case EL_AMOEBA_WET:
10891 case EL_AMOEBA_DRY:
10892 case EL_AMOEBA_FULL:
10894 case EL_EMC_DRIPPER:
10895 AmoebeAbleger(x, y);
10899 case EL_GAME_OF_LIFE:
10904 case EL_EXIT_CLOSED:
10908 case EL_EM_EXIT_CLOSED:
10912 case EL_STEEL_EXIT_CLOSED:
10913 CheckExitSteel(x, y);
10916 case EL_EM_STEEL_EXIT_CLOSED:
10917 CheckExitSteelEM(x, y);
10920 case EL_SP_EXIT_CLOSED:
10924 case EL_EXPANDABLE_WALL_GROWING:
10925 case EL_EXPANDABLE_STEELWALL_GROWING:
10926 MauerWaechst(x, y);
10929 case EL_EXPANDABLE_WALL:
10930 case EL_EXPANDABLE_WALL_HORIZONTAL:
10931 case EL_EXPANDABLE_WALL_VERTICAL:
10932 case EL_EXPANDABLE_WALL_ANY:
10933 case EL_BD_EXPANDABLE_WALL:
10934 MauerAbleger(x, y);
10937 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10938 case EL_EXPANDABLE_STEELWALL_VERTICAL:
10939 case EL_EXPANDABLE_STEELWALL_ANY:
10940 MauerAblegerStahl(x, y);
10944 CheckForDragon(x, y);
10950 case EL_ELEMENT_SNAPPING:
10951 case EL_DIAGONAL_SHRINKING:
10952 case EL_DIAGONAL_GROWING:
10955 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10957 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10962 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10963 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10968 #else // ---------------------------------------------------------------------
10970 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10974 element = Feld[x][y];
10975 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10977 if (IS_ANIMATED(graphic) &&
10978 !IS_MOVING(x, y) &&
10980 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10982 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10983 DrawTwinkleOnField(x, y);
10985 else if ((element == EL_ACID ||
10986 element == EL_EXIT_OPEN ||
10987 element == EL_EM_EXIT_OPEN ||
10988 element == EL_SP_EXIT_OPEN ||
10989 element == EL_STEEL_EXIT_OPEN ||
10990 element == EL_EM_STEEL_EXIT_OPEN ||
10991 element == EL_SP_TERMINAL ||
10992 element == EL_SP_TERMINAL_ACTIVE ||
10993 element == EL_EXTRA_TIME ||
10994 element == EL_SHIELD_NORMAL ||
10995 element == EL_SHIELD_DEADLY) &&
10996 IS_ANIMATED(graphic))
10997 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10998 else if (IS_MOVING(x, y))
10999 ContinueMoving(x, y);
11000 else if (IS_ACTIVE_BOMB(element))
11001 CheckDynamite(x, y);
11002 else if (element == EL_AMOEBA_GROWING)
11003 AmoebeWaechst(x, y);
11004 else if (element == EL_AMOEBA_SHRINKING)
11005 AmoebaDisappearing(x, y);
11007 #if !USE_NEW_AMOEBA_CODE
11008 else if (IS_AMOEBALIVE(element))
11009 AmoebeAbleger(x, y);
11012 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11014 else if (element == EL_EXIT_CLOSED)
11016 else if (element == EL_EM_EXIT_CLOSED)
11018 else if (element == EL_STEEL_EXIT_CLOSED)
11019 CheckExitSteel(x, y);
11020 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11021 CheckExitSteelEM(x, y);
11022 else if (element == EL_SP_EXIT_CLOSED)
11024 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11025 element == EL_EXPANDABLE_STEELWALL_GROWING)
11026 MauerWaechst(x, y);
11027 else if (element == EL_EXPANDABLE_WALL ||
11028 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11029 element == EL_EXPANDABLE_WALL_VERTICAL ||
11030 element == EL_EXPANDABLE_WALL_ANY ||
11031 element == EL_BD_EXPANDABLE_WALL)
11032 MauerAbleger(x, y);
11033 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11034 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11035 element == EL_EXPANDABLE_STEELWALL_ANY)
11036 MauerAblegerStahl(x, y);
11037 else if (element == EL_FLAMES)
11038 CheckForDragon(x, y);
11039 else if (element == EL_EXPLOSION)
11040 ; /* drawing of correct explosion animation is handled separately */
11041 else if (element == EL_ELEMENT_SNAPPING ||
11042 element == EL_DIAGONAL_SHRINKING ||
11043 element == EL_DIAGONAL_GROWING)
11045 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11047 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11049 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11050 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11052 #endif // ---------------------------------------------------------------------
11054 if (IS_BELT_ACTIVE(element))
11055 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11057 if (game.magic_wall_active)
11059 int jx = local_player->jx, jy = local_player->jy;
11061 /* play the element sound at the position nearest to the player */
11062 if ((element == EL_MAGIC_WALL_FULL ||
11063 element == EL_MAGIC_WALL_ACTIVE ||
11064 element == EL_MAGIC_WALL_EMPTYING ||
11065 element == EL_BD_MAGIC_WALL_FULL ||
11066 element == EL_BD_MAGIC_WALL_ACTIVE ||
11067 element == EL_BD_MAGIC_WALL_EMPTYING ||
11068 element == EL_DC_MAGIC_WALL_FULL ||
11069 element == EL_DC_MAGIC_WALL_ACTIVE ||
11070 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11071 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11080 debug_print_timestamp(0, "- time for MAIN loop: -->");
11083 #if USE_NEW_AMOEBA_CODE
11084 /* new experimental amoeba growth stuff */
11085 if (!(FrameCounter % 8))
11087 static unsigned long random = 1684108901;
11089 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11091 x = RND(lev_fieldx);
11092 y = RND(lev_fieldy);
11093 element = Feld[x][y];
11095 if (!IS_PLAYER(x,y) &&
11096 (element == EL_EMPTY ||
11097 CAN_GROW_INTO(element) ||
11098 element == EL_QUICKSAND_EMPTY ||
11099 element == EL_QUICKSAND_FAST_EMPTY ||
11100 element == EL_ACID_SPLASH_LEFT ||
11101 element == EL_ACID_SPLASH_RIGHT))
11103 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11104 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11105 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11106 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11107 Feld[x][y] = EL_AMOEBA_DROP;
11110 random = random * 129 + 1;
11116 if (game.explosions_delayed)
11119 game.explosions_delayed = FALSE;
11121 SCAN_PLAYFIELD(x, y)
11123 element = Feld[x][y];
11125 if (ExplodeField[x][y])
11126 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11127 else if (element == EL_EXPLOSION)
11128 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11130 ExplodeField[x][y] = EX_TYPE_NONE;
11133 game.explosions_delayed = TRUE;
11136 if (game.magic_wall_active)
11138 if (!(game.magic_wall_time_left % 4))
11140 int element = Feld[magic_wall_x][magic_wall_y];
11142 if (element == EL_BD_MAGIC_WALL_FULL ||
11143 element == EL_BD_MAGIC_WALL_ACTIVE ||
11144 element == EL_BD_MAGIC_WALL_EMPTYING)
11145 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11146 else if (element == EL_DC_MAGIC_WALL_FULL ||
11147 element == EL_DC_MAGIC_WALL_ACTIVE ||
11148 element == EL_DC_MAGIC_WALL_EMPTYING)
11149 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11151 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11154 if (game.magic_wall_time_left > 0)
11156 game.magic_wall_time_left--;
11157 if (!game.magic_wall_time_left)
11159 SCAN_PLAYFIELD(x, y)
11161 element = Feld[x][y];
11163 if (element == EL_MAGIC_WALL_ACTIVE ||
11164 element == EL_MAGIC_WALL_FULL)
11166 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11167 DrawLevelField(x, y);
11169 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11170 element == EL_BD_MAGIC_WALL_FULL)
11172 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11173 DrawLevelField(x, y);
11175 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11176 element == EL_DC_MAGIC_WALL_FULL)
11178 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11179 DrawLevelField(x, y);
11183 game.magic_wall_active = FALSE;
11188 if (game.light_time_left > 0)
11190 game.light_time_left--;
11192 if (game.light_time_left == 0)
11193 RedrawAllLightSwitchesAndInvisibleElements();
11196 if (game.timegate_time_left > 0)
11198 game.timegate_time_left--;
11200 if (game.timegate_time_left == 0)
11201 CloseAllOpenTimegates();
11204 if (game.lenses_time_left > 0)
11206 game.lenses_time_left--;
11208 if (game.lenses_time_left == 0)
11209 RedrawAllInvisibleElementsForLenses();
11212 if (game.magnify_time_left > 0)
11214 game.magnify_time_left--;
11216 if (game.magnify_time_left == 0)
11217 RedrawAllInvisibleElementsForMagnifier();
11220 for (i = 0; i < MAX_PLAYERS; i++)
11222 struct PlayerInfo *player = &stored_player[i];
11224 if (SHIELD_ON(player))
11226 if (player->shield_deadly_time_left)
11227 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11228 else if (player->shield_normal_time_left)
11229 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11236 PlayAllPlayersSound();
11238 if (options.debug) /* calculate frames per second */
11240 static unsigned long fps_counter = 0;
11241 static int fps_frames = 0;
11242 unsigned long fps_delay_ms = Counter() - fps_counter;
11246 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11248 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11251 fps_counter = Counter();
11254 redraw_mask |= REDRAW_FPS;
11257 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11259 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11261 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11263 local_player->show_envelope = 0;
11267 debug_print_timestamp(0, "stop main loop profiling ");
11268 printf("----------------------------------------------------------\n");
11271 /* use random number generator in every frame to make it less predictable */
11272 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11276 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11278 int min_x = x, min_y = y, max_x = x, max_y = y;
11281 for (i = 0; i < MAX_PLAYERS; i++)
11283 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11285 if (!stored_player[i].active || &stored_player[i] == player)
11288 min_x = MIN(min_x, jx);
11289 min_y = MIN(min_y, jy);
11290 max_x = MAX(max_x, jx);
11291 max_y = MAX(max_y, jy);
11294 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11297 static boolean AllPlayersInVisibleScreen()
11301 for (i = 0; i < MAX_PLAYERS; i++)
11303 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11305 if (!stored_player[i].active)
11308 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11315 void ScrollLevel(int dx, int dy)
11318 static Bitmap *bitmap_db_field2 = NULL;
11319 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11326 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11327 /* only horizontal XOR vertical scroll direction allowed */
11328 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11333 if (bitmap_db_field2 == NULL)
11334 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11336 /* needed when blitting directly to same bitmap -- should not be needed with
11337 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11338 BlitBitmap(drawto_field, bitmap_db_field2,
11339 FX + TILEX * (dx == -1) - softscroll_offset,
11340 FY + TILEY * (dy == -1) - softscroll_offset,
11341 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11342 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11343 FX + TILEX * (dx == 1) - softscroll_offset,
11344 FY + TILEY * (dy == 1) - softscroll_offset);
11345 BlitBitmap(bitmap_db_field2, drawto_field,
11346 FX + TILEX * (dx == 1) - softscroll_offset,
11347 FY + TILEY * (dy == 1) - softscroll_offset,
11348 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11349 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11350 FX + TILEX * (dx == 1) - softscroll_offset,
11351 FY + TILEY * (dy == 1) - softscroll_offset);
11356 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11357 int xsize = (BX2 - BX1 + 1);
11358 int ysize = (BY2 - BY1 + 1);
11359 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11360 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11361 int step = (start < end ? +1 : -1);
11363 for (i = start; i != end; i += step)
11365 BlitBitmap(drawto_field, drawto_field,
11366 FX + TILEX * (dx != 0 ? i + step : 0),
11367 FY + TILEY * (dy != 0 ? i + step : 0),
11368 TILEX * (dx != 0 ? 1 : xsize),
11369 TILEY * (dy != 0 ? 1 : ysize),
11370 FX + TILEX * (dx != 0 ? i : 0),
11371 FY + TILEY * (dy != 0 ? i : 0));
11376 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11378 BlitBitmap(drawto_field, drawto_field,
11379 FX + TILEX * (dx == -1) - softscroll_offset,
11380 FY + TILEY * (dy == -1) - softscroll_offset,
11381 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11382 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11383 FX + TILEX * (dx == 1) - softscroll_offset,
11384 FY + TILEY * (dy == 1) - softscroll_offset);
11390 x = (dx == 1 ? BX1 : BX2);
11391 for (y = BY1; y <= BY2; y++)
11392 DrawScreenField(x, y);
11397 y = (dy == 1 ? BY1 : BY2);
11398 for (x = BX1; x <= BX2; x++)
11399 DrawScreenField(x, y);
11402 redraw_mask |= REDRAW_FIELD;
11405 static boolean canFallDown(struct PlayerInfo *player)
11407 int jx = player->jx, jy = player->jy;
11409 return (IN_LEV_FIELD(jx, jy + 1) &&
11410 (IS_FREE(jx, jy + 1) ||
11411 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11412 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11413 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11416 static boolean canPassField(int x, int y, int move_dir)
11418 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11419 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11420 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11421 int nextx = x + dx;
11422 int nexty = y + dy;
11423 int element = Feld[x][y];
11425 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11426 !CAN_MOVE(element) &&
11427 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11428 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11429 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11432 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11434 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11435 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11436 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11440 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11441 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11442 (IS_DIGGABLE(Feld[newx][newy]) ||
11443 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11444 canPassField(newx, newy, move_dir)));
11447 static void CheckGravityMovement(struct PlayerInfo *player)
11449 #if USE_PLAYER_GRAVITY
11450 if (player->gravity && !player->programmed_action)
11452 if (game.gravity && !player->programmed_action)
11455 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11456 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11457 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11458 int jx = player->jx, jy = player->jy;
11459 boolean player_is_moving_to_valid_field =
11460 (!player_is_snapping &&
11461 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11462 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11463 boolean player_can_fall_down = canFallDown(player);
11465 if (player_can_fall_down &&
11466 !player_is_moving_to_valid_field)
11467 player->programmed_action = MV_DOWN;
11471 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11473 return CheckGravityMovement(player);
11475 #if USE_PLAYER_GRAVITY
11476 if (player->gravity && !player->programmed_action)
11478 if (game.gravity && !player->programmed_action)
11481 int jx = player->jx, jy = player->jy;
11482 boolean field_under_player_is_free =
11483 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11484 boolean player_is_standing_on_valid_field =
11485 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11486 (IS_WALKABLE(Feld[jx][jy]) &&
11487 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11489 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11490 player->programmed_action = MV_DOWN;
11495 MovePlayerOneStep()
11496 -----------------------------------------------------------------------------
11497 dx, dy: direction (non-diagonal) to try to move the player to
11498 real_dx, real_dy: direction as read from input device (can be diagonal)
11501 boolean MovePlayerOneStep(struct PlayerInfo *player,
11502 int dx, int dy, int real_dx, int real_dy)
11504 int jx = player->jx, jy = player->jy;
11505 int new_jx = jx + dx, new_jy = jy + dy;
11506 #if !USE_FIXED_DONT_RUN_INTO
11510 boolean player_can_move = !player->cannot_move;
11512 if (!player->active || (!dx && !dy))
11513 return MP_NO_ACTION;
11515 player->MovDir = (dx < 0 ? MV_LEFT :
11516 dx > 0 ? MV_RIGHT :
11518 dy > 0 ? MV_DOWN : MV_NONE);
11520 if (!IN_LEV_FIELD(new_jx, new_jy))
11521 return MP_NO_ACTION;
11523 if (!player_can_move)
11525 if (player->MovPos == 0)
11527 player->is_moving = FALSE;
11528 player->is_digging = FALSE;
11529 player->is_collecting = FALSE;
11530 player->is_snapping = FALSE;
11531 player->is_pushing = FALSE;
11536 if (!options.network && game.centered_player_nr == -1 &&
11537 !AllPlayersInSight(player, new_jx, new_jy))
11538 return MP_NO_ACTION;
11540 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11541 return MP_NO_ACTION;
11544 #if !USE_FIXED_DONT_RUN_INTO
11545 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11547 /* (moved to DigField()) */
11548 if (player_can_move && DONT_RUN_INTO(element))
11550 if (element == EL_ACID && dx == 0 && dy == 1)
11552 SplashAcid(new_jx, new_jy);
11553 Feld[jx][jy] = EL_PLAYER_1;
11554 InitMovingField(jx, jy, MV_DOWN);
11555 Store[jx][jy] = EL_ACID;
11556 ContinueMoving(jx, jy);
11557 BuryPlayer(player);
11560 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11566 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11567 if (can_move != MP_MOVING)
11570 /* check if DigField() has caused relocation of the player */
11571 if (player->jx != jx || player->jy != jy)
11572 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11574 StorePlayer[jx][jy] = 0;
11575 player->last_jx = jx;
11576 player->last_jy = jy;
11577 player->jx = new_jx;
11578 player->jy = new_jy;
11579 StorePlayer[new_jx][new_jy] = player->element_nr;
11581 if (player->move_delay_value_next != -1)
11583 player->move_delay_value = player->move_delay_value_next;
11584 player->move_delay_value_next = -1;
11588 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11590 player->step_counter++;
11592 PlayerVisit[jx][jy] = FrameCounter;
11594 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11595 player->is_moving = TRUE;
11599 /* should better be called in MovePlayer(), but this breaks some tapes */
11600 ScrollPlayer(player, SCROLL_INIT);
11606 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11608 int jx = player->jx, jy = player->jy;
11609 int old_jx = jx, old_jy = jy;
11610 int moved = MP_NO_ACTION;
11612 if (!player->active)
11617 if (player->MovPos == 0)
11619 player->is_moving = FALSE;
11620 player->is_digging = FALSE;
11621 player->is_collecting = FALSE;
11622 player->is_snapping = FALSE;
11623 player->is_pushing = FALSE;
11629 if (player->move_delay > 0)
11632 player->move_delay = -1; /* set to "uninitialized" value */
11634 /* store if player is automatically moved to next field */
11635 player->is_auto_moving = (player->programmed_action != MV_NONE);
11637 /* remove the last programmed player action */
11638 player->programmed_action = 0;
11640 if (player->MovPos)
11642 /* should only happen if pre-1.2 tape recordings are played */
11643 /* this is only for backward compatibility */
11645 int original_move_delay_value = player->move_delay_value;
11648 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11652 /* scroll remaining steps with finest movement resolution */
11653 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11655 while (player->MovPos)
11657 ScrollPlayer(player, SCROLL_GO_ON);
11658 ScrollScreen(NULL, SCROLL_GO_ON);
11660 AdvanceFrameAndPlayerCounters(player->index_nr);
11666 player->move_delay_value = original_move_delay_value;
11669 player->is_active = FALSE;
11671 if (player->last_move_dir & MV_HORIZONTAL)
11673 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11674 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11678 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11679 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11682 #if USE_FIXED_BORDER_RUNNING_GFX
11683 if (!moved && !player->is_active)
11685 player->is_moving = FALSE;
11686 player->is_digging = FALSE;
11687 player->is_collecting = FALSE;
11688 player->is_snapping = FALSE;
11689 player->is_pushing = FALSE;
11697 if (moved & MP_MOVING && !ScreenMovPos &&
11698 (player->index_nr == game.centered_player_nr ||
11699 game.centered_player_nr == -1))
11701 if (moved & MP_MOVING && !ScreenMovPos &&
11702 (player == local_player || !options.network))
11705 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11706 int offset = (setup.scroll_delay ? 3 : 0);
11708 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11710 /* actual player has left the screen -- scroll in that direction */
11711 if (jx != old_jx) /* player has moved horizontally */
11712 scroll_x += (jx - old_jx);
11713 else /* player has moved vertically */
11714 scroll_y += (jy - old_jy);
11718 if (jx != old_jx) /* player has moved horizontally */
11720 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
11721 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11722 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11724 /* don't scroll over playfield boundaries */
11725 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11726 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11728 /* don't scroll more than one field at a time */
11729 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11731 /* don't scroll against the player's moving direction */
11732 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
11733 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11734 scroll_x = old_scroll_x;
11736 else /* player has moved vertically */
11738 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
11739 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11740 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11742 /* don't scroll over playfield boundaries */
11743 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11744 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11746 /* don't scroll more than one field at a time */
11747 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11749 /* don't scroll against the player's moving direction */
11750 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
11751 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11752 scroll_y = old_scroll_y;
11756 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11759 if (!options.network && game.centered_player_nr == -1 &&
11760 !AllPlayersInVisibleScreen())
11762 scroll_x = old_scroll_x;
11763 scroll_y = old_scroll_y;
11767 if (!options.network && !AllPlayersInVisibleScreen())
11769 scroll_x = old_scroll_x;
11770 scroll_y = old_scroll_y;
11775 ScrollScreen(player, SCROLL_INIT);
11776 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11781 player->StepFrame = 0;
11783 if (moved & MP_MOVING)
11785 if (old_jx != jx && old_jy == jy)
11786 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11787 else if (old_jx == jx && old_jy != jy)
11788 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11790 DrawLevelField(jx, jy); /* for "crumbled sand" */
11792 player->last_move_dir = player->MovDir;
11793 player->is_moving = TRUE;
11794 player->is_snapping = FALSE;
11795 player->is_switching = FALSE;
11796 player->is_dropping = FALSE;
11797 player->is_dropping_pressed = FALSE;
11798 player->drop_pressed_delay = 0;
11801 /* should better be called here than above, but this breaks some tapes */
11802 ScrollPlayer(player, SCROLL_INIT);
11807 CheckGravityMovementWhenNotMoving(player);
11809 player->is_moving = FALSE;
11811 /* at this point, the player is allowed to move, but cannot move right now
11812 (e.g. because of something blocking the way) -- ensure that the player
11813 is also allowed to move in the next frame (in old versions before 3.1.1,
11814 the player was forced to wait again for eight frames before next try) */
11816 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11817 player->move_delay = 0; /* allow direct movement in the next frame */
11820 if (player->move_delay == -1) /* not yet initialized by DigField() */
11821 player->move_delay = player->move_delay_value;
11823 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11825 TestIfPlayerTouchesBadThing(jx, jy);
11826 TestIfPlayerTouchesCustomElement(jx, jy);
11829 if (!player->active)
11830 RemovePlayer(player);
11835 void ScrollPlayer(struct PlayerInfo *player, int mode)
11837 int jx = player->jx, jy = player->jy;
11838 int last_jx = player->last_jx, last_jy = player->last_jy;
11839 int move_stepsize = TILEX / player->move_delay_value;
11841 #if USE_NEW_PLAYER_SPEED
11842 if (!player->active)
11845 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11848 if (!player->active || player->MovPos == 0)
11852 if (mode == SCROLL_INIT)
11854 player->actual_frame_counter = FrameCounter;
11855 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11857 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11858 Feld[last_jx][last_jy] == EL_EMPTY)
11860 int last_field_block_delay = 0; /* start with no blocking at all */
11861 int block_delay_adjustment = player->block_delay_adjustment;
11863 /* if player blocks last field, add delay for exactly one move */
11864 if (player->block_last_field)
11866 last_field_block_delay += player->move_delay_value;
11868 /* when blocking enabled, prevent moving up despite gravity */
11869 #if USE_PLAYER_GRAVITY
11870 if (player->gravity && player->MovDir == MV_UP)
11871 block_delay_adjustment = -1;
11873 if (game.gravity && player->MovDir == MV_UP)
11874 block_delay_adjustment = -1;
11878 /* add block delay adjustment (also possible when not blocking) */
11879 last_field_block_delay += block_delay_adjustment;
11881 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11882 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11885 #if USE_NEW_PLAYER_SPEED
11886 if (player->MovPos != 0) /* player has not yet reached destination */
11892 else if (!FrameReached(&player->actual_frame_counter, 1))
11895 #if USE_NEW_PLAYER_SPEED
11896 if (player->MovPos != 0)
11898 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11899 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11901 /* before DrawPlayer() to draw correct player graphic for this case */
11902 if (player->MovPos == 0)
11903 CheckGravityMovement(player);
11906 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11907 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11909 /* before DrawPlayer() to draw correct player graphic for this case */
11910 if (player->MovPos == 0)
11911 CheckGravityMovement(player);
11914 if (player->MovPos == 0) /* player reached destination field */
11916 if (player->move_delay_reset_counter > 0)
11918 player->move_delay_reset_counter--;
11920 if (player->move_delay_reset_counter == 0)
11922 /* continue with normal speed after quickly moving through gate */
11923 HALVE_PLAYER_SPEED(player);
11925 /* be able to make the next move without delay */
11926 player->move_delay = 0;
11930 player->last_jx = jx;
11931 player->last_jy = jy;
11933 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11934 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11935 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11936 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11937 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11938 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11940 DrawPlayer(player); /* needed here only to cleanup last field */
11941 RemovePlayer(player);
11943 if (local_player->friends_still_needed == 0 ||
11944 IS_SP_ELEMENT(Feld[jx][jy]))
11945 PlayerWins(player);
11948 /* this breaks one level: "machine", level 000 */
11950 int move_direction = player->MovDir;
11951 int enter_side = MV_DIR_OPPOSITE(move_direction);
11952 int leave_side = move_direction;
11953 int old_jx = last_jx;
11954 int old_jy = last_jy;
11955 int old_element = Feld[old_jx][old_jy];
11956 int new_element = Feld[jx][jy];
11958 if (IS_CUSTOM_ELEMENT(old_element))
11959 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11961 player->index_bit, leave_side);
11963 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11964 CE_PLAYER_LEAVES_X,
11965 player->index_bit, leave_side);
11967 if (IS_CUSTOM_ELEMENT(new_element))
11968 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11969 player->index_bit, enter_side);
11971 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11972 CE_PLAYER_ENTERS_X,
11973 player->index_bit, enter_side);
11975 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11976 CE_MOVE_OF_X, move_direction);
11979 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11981 TestIfPlayerTouchesBadThing(jx, jy);
11982 TestIfPlayerTouchesCustomElement(jx, jy);
11984 /* needed because pushed element has not yet reached its destination,
11985 so it would trigger a change event at its previous field location */
11986 if (!player->is_pushing)
11987 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11989 if (!player->active)
11990 RemovePlayer(player);
11993 if (!local_player->LevelSolved && level.use_step_counter)
12003 if (TimeLeft <= 10 && setup.time_limit)
12004 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12006 DrawGameValue_Time(TimeLeft);
12008 if (!TimeLeft && setup.time_limit)
12009 for (i = 0; i < MAX_PLAYERS; i++)
12010 KillPlayer(&stored_player[i]);
12012 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12013 DrawGameValue_Time(TimePlayed);
12016 if (tape.single_step && tape.recording && !tape.pausing &&
12017 !player->programmed_action)
12018 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12022 void ScrollScreen(struct PlayerInfo *player, int mode)
12024 static unsigned long screen_frame_counter = 0;
12026 if (mode == SCROLL_INIT)
12028 /* set scrolling step size according to actual player's moving speed */
12029 ScrollStepSize = TILEX / player->move_delay_value;
12031 screen_frame_counter = FrameCounter;
12032 ScreenMovDir = player->MovDir;
12033 ScreenMovPos = player->MovPos;
12034 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12037 else if (!FrameReached(&screen_frame_counter, 1))
12042 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12043 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12044 redraw_mask |= REDRAW_FIELD;
12047 ScreenMovDir = MV_NONE;
12050 void TestIfPlayerTouchesCustomElement(int x, int y)
12052 static int xy[4][2] =
12059 static int trigger_sides[4][2] =
12061 /* center side border side */
12062 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12063 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12064 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12065 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12067 static int touch_dir[4] =
12069 MV_LEFT | MV_RIGHT,
12074 int center_element = Feld[x][y]; /* should always be non-moving! */
12077 for (i = 0; i < NUM_DIRECTIONS; i++)
12079 int xx = x + xy[i][0];
12080 int yy = y + xy[i][1];
12081 int center_side = trigger_sides[i][0];
12082 int border_side = trigger_sides[i][1];
12083 int border_element;
12085 if (!IN_LEV_FIELD(xx, yy))
12088 if (IS_PLAYER(x, y))
12090 struct PlayerInfo *player = PLAYERINFO(x, y);
12092 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12093 border_element = Feld[xx][yy]; /* may be moving! */
12094 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12095 border_element = Feld[xx][yy];
12096 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12097 border_element = MovingOrBlocked2Element(xx, yy);
12099 continue; /* center and border element do not touch */
12101 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12102 player->index_bit, border_side);
12103 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12104 CE_PLAYER_TOUCHES_X,
12105 player->index_bit, border_side);
12107 else if (IS_PLAYER(xx, yy))
12109 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12111 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12113 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12114 continue; /* center and border element do not touch */
12117 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12118 player->index_bit, center_side);
12119 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12120 CE_PLAYER_TOUCHES_X,
12121 player->index_bit, center_side);
12127 #if USE_ELEMENT_TOUCHING_BUGFIX
12129 void TestIfElementTouchesCustomElement(int x, int y)
12131 static int xy[4][2] =
12138 static int trigger_sides[4][2] =
12140 /* center side border side */
12141 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12142 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12143 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12144 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12146 static int touch_dir[4] =
12148 MV_LEFT | MV_RIGHT,
12153 boolean change_center_element = FALSE;
12154 int center_element = Feld[x][y]; /* should always be non-moving! */
12155 int border_element_old[NUM_DIRECTIONS];
12158 for (i = 0; i < NUM_DIRECTIONS; i++)
12160 int xx = x + xy[i][0];
12161 int yy = y + xy[i][1];
12162 int border_element;
12164 border_element_old[i] = -1;
12166 if (!IN_LEV_FIELD(xx, yy))
12169 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12170 border_element = Feld[xx][yy]; /* may be moving! */
12171 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12172 border_element = Feld[xx][yy];
12173 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12174 border_element = MovingOrBlocked2Element(xx, yy);
12176 continue; /* center and border element do not touch */
12178 border_element_old[i] = border_element;
12181 for (i = 0; i < NUM_DIRECTIONS; i++)
12183 int xx = x + xy[i][0];
12184 int yy = y + xy[i][1];
12185 int center_side = trigger_sides[i][0];
12186 int border_element = border_element_old[i];
12188 if (border_element == -1)
12191 /* check for change of border element */
12192 CheckElementChangeBySide(xx, yy, border_element, center_element,
12193 CE_TOUCHING_X, center_side);
12196 for (i = 0; i < NUM_DIRECTIONS; i++)
12198 int border_side = trigger_sides[i][1];
12199 int border_element = border_element_old[i];
12201 if (border_element == -1)
12204 /* check for change of center element (but change it only once) */
12205 if (!change_center_element)
12206 change_center_element =
12207 CheckElementChangeBySide(x, y, center_element, border_element,
12208 CE_TOUCHING_X, border_side);
12214 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12216 static int xy[4][2] =
12223 static int trigger_sides[4][2] =
12225 /* center side border side */
12226 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12227 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12228 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12229 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12231 static int touch_dir[4] =
12233 MV_LEFT | MV_RIGHT,
12238 boolean change_center_element = FALSE;
12239 int center_element = Feld[x][y]; /* should always be non-moving! */
12242 for (i = 0; i < NUM_DIRECTIONS; i++)
12244 int xx = x + xy[i][0];
12245 int yy = y + xy[i][1];
12246 int center_side = trigger_sides[i][0];
12247 int border_side = trigger_sides[i][1];
12248 int border_element;
12250 if (!IN_LEV_FIELD(xx, yy))
12253 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12254 border_element = Feld[xx][yy]; /* may be moving! */
12255 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12256 border_element = Feld[xx][yy];
12257 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12258 border_element = MovingOrBlocked2Element(xx, yy);
12260 continue; /* center and border element do not touch */
12262 /* check for change of center element (but change it only once) */
12263 if (!change_center_element)
12264 change_center_element =
12265 CheckElementChangeBySide(x, y, center_element, border_element,
12266 CE_TOUCHING_X, border_side);
12268 /* check for change of border element */
12269 CheckElementChangeBySide(xx, yy, border_element, center_element,
12270 CE_TOUCHING_X, center_side);
12276 void TestIfElementHitsCustomElement(int x, int y, int direction)
12278 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12279 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12280 int hitx = x + dx, hity = y + dy;
12281 int hitting_element = Feld[x][y];
12282 int touched_element;
12284 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12287 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12288 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12290 if (IN_LEV_FIELD(hitx, hity))
12292 int opposite_direction = MV_DIR_OPPOSITE(direction);
12293 int hitting_side = direction;
12294 int touched_side = opposite_direction;
12295 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12296 MovDir[hitx][hity] != direction ||
12297 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12303 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12304 CE_HITTING_X, touched_side);
12306 CheckElementChangeBySide(hitx, hity, touched_element,
12307 hitting_element, CE_HIT_BY_X, hitting_side);
12309 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12310 CE_HIT_BY_SOMETHING, opposite_direction);
12314 /* "hitting something" is also true when hitting the playfield border */
12315 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12316 CE_HITTING_SOMETHING, direction);
12320 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12322 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12323 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12324 int hitx = x + dx, hity = y + dy;
12325 int hitting_element = Feld[x][y];
12326 int touched_element;
12328 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12329 !IS_FREE(hitx, hity) &&
12330 (!IS_MOVING(hitx, hity) ||
12331 MovDir[hitx][hity] != direction ||
12332 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12335 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12339 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12343 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12344 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12346 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12347 EP_CAN_SMASH_EVERYTHING, direction);
12349 if (IN_LEV_FIELD(hitx, hity))
12351 int opposite_direction = MV_DIR_OPPOSITE(direction);
12352 int hitting_side = direction;
12353 int touched_side = opposite_direction;
12355 int touched_element = MovingOrBlocked2Element(hitx, hity);
12358 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12359 MovDir[hitx][hity] != direction ||
12360 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12369 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12370 CE_SMASHED_BY_SOMETHING, opposite_direction);
12372 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12373 CE_OTHER_IS_SMASHING, touched_side);
12375 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12376 CE_OTHER_GETS_SMASHED, hitting_side);
12382 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12384 int i, kill_x = -1, kill_y = -1;
12386 int bad_element = -1;
12387 static int test_xy[4][2] =
12394 static int test_dir[4] =
12402 for (i = 0; i < NUM_DIRECTIONS; i++)
12404 int test_x, test_y, test_move_dir, test_element;
12406 test_x = good_x + test_xy[i][0];
12407 test_y = good_y + test_xy[i][1];
12409 if (!IN_LEV_FIELD(test_x, test_y))
12413 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12415 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12417 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12418 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12420 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12421 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12425 bad_element = test_element;
12431 if (kill_x != -1 || kill_y != -1)
12433 if (IS_PLAYER(good_x, good_y))
12435 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12437 if (player->shield_deadly_time_left > 0 &&
12438 !IS_INDESTRUCTIBLE(bad_element))
12439 Bang(kill_x, kill_y);
12440 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12441 KillPlayer(player);
12444 Bang(good_x, good_y);
12448 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12450 int i, kill_x = -1, kill_y = -1;
12451 int bad_element = Feld[bad_x][bad_y];
12452 static int test_xy[4][2] =
12459 static int touch_dir[4] =
12461 MV_LEFT | MV_RIGHT,
12466 static int test_dir[4] =
12474 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12477 for (i = 0; i < NUM_DIRECTIONS; i++)
12479 int test_x, test_y, test_move_dir, test_element;
12481 test_x = bad_x + test_xy[i][0];
12482 test_y = bad_y + test_xy[i][1];
12483 if (!IN_LEV_FIELD(test_x, test_y))
12487 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12489 test_element = Feld[test_x][test_y];
12491 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12492 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12494 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12495 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12497 /* good thing is player or penguin that does not move away */
12498 if (IS_PLAYER(test_x, test_y))
12500 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12502 if (bad_element == EL_ROBOT && player->is_moving)
12503 continue; /* robot does not kill player if he is moving */
12505 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12507 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12508 continue; /* center and border element do not touch */
12515 else if (test_element == EL_PENGUIN)
12524 if (kill_x != -1 || kill_y != -1)
12526 if (IS_PLAYER(kill_x, kill_y))
12528 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12530 if (player->shield_deadly_time_left > 0 &&
12531 !IS_INDESTRUCTIBLE(bad_element))
12532 Bang(bad_x, bad_y);
12533 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12534 KillPlayer(player);
12537 Bang(kill_x, kill_y);
12541 void TestIfPlayerTouchesBadThing(int x, int y)
12543 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12546 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12548 TestIfGoodThingHitsBadThing(x, y, move_dir);
12551 void TestIfBadThingTouchesPlayer(int x, int y)
12553 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12556 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12558 TestIfBadThingHitsGoodThing(x, y, move_dir);
12561 void TestIfFriendTouchesBadThing(int x, int y)
12563 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12566 void TestIfBadThingTouchesFriend(int x, int y)
12568 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12571 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12573 int i, kill_x = bad_x, kill_y = bad_y;
12574 static int xy[4][2] =
12582 for (i = 0; i < NUM_DIRECTIONS; i++)
12586 x = bad_x + xy[i][0];
12587 y = bad_y + xy[i][1];
12588 if (!IN_LEV_FIELD(x, y))
12591 element = Feld[x][y];
12592 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12593 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12601 if (kill_x != bad_x || kill_y != bad_y)
12602 Bang(bad_x, bad_y);
12605 void KillPlayer(struct PlayerInfo *player)
12607 int jx = player->jx, jy = player->jy;
12609 if (!player->active)
12612 /* the following code was introduced to prevent an infinite loop when calling
12614 -> CheckTriggeredElementChangeExt()
12615 -> ExecuteCustomElementAction()
12617 -> (infinitely repeating the above sequence of function calls)
12618 which occurs when killing the player while having a CE with the setting
12619 "kill player X when explosion of <player X>"; the solution using a new
12620 field "player->killed" was chosen for backwards compatibility, although
12621 clever use of the fields "player->active" etc. would probably also work */
12623 if (player->killed)
12627 player->killed = TRUE;
12629 /* remove accessible field at the player's position */
12630 Feld[jx][jy] = EL_EMPTY;
12632 /* deactivate shield (else Bang()/Explode() would not work right) */
12633 player->shield_normal_time_left = 0;
12634 player->shield_deadly_time_left = 0;
12637 BuryPlayer(player);
12640 static void KillPlayerUnlessEnemyProtected(int x, int y)
12642 if (!PLAYER_ENEMY_PROTECTED(x, y))
12643 KillPlayer(PLAYERINFO(x, y));
12646 static void KillPlayerUnlessExplosionProtected(int x, int y)
12648 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12649 KillPlayer(PLAYERINFO(x, y));
12652 void BuryPlayer(struct PlayerInfo *player)
12654 int jx = player->jx, jy = player->jy;
12656 if (!player->active)
12659 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12660 PlayLevelSound(jx, jy, SND_GAME_LOSING);
12662 player->GameOver = TRUE;
12663 RemovePlayer(player);
12666 void RemovePlayer(struct PlayerInfo *player)
12668 int jx = player->jx, jy = player->jy;
12669 int i, found = FALSE;
12671 player->present = FALSE;
12672 player->active = FALSE;
12674 if (!ExplodeField[jx][jy])
12675 StorePlayer[jx][jy] = 0;
12677 if (player->is_moving)
12678 DrawLevelField(player->last_jx, player->last_jy);
12680 for (i = 0; i < MAX_PLAYERS; i++)
12681 if (stored_player[i].active)
12685 AllPlayersGone = TRUE;
12691 #if USE_NEW_SNAP_DELAY
12692 static void setFieldForSnapping(int x, int y, int element, int direction)
12694 struct ElementInfo *ei = &element_info[element];
12695 int direction_bit = MV_DIR_TO_BIT(direction);
12696 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12697 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12698 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12700 Feld[x][y] = EL_ELEMENT_SNAPPING;
12701 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12703 ResetGfxAnimation(x, y);
12705 GfxElement[x][y] = element;
12706 GfxAction[x][y] = action;
12707 GfxDir[x][y] = direction;
12708 GfxFrame[x][y] = -1;
12713 =============================================================================
12714 checkDiagonalPushing()
12715 -----------------------------------------------------------------------------
12716 check if diagonal input device direction results in pushing of object
12717 (by checking if the alternative direction is walkable, diggable, ...)
12718 =============================================================================
12721 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12722 int x, int y, int real_dx, int real_dy)
12724 int jx, jy, dx, dy, xx, yy;
12726 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
12729 /* diagonal direction: check alternative direction */
12734 xx = jx + (dx == 0 ? real_dx : 0);
12735 yy = jy + (dy == 0 ? real_dy : 0);
12737 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12741 =============================================================================
12743 -----------------------------------------------------------------------------
12744 x, y: field next to player (non-diagonal) to try to dig to
12745 real_dx, real_dy: direction as read from input device (can be diagonal)
12746 =============================================================================
12749 int DigField(struct PlayerInfo *player,
12750 int oldx, int oldy, int x, int y,
12751 int real_dx, int real_dy, int mode)
12753 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12754 boolean player_was_pushing = player->is_pushing;
12755 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12756 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12757 int jx = oldx, jy = oldy;
12758 int dx = x - jx, dy = y - jy;
12759 int nextx = x + dx, nexty = y + dy;
12760 int move_direction = (dx == -1 ? MV_LEFT :
12761 dx == +1 ? MV_RIGHT :
12763 dy == +1 ? MV_DOWN : MV_NONE);
12764 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12765 int dig_side = MV_DIR_OPPOSITE(move_direction);
12766 int old_element = Feld[jx][jy];
12767 #if USE_FIXED_DONT_RUN_INTO
12768 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12774 if (is_player) /* function can also be called by EL_PENGUIN */
12776 if (player->MovPos == 0)
12778 player->is_digging = FALSE;
12779 player->is_collecting = FALSE;
12782 if (player->MovPos == 0) /* last pushing move finished */
12783 player->is_pushing = FALSE;
12785 if (mode == DF_NO_PUSH) /* player just stopped pushing */
12787 player->is_switching = FALSE;
12788 player->push_delay = -1;
12790 return MP_NO_ACTION;
12794 #if !USE_FIXED_DONT_RUN_INTO
12795 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12796 return MP_NO_ACTION;
12799 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12800 old_element = Back[jx][jy];
12802 /* in case of element dropped at player position, check background */
12803 else if (Back[jx][jy] != EL_EMPTY &&
12804 game.engine_version >= VERSION_IDENT(2,2,0,0))
12805 old_element = Back[jx][jy];
12807 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12808 return MP_NO_ACTION; /* field has no opening in this direction */
12810 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12811 return MP_NO_ACTION; /* field has no opening in this direction */
12813 #if USE_FIXED_DONT_RUN_INTO
12814 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12818 Feld[jx][jy] = player->artwork_element;
12819 InitMovingField(jx, jy, MV_DOWN);
12820 Store[jx][jy] = EL_ACID;
12821 ContinueMoving(jx, jy);
12822 BuryPlayer(player);
12824 return MP_DONT_RUN_INTO;
12828 #if USE_FIXED_DONT_RUN_INTO
12829 if (player_can_move && DONT_RUN_INTO(element))
12831 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12833 return MP_DONT_RUN_INTO;
12837 #if USE_FIXED_DONT_RUN_INTO
12838 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12839 return MP_NO_ACTION;
12842 #if !USE_FIXED_DONT_RUN_INTO
12843 element = Feld[x][y];
12846 collect_count = element_info[element].collect_count_initial;
12848 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12849 return MP_NO_ACTION;
12851 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12852 player_can_move = player_can_move_or_snap;
12854 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12855 game.engine_version >= VERSION_IDENT(2,2,0,0))
12857 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12858 player->index_bit, dig_side);
12859 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12860 player->index_bit, dig_side);
12862 if (element == EL_DC_LANDMINE)
12865 if (Feld[x][y] != element) /* field changed by snapping */
12868 return MP_NO_ACTION;
12871 #if USE_PLAYER_GRAVITY
12872 if (player->gravity && is_player && !player->is_auto_moving &&
12873 canFallDown(player) && move_direction != MV_DOWN &&
12874 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12875 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12877 if (game.gravity && is_player && !player->is_auto_moving &&
12878 canFallDown(player) && move_direction != MV_DOWN &&
12879 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12880 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12883 if (player_can_move &&
12884 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12886 int sound_element = SND_ELEMENT(element);
12887 int sound_action = ACTION_WALKING;
12889 if (IS_RND_GATE(element))
12891 if (!player->key[RND_GATE_NR(element)])
12892 return MP_NO_ACTION;
12894 else if (IS_RND_GATE_GRAY(element))
12896 if (!player->key[RND_GATE_GRAY_NR(element)])
12897 return MP_NO_ACTION;
12899 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12901 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12902 return MP_NO_ACTION;
12904 else if (element == EL_EXIT_OPEN ||
12905 element == EL_EM_EXIT_OPEN ||
12906 element == EL_STEEL_EXIT_OPEN ||
12907 element == EL_EM_STEEL_EXIT_OPEN ||
12908 element == EL_SP_EXIT_OPEN ||
12909 element == EL_SP_EXIT_OPENING)
12911 sound_action = ACTION_PASSING; /* player is passing exit */
12913 else if (element == EL_EMPTY)
12915 sound_action = ACTION_MOVING; /* nothing to walk on */
12918 /* play sound from background or player, whatever is available */
12919 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12920 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12922 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12924 else if (player_can_move &&
12925 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12927 if (!ACCESS_FROM(element, opposite_direction))
12928 return MP_NO_ACTION; /* field not accessible from this direction */
12930 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12931 return MP_NO_ACTION;
12933 if (IS_EM_GATE(element))
12935 if (!player->key[EM_GATE_NR(element)])
12936 return MP_NO_ACTION;
12938 else if (IS_EM_GATE_GRAY(element))
12940 if (!player->key[EM_GATE_GRAY_NR(element)])
12941 return MP_NO_ACTION;
12943 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12945 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12946 return MP_NO_ACTION;
12948 else if (IS_EMC_GATE(element))
12950 if (!player->key[EMC_GATE_NR(element)])
12951 return MP_NO_ACTION;
12953 else if (IS_EMC_GATE_GRAY(element))
12955 if (!player->key[EMC_GATE_GRAY_NR(element)])
12956 return MP_NO_ACTION;
12958 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12960 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12961 return MP_NO_ACTION;
12963 else if (element == EL_DC_GATE_WHITE ||
12964 element == EL_DC_GATE_WHITE_GRAY ||
12965 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12967 if (player->num_white_keys == 0)
12968 return MP_NO_ACTION;
12970 player->num_white_keys--;
12972 else if (IS_SP_PORT(element))
12974 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12975 element == EL_SP_GRAVITY_PORT_RIGHT ||
12976 element == EL_SP_GRAVITY_PORT_UP ||
12977 element == EL_SP_GRAVITY_PORT_DOWN)
12978 #if USE_PLAYER_GRAVITY
12979 player->gravity = !player->gravity;
12981 game.gravity = !game.gravity;
12983 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12984 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12985 element == EL_SP_GRAVITY_ON_PORT_UP ||
12986 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12987 #if USE_PLAYER_GRAVITY
12988 player->gravity = TRUE;
12990 game.gravity = TRUE;
12992 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12993 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12994 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12995 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12996 #if USE_PLAYER_GRAVITY
12997 player->gravity = FALSE;
12999 game.gravity = FALSE;
13003 /* automatically move to the next field with double speed */
13004 player->programmed_action = move_direction;
13006 if (player->move_delay_reset_counter == 0)
13008 player->move_delay_reset_counter = 2; /* two double speed steps */
13010 DOUBLE_PLAYER_SPEED(player);
13013 PlayLevelSoundAction(x, y, ACTION_PASSING);
13015 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13019 if (mode != DF_SNAP)
13021 GfxElement[x][y] = GFX_ELEMENT(element);
13022 player->is_digging = TRUE;
13025 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13027 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13028 player->index_bit, dig_side);
13030 if (mode == DF_SNAP)
13032 #if USE_NEW_SNAP_DELAY
13033 if (level.block_snap_field)
13034 setFieldForSnapping(x, y, element, move_direction);
13036 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13038 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13041 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13042 player->index_bit, dig_side);
13045 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13049 if (is_player && mode != DF_SNAP)
13051 GfxElement[x][y] = element;
13052 player->is_collecting = TRUE;
13055 if (element == EL_SPEED_PILL)
13057 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13059 else if (element == EL_EXTRA_TIME && level.time > 0)
13061 TimeLeft += level.extra_time;
13062 DrawGameValue_Time(TimeLeft);
13064 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13066 player->shield_normal_time_left += level.shield_normal_time;
13067 if (element == EL_SHIELD_DEADLY)
13068 player->shield_deadly_time_left += level.shield_deadly_time;
13070 else if (element == EL_DYNAMITE ||
13071 element == EL_EM_DYNAMITE ||
13072 element == EL_SP_DISK_RED)
13074 if (player->inventory_size < MAX_INVENTORY_SIZE)
13075 player->inventory_element[player->inventory_size++] = element;
13077 DrawGameDoorValues();
13079 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13081 player->dynabomb_count++;
13082 player->dynabombs_left++;
13084 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13086 player->dynabomb_size++;
13088 else if (element == EL_DYNABOMB_INCREASE_POWER)
13090 player->dynabomb_xl = TRUE;
13092 else if (IS_KEY(element))
13094 player->key[KEY_NR(element)] = TRUE;
13096 DrawGameDoorValues();
13098 else if (element == EL_DC_KEY_WHITE)
13100 player->num_white_keys++;
13102 /* display white keys? */
13103 /* DrawGameDoorValues(); */
13105 else if (IS_ENVELOPE(element))
13107 player->show_envelope = element;
13109 else if (element == EL_EMC_LENSES)
13111 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13113 RedrawAllInvisibleElementsForLenses();
13115 else if (element == EL_EMC_MAGNIFIER)
13117 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13119 RedrawAllInvisibleElementsForMagnifier();
13121 else if (IS_DROPPABLE(element) ||
13122 IS_THROWABLE(element)) /* can be collected and dropped */
13126 if (collect_count == 0)
13127 player->inventory_infinite_element = element;
13129 for (i = 0; i < collect_count; i++)
13130 if (player->inventory_size < MAX_INVENTORY_SIZE)
13131 player->inventory_element[player->inventory_size++] = element;
13133 DrawGameDoorValues();
13135 else if (collect_count > 0)
13137 local_player->gems_still_needed -= collect_count;
13138 if (local_player->gems_still_needed < 0)
13139 local_player->gems_still_needed = 0;
13141 DrawGameValue_Emeralds(local_player->gems_still_needed);
13144 RaiseScoreElement(element);
13145 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13148 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13149 player->index_bit, dig_side);
13151 if (mode == DF_SNAP)
13153 #if USE_NEW_SNAP_DELAY
13154 if (level.block_snap_field)
13155 setFieldForSnapping(x, y, element, move_direction);
13157 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13159 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13162 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13163 player->index_bit, dig_side);
13166 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13168 if (mode == DF_SNAP && element != EL_BD_ROCK)
13169 return MP_NO_ACTION;
13171 if (CAN_FALL(element) && dy)
13172 return MP_NO_ACTION;
13174 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13175 !(element == EL_SPRING && level.use_spring_bug))
13176 return MP_NO_ACTION;
13178 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13179 ((move_direction & MV_VERTICAL &&
13180 ((element_info[element].move_pattern & MV_LEFT &&
13181 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13182 (element_info[element].move_pattern & MV_RIGHT &&
13183 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13184 (move_direction & MV_HORIZONTAL &&
13185 ((element_info[element].move_pattern & MV_UP &&
13186 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13187 (element_info[element].move_pattern & MV_DOWN &&
13188 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13189 return MP_NO_ACTION;
13191 /* do not push elements already moving away faster than player */
13192 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13193 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13194 return MP_NO_ACTION;
13196 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13198 if (player->push_delay_value == -1 || !player_was_pushing)
13199 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13201 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13203 if (player->push_delay_value == -1)
13204 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13206 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13208 if (!player->is_pushing)
13209 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13212 player->is_pushing = TRUE;
13213 player->is_active = TRUE;
13215 if (!(IN_LEV_FIELD(nextx, nexty) &&
13216 (IS_FREE(nextx, nexty) ||
13217 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13218 IS_SB_ELEMENT(element)))))
13219 return MP_NO_ACTION;
13221 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13222 return MP_NO_ACTION;
13224 if (player->push_delay == -1) /* new pushing; restart delay */
13225 player->push_delay = 0;
13227 if (player->push_delay < player->push_delay_value &&
13228 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13229 element != EL_SPRING && element != EL_BALLOON)
13231 /* make sure that there is no move delay before next try to push */
13232 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13233 player->move_delay = 0;
13235 return MP_NO_ACTION;
13238 if (IS_SB_ELEMENT(element))
13240 if (element == EL_SOKOBAN_FIELD_FULL)
13242 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13243 local_player->sokobanfields_still_needed++;
13246 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13248 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13249 local_player->sokobanfields_still_needed--;
13252 Feld[x][y] = EL_SOKOBAN_OBJECT;
13254 if (Back[x][y] == Back[nextx][nexty])
13255 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13256 else if (Back[x][y] != 0)
13257 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13260 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13263 if (local_player->sokobanfields_still_needed == 0 &&
13264 game.emulation == EMU_SOKOBAN)
13266 PlayerWins(player);
13268 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13272 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13274 InitMovingField(x, y, move_direction);
13275 GfxAction[x][y] = ACTION_PUSHING;
13277 if (mode == DF_SNAP)
13278 ContinueMoving(x, y);
13280 MovPos[x][y] = (dx != 0 ? dx : dy);
13282 Pushed[x][y] = TRUE;
13283 Pushed[nextx][nexty] = TRUE;
13285 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13286 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13288 player->push_delay_value = -1; /* get new value later */
13290 /* check for element change _after_ element has been pushed */
13291 if (game.use_change_when_pushing_bug)
13293 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13294 player->index_bit, dig_side);
13295 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13296 player->index_bit, dig_side);
13299 else if (IS_SWITCHABLE(element))
13301 if (PLAYER_SWITCHING(player, x, y))
13303 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13304 player->index_bit, dig_side);
13309 player->is_switching = TRUE;
13310 player->switch_x = x;
13311 player->switch_y = y;
13313 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13315 if (element == EL_ROBOT_WHEEL)
13317 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13321 DrawLevelField(x, y);
13323 else if (element == EL_SP_TERMINAL)
13327 SCAN_PLAYFIELD(xx, yy)
13329 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13331 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13332 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13335 else if (IS_BELT_SWITCH(element))
13337 ToggleBeltSwitch(x, y);
13339 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13340 element == EL_SWITCHGATE_SWITCH_DOWN ||
13341 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13342 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13344 ToggleSwitchgateSwitch(x, y);
13346 else if (element == EL_LIGHT_SWITCH ||
13347 element == EL_LIGHT_SWITCH_ACTIVE)
13349 ToggleLightSwitch(x, y);
13351 else if (element == EL_TIMEGATE_SWITCH ||
13352 element == EL_DC_TIMEGATE_SWITCH)
13354 ActivateTimegateSwitch(x, y);
13356 else if (element == EL_BALLOON_SWITCH_LEFT ||
13357 element == EL_BALLOON_SWITCH_RIGHT ||
13358 element == EL_BALLOON_SWITCH_UP ||
13359 element == EL_BALLOON_SWITCH_DOWN ||
13360 element == EL_BALLOON_SWITCH_NONE ||
13361 element == EL_BALLOON_SWITCH_ANY)
13363 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13364 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13365 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13366 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13367 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13370 else if (element == EL_LAMP)
13372 Feld[x][y] = EL_LAMP_ACTIVE;
13373 local_player->lights_still_needed--;
13375 ResetGfxAnimation(x, y);
13376 DrawLevelField(x, y);
13378 else if (element == EL_TIME_ORB_FULL)
13380 Feld[x][y] = EL_TIME_ORB_EMPTY;
13382 if (level.time > 0 || level.use_time_orb_bug)
13384 TimeLeft += level.time_orb_time;
13385 DrawGameValue_Time(TimeLeft);
13388 ResetGfxAnimation(x, y);
13389 DrawLevelField(x, y);
13391 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13392 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13396 game.ball_state = !game.ball_state;
13398 SCAN_PLAYFIELD(xx, yy)
13400 int e = Feld[xx][yy];
13402 if (game.ball_state)
13404 if (e == EL_EMC_MAGIC_BALL)
13405 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13406 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13407 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13411 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13412 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13413 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13414 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13419 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13420 player->index_bit, dig_side);
13422 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13423 player->index_bit, dig_side);
13425 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13426 player->index_bit, dig_side);
13432 if (!PLAYER_SWITCHING(player, x, y))
13434 player->is_switching = TRUE;
13435 player->switch_x = x;
13436 player->switch_y = y;
13438 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13439 player->index_bit, dig_side);
13440 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13441 player->index_bit, dig_side);
13443 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13444 player->index_bit, dig_side);
13445 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13446 player->index_bit, dig_side);
13449 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13450 player->index_bit, dig_side);
13451 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13452 player->index_bit, dig_side);
13454 return MP_NO_ACTION;
13457 player->push_delay = -1;
13459 if (is_player) /* function can also be called by EL_PENGUIN */
13461 if (Feld[x][y] != element) /* really digged/collected something */
13463 player->is_collecting = !player->is_digging;
13464 player->is_active = TRUE;
13471 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13473 int jx = player->jx, jy = player->jy;
13474 int x = jx + dx, y = jy + dy;
13475 int snap_direction = (dx == -1 ? MV_LEFT :
13476 dx == +1 ? MV_RIGHT :
13478 dy == +1 ? MV_DOWN : MV_NONE);
13479 boolean can_continue_snapping = (level.continuous_snapping &&
13480 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13482 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13485 if (!player->active || !IN_LEV_FIELD(x, y))
13493 if (player->MovPos == 0)
13494 player->is_pushing = FALSE;
13496 player->is_snapping = FALSE;
13498 if (player->MovPos == 0)
13500 player->is_moving = FALSE;
13501 player->is_digging = FALSE;
13502 player->is_collecting = FALSE;
13508 #if USE_NEW_CONTINUOUS_SNAPPING
13509 /* prevent snapping with already pressed snap key when not allowed */
13510 if (player->is_snapping && !can_continue_snapping)
13513 if (player->is_snapping)
13517 player->MovDir = snap_direction;
13519 if (player->MovPos == 0)
13521 player->is_moving = FALSE;
13522 player->is_digging = FALSE;
13523 player->is_collecting = FALSE;
13526 player->is_dropping = FALSE;
13527 player->is_dropping_pressed = FALSE;
13528 player->drop_pressed_delay = 0;
13530 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13533 player->is_snapping = TRUE;
13534 player->is_active = TRUE;
13536 if (player->MovPos == 0)
13538 player->is_moving = FALSE;
13539 player->is_digging = FALSE;
13540 player->is_collecting = FALSE;
13543 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13544 DrawLevelField(player->last_jx, player->last_jy);
13546 DrawLevelField(x, y);
13551 boolean DropElement(struct PlayerInfo *player)
13553 int old_element, new_element;
13554 int dropx = player->jx, dropy = player->jy;
13555 int drop_direction = player->MovDir;
13556 int drop_side = drop_direction;
13557 int drop_element = (player->inventory_size > 0 ?
13558 player->inventory_element[player->inventory_size - 1] :
13559 player->inventory_infinite_element != EL_UNDEFINED ?
13560 player->inventory_infinite_element :
13561 player->dynabombs_left > 0 ?
13562 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13565 player->is_dropping_pressed = TRUE;
13567 /* do not drop an element on top of another element; when holding drop key
13568 pressed without moving, dropped element must move away before the next
13569 element can be dropped (this is especially important if the next element
13570 is dynamite, which can be placed on background for historical reasons) */
13571 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13574 if (IS_THROWABLE(drop_element))
13576 dropx += GET_DX_FROM_DIR(drop_direction);
13577 dropy += GET_DY_FROM_DIR(drop_direction);
13579 if (!IN_LEV_FIELD(dropx, dropy))
13583 old_element = Feld[dropx][dropy]; /* old element at dropping position */
13584 new_element = drop_element; /* default: no change when dropping */
13586 /* check if player is active, not moving and ready to drop */
13587 if (!player->active || player->MovPos || player->drop_delay > 0)
13590 /* check if player has anything that can be dropped */
13591 if (new_element == EL_UNDEFINED)
13594 /* check if drop key was pressed long enough for EM style dynamite */
13595 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13598 /* check if anything can be dropped at the current position */
13599 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13602 /* collected custom elements can only be dropped on empty fields */
13603 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13606 if (old_element != EL_EMPTY)
13607 Back[dropx][dropy] = old_element; /* store old element on this field */
13609 ResetGfxAnimation(dropx, dropy);
13610 ResetRandomAnimationValue(dropx, dropy);
13612 if (player->inventory_size > 0 ||
13613 player->inventory_infinite_element != EL_UNDEFINED)
13615 if (player->inventory_size > 0)
13617 player->inventory_size--;
13619 DrawGameDoorValues();
13621 if (new_element == EL_DYNAMITE)
13622 new_element = EL_DYNAMITE_ACTIVE;
13623 else if (new_element == EL_EM_DYNAMITE)
13624 new_element = EL_EM_DYNAMITE_ACTIVE;
13625 else if (new_element == EL_SP_DISK_RED)
13626 new_element = EL_SP_DISK_RED_ACTIVE;
13629 Feld[dropx][dropy] = new_element;
13631 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13632 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13633 el2img(Feld[dropx][dropy]), 0);
13635 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13637 /* needed if previous element just changed to "empty" in the last frame */
13638 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13640 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13641 player->index_bit, drop_side);
13642 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13644 player->index_bit, drop_side);
13646 TestIfElementTouchesCustomElement(dropx, dropy);
13648 else /* player is dropping a dyna bomb */
13650 player->dynabombs_left--;
13652 Feld[dropx][dropy] = new_element;
13654 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13655 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13656 el2img(Feld[dropx][dropy]), 0);
13658 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13661 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13662 InitField_WithBug1(dropx, dropy, FALSE);
13664 new_element = Feld[dropx][dropy]; /* element might have changed */
13666 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13667 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13669 int move_direction, nextx, nexty;
13671 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13672 MovDir[dropx][dropy] = drop_direction;
13674 move_direction = MovDir[dropx][dropy];
13675 nextx = dropx + GET_DX_FROM_DIR(move_direction);
13676 nexty = dropy + GET_DY_FROM_DIR(move_direction);
13678 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13680 #if USE_FIX_IMPACT_COLLISION
13681 /* do not cause impact style collision by dropping elements that can fall */
13682 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13684 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13688 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13689 player->is_dropping = TRUE;
13691 player->drop_pressed_delay = 0;
13692 player->is_dropping_pressed = FALSE;
13694 player->drop_x = dropx;
13695 player->drop_y = dropy;
13700 /* ------------------------------------------------------------------------- */
13701 /* game sound playing functions */
13702 /* ------------------------------------------------------------------------- */
13704 static int *loop_sound_frame = NULL;
13705 static int *loop_sound_volume = NULL;
13707 void InitPlayLevelSound()
13709 int num_sounds = getSoundListSize();
13711 checked_free(loop_sound_frame);
13712 checked_free(loop_sound_volume);
13714 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
13715 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13718 static void PlayLevelSound(int x, int y, int nr)
13720 int sx = SCREENX(x), sy = SCREENY(y);
13721 int volume, stereo_position;
13722 int max_distance = 8;
13723 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13725 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13726 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13729 if (!IN_LEV_FIELD(x, y) ||
13730 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13731 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13734 volume = SOUND_MAX_VOLUME;
13736 if (!IN_SCR_FIELD(sx, sy))
13738 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13739 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13741 volume -= volume * (dx > dy ? dx : dy) / max_distance;
13744 stereo_position = (SOUND_MAX_LEFT +
13745 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13746 (SCR_FIELDX + 2 * max_distance));
13748 if (IS_LOOP_SOUND(nr))
13750 /* This assures that quieter loop sounds do not overwrite louder ones,
13751 while restarting sound volume comparison with each new game frame. */
13753 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13756 loop_sound_volume[nr] = volume;
13757 loop_sound_frame[nr] = FrameCounter;
13760 PlaySoundExt(nr, volume, stereo_position, type);
13763 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13765 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13766 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13767 y < LEVELY(BY1) ? LEVELY(BY1) :
13768 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13772 static void PlayLevelSoundAction(int x, int y, int action)
13774 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13777 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13779 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13781 if (sound_effect != SND_UNDEFINED)
13782 PlayLevelSound(x, y, sound_effect);
13785 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13788 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13790 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13791 PlayLevelSound(x, y, sound_effect);
13794 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13796 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13798 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13799 PlayLevelSound(x, y, sound_effect);
13802 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13804 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13806 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13807 StopSound(sound_effect);
13810 static void PlayLevelMusic()
13812 if (levelset.music[level_nr] != MUS_UNDEFINED)
13813 PlayMusic(levelset.music[level_nr]); /* from config file */
13815 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13818 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13820 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13821 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13822 int x = xx - 1 - offset;
13823 int y = yy - 1 - offset;
13828 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13832 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13836 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13840 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13844 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13848 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13852 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13855 case SAMPLE_android_clone:
13856 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13859 case SAMPLE_android_move:
13860 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13863 case SAMPLE_spring:
13864 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13868 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13872 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13875 case SAMPLE_eater_eat:
13876 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13880 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13883 case SAMPLE_collect:
13884 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13887 case SAMPLE_diamond:
13888 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13891 case SAMPLE_squash:
13892 /* !!! CHECK THIS !!! */
13894 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13896 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13900 case SAMPLE_wonderfall:
13901 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13905 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13909 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13913 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13917 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13921 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13925 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13928 case SAMPLE_wonder:
13929 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13933 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13936 case SAMPLE_exit_open:
13937 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13940 case SAMPLE_exit_leave:
13941 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13944 case SAMPLE_dynamite:
13945 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13949 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13953 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13957 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13961 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13965 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13969 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13973 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13979 void ChangeTime(int value)
13981 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13985 /* EMC game engine uses value from time counter of RND game engine */
13986 level.native_em_level->lev->time = *time;
13988 DrawGameValue_Time(*time);
13991 void RaiseScore(int value)
13993 /* EMC game engine and RND game engine have separate score counters */
13994 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13995 &level.native_em_level->lev->score : &local_player->score);
13999 DrawGameValue_Score(*score);
14003 void RaiseScore(int value)
14005 local_player->score += value;
14007 DrawGameValue_Score(local_player->score);
14010 void RaiseScoreElement(int element)
14015 case EL_BD_DIAMOND:
14016 case EL_EMERALD_YELLOW:
14017 case EL_EMERALD_RED:
14018 case EL_EMERALD_PURPLE:
14019 case EL_SP_INFOTRON:
14020 RaiseScore(level.score[SC_EMERALD]);
14023 RaiseScore(level.score[SC_DIAMOND]);
14026 RaiseScore(level.score[SC_CRYSTAL]);
14029 RaiseScore(level.score[SC_PEARL]);
14032 case EL_BD_BUTTERFLY:
14033 case EL_SP_ELECTRON:
14034 RaiseScore(level.score[SC_BUG]);
14037 case EL_BD_FIREFLY:
14038 case EL_SP_SNIKSNAK:
14039 RaiseScore(level.score[SC_SPACESHIP]);
14042 case EL_DARK_YAMYAM:
14043 RaiseScore(level.score[SC_YAMYAM]);
14046 RaiseScore(level.score[SC_ROBOT]);
14049 RaiseScore(level.score[SC_PACMAN]);
14052 RaiseScore(level.score[SC_NUT]);
14055 case EL_EM_DYNAMITE:
14056 case EL_SP_DISK_RED:
14057 case EL_DYNABOMB_INCREASE_NUMBER:
14058 case EL_DYNABOMB_INCREASE_SIZE:
14059 case EL_DYNABOMB_INCREASE_POWER:
14060 RaiseScore(level.score[SC_DYNAMITE]);
14062 case EL_SHIELD_NORMAL:
14063 case EL_SHIELD_DEADLY:
14064 RaiseScore(level.score[SC_SHIELD]);
14066 case EL_EXTRA_TIME:
14067 RaiseScore(level.extra_time_score);
14081 case EL_DC_KEY_WHITE:
14082 RaiseScore(level.score[SC_KEY]);
14085 RaiseScore(element_info[element].collect_score);
14090 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14092 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14094 #if defined(NETWORK_AVALIABLE)
14095 if (options.network)
14096 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14102 game_status = GAME_MODE_MAIN;
14108 FadeOut(REDRAW_FIELD);
14110 game_status = GAME_MODE_MAIN;
14112 DrawAndFadeInMainMenu(REDRAW_FIELD);
14116 else /* continue playing the game */
14118 if (tape.playing && tape.deactivate_display)
14119 TapeDeactivateDisplayOff(TRUE);
14121 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14123 if (tape.playing && tape.deactivate_display)
14124 TapeDeactivateDisplayOn();
14128 void RequestQuitGame(boolean ask_if_really_quit)
14130 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14131 boolean skip_request = AllPlayersGone || quick_quit;
14133 RequestQuitGameExt(skip_request, quick_quit,
14134 "Do you really want to quit the game ?");
14138 /* ------------------------------------------------------------------------- */
14139 /* random generator functions */
14140 /* ------------------------------------------------------------------------- */
14142 unsigned int InitEngineRandom_RND(long seed)
14144 game.num_random_calls = 0;
14147 unsigned int rnd_seed = InitEngineRandom(seed);
14149 printf("::: START RND: %d\n", rnd_seed);
14154 return InitEngineRandom(seed);
14160 unsigned int RND(int max)
14164 game.num_random_calls++;
14166 return GetEngineRandom(max);
14173 /* ------------------------------------------------------------------------- */
14174 /* game engine snapshot handling functions */
14175 /* ------------------------------------------------------------------------- */
14177 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14179 struct EngineSnapshotInfo
14181 /* runtime values for custom element collect score */
14182 int collect_score[NUM_CUSTOM_ELEMENTS];
14184 /* runtime values for group element choice position */
14185 int choice_pos[NUM_GROUP_ELEMENTS];
14187 /* runtime values for belt position animations */
14188 int belt_graphic[4 * NUM_BELT_PARTS];
14189 int belt_anim_mode[4 * NUM_BELT_PARTS];
14192 struct EngineSnapshotNodeInfo
14199 static struct EngineSnapshotInfo engine_snapshot_rnd;
14200 static ListNode *engine_snapshot_list = NULL;
14201 static char *snapshot_level_identifier = NULL;
14202 static int snapshot_level_nr = -1;
14204 void FreeEngineSnapshot()
14206 while (engine_snapshot_list != NULL)
14207 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14210 setString(&snapshot_level_identifier, NULL);
14211 snapshot_level_nr = -1;
14214 static void SaveEngineSnapshotValues_RND()
14216 static int belt_base_active_element[4] =
14218 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14219 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14220 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14221 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14225 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14227 int element = EL_CUSTOM_START + i;
14229 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14232 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14234 int element = EL_GROUP_START + i;
14236 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14239 for (i = 0; i < 4; i++)
14241 for (j = 0; j < NUM_BELT_PARTS; j++)
14243 int element = belt_base_active_element[i] + j;
14244 int graphic = el2img(element);
14245 int anim_mode = graphic_info[graphic].anim_mode;
14247 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14248 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14253 static void LoadEngineSnapshotValues_RND()
14255 unsigned long num_random_calls = game.num_random_calls;
14258 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14260 int element = EL_CUSTOM_START + i;
14262 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14265 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14267 int element = EL_GROUP_START + i;
14269 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14272 for (i = 0; i < 4; i++)
14274 for (j = 0; j < NUM_BELT_PARTS; j++)
14276 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14277 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14279 graphic_info[graphic].anim_mode = anim_mode;
14283 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14285 InitRND(tape.random_seed);
14286 for (i = 0; i < num_random_calls; i++)
14290 if (game.num_random_calls != num_random_calls)
14292 Error(ERR_INFO, "number of random calls out of sync");
14293 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14294 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14295 Error(ERR_EXIT, "this should not happen -- please debug");
14299 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14301 struct EngineSnapshotNodeInfo *bi =
14302 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14304 bi->buffer_orig = buffer;
14305 bi->buffer_copy = checked_malloc(size);
14308 memcpy(bi->buffer_copy, buffer, size);
14310 addNodeToList(&engine_snapshot_list, NULL, bi);
14313 void SaveEngineSnapshot()
14315 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14317 if (level_editor_test_game) /* do not save snapshots from editor */
14320 /* copy some special values to a structure better suited for the snapshot */
14322 SaveEngineSnapshotValues_RND();
14323 SaveEngineSnapshotValues_EM();
14325 /* save values stored in special snapshot structure */
14327 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14328 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14330 /* save further RND engine values */
14332 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14333 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14334 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14336 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14337 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14338 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14339 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14341 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14342 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14343 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14344 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14345 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14347 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14348 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14349 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14351 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14353 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14355 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14356 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14358 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14359 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14360 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14361 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14362 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14363 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14364 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14365 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14366 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14367 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14368 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14369 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14370 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14371 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14372 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14373 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14374 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14375 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14377 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14378 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14380 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14381 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14382 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14384 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14385 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14387 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14388 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14389 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14390 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14391 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14393 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14394 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14396 /* save level identification information */
14398 setString(&snapshot_level_identifier, leveldir_current->identifier);
14399 snapshot_level_nr = level_nr;
14402 ListNode *node = engine_snapshot_list;
14405 while (node != NULL)
14407 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14412 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14416 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14418 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14421 void LoadEngineSnapshot()
14423 ListNode *node = engine_snapshot_list;
14425 if (engine_snapshot_list == NULL)
14428 while (node != NULL)
14430 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14435 /* restore special values from snapshot structure */
14437 LoadEngineSnapshotValues_RND();
14438 LoadEngineSnapshotValues_EM();
14441 boolean CheckEngineSnapshot()
14443 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14444 snapshot_level_nr == level_nr);
14448 /* ---------- new game button stuff ---------------------------------------- */
14450 /* graphic position values for game buttons */
14451 #define GAME_BUTTON_XSIZE 30
14452 #define GAME_BUTTON_YSIZE 30
14453 #define GAME_BUTTON_XPOS 5
14454 #define GAME_BUTTON_YPOS 215
14455 #define SOUND_BUTTON_XPOS 5
14456 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14458 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14459 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14460 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14461 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14462 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14463 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14471 } gamebutton_info[NUM_GAME_BUTTONS] =
14475 &game.button.stop.x, &game.button.stop.y,
14476 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14481 &game.button.pause.x, &game.button.pause.y,
14482 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14483 GAME_CTRL_ID_PAUSE,
14487 &game.button.play.x, &game.button.play.y,
14488 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14493 &game.button.sound_music.x, &game.button.sound_music.y,
14494 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14495 SOUND_CTRL_ID_MUSIC,
14496 "background music on/off"
14499 &game.button.sound_loops.x, &game.button.sound_loops.y,
14500 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
14501 SOUND_CTRL_ID_LOOPS,
14502 "sound loops on/off"
14505 &game.button.sound_simple.x,&game.button.sound_simple.y,
14506 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
14507 SOUND_CTRL_ID_SIMPLE,
14508 "normal sounds on/off"
14512 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14517 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14518 GAME_CTRL_ID_PAUSE,
14522 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14527 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14528 SOUND_CTRL_ID_MUSIC,
14529 "background music on/off"
14532 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
14533 SOUND_CTRL_ID_LOOPS,
14534 "sound loops on/off"
14537 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
14538 SOUND_CTRL_ID_SIMPLE,
14539 "normal sounds on/off"
14544 void CreateGameButtons()
14548 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14550 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14551 struct GadgetInfo *gi;
14554 unsigned long event_mask;
14556 int gd_xoffset, gd_yoffset;
14557 int gd_x1, gd_x2, gd_y1, gd_y2;
14560 x = DX + *gamebutton_info[i].x;
14561 y = DY + *gamebutton_info[i].y;
14562 gd_xoffset = gamebutton_info[i].gd_x;
14563 gd_yoffset = gamebutton_info[i].gd_y;
14564 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14565 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14567 if (id == GAME_CTRL_ID_STOP ||
14568 id == GAME_CTRL_ID_PAUSE ||
14569 id == GAME_CTRL_ID_PLAY)
14571 button_type = GD_TYPE_NORMAL_BUTTON;
14573 event_mask = GD_EVENT_RELEASED;
14574 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14575 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14579 button_type = GD_TYPE_CHECK_BUTTON;
14581 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14582 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14583 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14584 event_mask = GD_EVENT_PRESSED;
14585 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
14586 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14589 gi = CreateGadget(GDI_CUSTOM_ID, id,
14590 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14595 GDI_X, DX + gd_xoffset,
14596 GDI_Y, DY + gd_yoffset,
14598 GDI_WIDTH, GAME_BUTTON_XSIZE,
14599 GDI_HEIGHT, GAME_BUTTON_YSIZE,
14600 GDI_TYPE, button_type,
14601 GDI_STATE, GD_BUTTON_UNPRESSED,
14602 GDI_CHECKED, checked,
14603 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14604 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14605 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14606 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14607 GDI_EVENT_MASK, event_mask,
14608 GDI_CALLBACK_ACTION, HandleGameButtons,
14612 Error(ERR_EXIT, "cannot create gadget");
14614 game_gadget[id] = gi;
14618 void FreeGameButtons()
14622 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14623 FreeGadget(game_gadget[i]);
14626 static void MapGameButtons()
14630 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14631 MapGadget(game_gadget[i]);
14634 void UnmapGameButtons()
14638 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14639 UnmapGadget(game_gadget[i]);
14642 static void HandleGameButtons(struct GadgetInfo *gi)
14644 int id = gi->custom_id;
14646 if (game_status != GAME_MODE_PLAYING)
14651 case GAME_CTRL_ID_STOP:
14655 RequestQuitGame(TRUE);
14658 case GAME_CTRL_ID_PAUSE:
14659 if (options.network)
14661 #if defined(NETWORK_AVALIABLE)
14663 SendToServer_ContinuePlaying();
14665 SendToServer_PausePlaying();
14669 TapeTogglePause(TAPE_TOGGLE_MANUAL);
14672 case GAME_CTRL_ID_PLAY:
14675 #if defined(NETWORK_AVALIABLE)
14676 if (options.network)
14677 SendToServer_ContinuePlaying();
14681 tape.pausing = FALSE;
14682 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14687 case SOUND_CTRL_ID_MUSIC:
14688 if (setup.sound_music)
14690 setup.sound_music = FALSE;
14693 else if (audio.music_available)
14695 setup.sound = setup.sound_music = TRUE;
14697 SetAudioMode(setup.sound);
14703 case SOUND_CTRL_ID_LOOPS:
14704 if (setup.sound_loops)
14705 setup.sound_loops = FALSE;
14706 else if (audio.loops_available)
14708 setup.sound = setup.sound_loops = TRUE;
14709 SetAudioMode(setup.sound);
14713 case SOUND_CTRL_ID_SIMPLE:
14714 if (setup.sound_simple)
14715 setup.sound_simple = FALSE;
14716 else if (audio.sound_available)
14718 setup.sound = setup.sound_simple = TRUE;
14719 SetAudioMode(setup.sound);