1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
34 #define USE_QUICKSAND_IMPACT_BUGFIX 0
35 #define USE_DELAYED_GFX_REDRAW 0
36 #define USE_NEW_PLAYER_ASSIGNMENTS 1
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y) \
40 GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y) \
42 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
44 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y) \
46 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
48 #define TEST_DrawLevelField(x, y) \
50 #define TEST_DrawLevelFieldCrumbled(x, y) \
51 DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
53 DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y) \
55 DrawTwinkleOnField(x, y)
64 /* for MovePlayer() */
65 #define MP_NO_ACTION 0
68 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
70 /* for ScrollPlayer() */
72 #define SCROLL_GO_ON 1
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START 0
76 #define EX_TYPE_NONE 0
77 #define EX_TYPE_NORMAL (1 << 0)
78 #define EX_TYPE_CENTER (1 << 1)
79 #define EX_TYPE_BORDER (1 << 2)
80 #define EX_TYPE_CROSS (1 << 3)
81 #define EX_TYPE_DYNA (1 << 4)
82 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
84 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER 0
91 #define GAME_PANEL_GEMS 1
92 #define GAME_PANEL_INVENTORY_COUNT 2
93 #define GAME_PANEL_INVENTORY_FIRST_1 3
94 #define GAME_PANEL_INVENTORY_FIRST_2 4
95 #define GAME_PANEL_INVENTORY_FIRST_3 5
96 #define GAME_PANEL_INVENTORY_FIRST_4 6
97 #define GAME_PANEL_INVENTORY_FIRST_5 7
98 #define GAME_PANEL_INVENTORY_FIRST_6 8
99 #define GAME_PANEL_INVENTORY_FIRST_7 9
100 #define GAME_PANEL_INVENTORY_FIRST_8 10
101 #define GAME_PANEL_INVENTORY_LAST_1 11
102 #define GAME_PANEL_INVENTORY_LAST_2 12
103 #define GAME_PANEL_INVENTORY_LAST_3 13
104 #define GAME_PANEL_INVENTORY_LAST_4 14
105 #define GAME_PANEL_INVENTORY_LAST_5 15
106 #define GAME_PANEL_INVENTORY_LAST_6 16
107 #define GAME_PANEL_INVENTORY_LAST_7 17
108 #define GAME_PANEL_INVENTORY_LAST_8 18
109 #define GAME_PANEL_KEY_1 19
110 #define GAME_PANEL_KEY_2 20
111 #define GAME_PANEL_KEY_3 21
112 #define GAME_PANEL_KEY_4 22
113 #define GAME_PANEL_KEY_5 23
114 #define GAME_PANEL_KEY_6 24
115 #define GAME_PANEL_KEY_7 25
116 #define GAME_PANEL_KEY_8 26
117 #define GAME_PANEL_KEY_WHITE 27
118 #define GAME_PANEL_KEY_WHITE_COUNT 28
119 #define GAME_PANEL_SCORE 29
120 #define GAME_PANEL_HIGHSCORE 30
121 #define GAME_PANEL_TIME 31
122 #define GAME_PANEL_TIME_HH 32
123 #define GAME_PANEL_TIME_MM 33
124 #define GAME_PANEL_TIME_SS 34
125 #define GAME_PANEL_FRAME 35
126 #define GAME_PANEL_SHIELD_NORMAL 36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
128 #define GAME_PANEL_SHIELD_DEADLY 38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
130 #define GAME_PANEL_EXIT 40
131 #define GAME_PANEL_EMC_MAGIC_BALL 41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
133 #define GAME_PANEL_LIGHT_SWITCH 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
135 #define GAME_PANEL_TIMEGATE_SWITCH 45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
137 #define GAME_PANEL_SWITCHGATE_SWITCH 47
138 #define GAME_PANEL_EMC_LENSES 48
139 #define GAME_PANEL_EMC_LENSES_TIME 49
140 #define GAME_PANEL_EMC_MAGNIFIER 50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
142 #define GAME_PANEL_BALLOON_SWITCH 52
143 #define GAME_PANEL_DYNABOMB_NUMBER 53
144 #define GAME_PANEL_DYNABOMB_SIZE 54
145 #define GAME_PANEL_DYNABOMB_POWER 55
146 #define GAME_PANEL_PENGUINS 56
147 #define GAME_PANEL_SOKOBAN_OBJECTS 57
148 #define GAME_PANEL_SOKOBAN_FIELDS 58
149 #define GAME_PANEL_ROBOT_WHEEL 59
150 #define GAME_PANEL_CONVEYOR_BELT_1 60
151 #define GAME_PANEL_CONVEYOR_BELT_2 61
152 #define GAME_PANEL_CONVEYOR_BELT_3 62
153 #define GAME_PANEL_CONVEYOR_BELT_4 63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
158 #define GAME_PANEL_MAGIC_WALL 68
159 #define GAME_PANEL_MAGIC_WALL_TIME 69
160 #define GAME_PANEL_GRAVITY_STATE 70
161 #define GAME_PANEL_GRAPHIC_1 71
162 #define GAME_PANEL_GRAPHIC_2 72
163 #define GAME_PANEL_GRAPHIC_3 73
164 #define GAME_PANEL_GRAPHIC_4 74
165 #define GAME_PANEL_GRAPHIC_5 75
166 #define GAME_PANEL_GRAPHIC_6 76
167 #define GAME_PANEL_GRAPHIC_7 77
168 #define GAME_PANEL_GRAPHIC_8 78
169 #define GAME_PANEL_ELEMENT_1 79
170 #define GAME_PANEL_ELEMENT_2 80
171 #define GAME_PANEL_ELEMENT_3 81
172 #define GAME_PANEL_ELEMENT_4 82
173 #define GAME_PANEL_ELEMENT_5 83
174 #define GAME_PANEL_ELEMENT_6 84
175 #define GAME_PANEL_ELEMENT_7 85
176 #define GAME_PANEL_ELEMENT_8 86
177 #define GAME_PANEL_ELEMENT_COUNT_1 87
178 #define GAME_PANEL_ELEMENT_COUNT_2 88
179 #define GAME_PANEL_ELEMENT_COUNT_3 89
180 #define GAME_PANEL_ELEMENT_COUNT_4 90
181 #define GAME_PANEL_ELEMENT_COUNT_5 91
182 #define GAME_PANEL_ELEMENT_COUNT_6 92
183 #define GAME_PANEL_ELEMENT_COUNT_7 93
184 #define GAME_PANEL_ELEMENT_COUNT_8 94
185 #define GAME_PANEL_CE_SCORE_1 95
186 #define GAME_PANEL_CE_SCORE_2 96
187 #define GAME_PANEL_CE_SCORE_3 97
188 #define GAME_PANEL_CE_SCORE_4 98
189 #define GAME_PANEL_CE_SCORE_5 99
190 #define GAME_PANEL_CE_SCORE_6 100
191 #define GAME_PANEL_CE_SCORE_7 101
192 #define GAME_PANEL_CE_SCORE_8 102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
201 #define GAME_PANEL_PLAYER_NAME 111
202 #define GAME_PANEL_LEVEL_NAME 112
203 #define GAME_PANEL_LEVEL_AUTHOR 113
205 #define NUM_GAME_PANEL_CONTROLS 114
207 struct GamePanelOrderInfo
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
215 struct GamePanelControlInfo
219 struct TextPosInfo *pos;
222 int value, last_value;
223 int frame, last_frame;
228 static struct GamePanelControlInfo game_panel_controls[] =
231 GAME_PANEL_LEVEL_NUMBER,
232 &game.panel.level_number,
241 GAME_PANEL_INVENTORY_COUNT,
242 &game.panel.inventory_count,
246 GAME_PANEL_INVENTORY_FIRST_1,
247 &game.panel.inventory_first[0],
251 GAME_PANEL_INVENTORY_FIRST_2,
252 &game.panel.inventory_first[1],
256 GAME_PANEL_INVENTORY_FIRST_3,
257 &game.panel.inventory_first[2],
261 GAME_PANEL_INVENTORY_FIRST_4,
262 &game.panel.inventory_first[3],
266 GAME_PANEL_INVENTORY_FIRST_5,
267 &game.panel.inventory_first[4],
271 GAME_PANEL_INVENTORY_FIRST_6,
272 &game.panel.inventory_first[5],
276 GAME_PANEL_INVENTORY_FIRST_7,
277 &game.panel.inventory_first[6],
281 GAME_PANEL_INVENTORY_FIRST_8,
282 &game.panel.inventory_first[7],
286 GAME_PANEL_INVENTORY_LAST_1,
287 &game.panel.inventory_last[0],
291 GAME_PANEL_INVENTORY_LAST_2,
292 &game.panel.inventory_last[1],
296 GAME_PANEL_INVENTORY_LAST_3,
297 &game.panel.inventory_last[2],
301 GAME_PANEL_INVENTORY_LAST_4,
302 &game.panel.inventory_last[3],
306 GAME_PANEL_INVENTORY_LAST_5,
307 &game.panel.inventory_last[4],
311 GAME_PANEL_INVENTORY_LAST_6,
312 &game.panel.inventory_last[5],
316 GAME_PANEL_INVENTORY_LAST_7,
317 &game.panel.inventory_last[6],
321 GAME_PANEL_INVENTORY_LAST_8,
322 &game.panel.inventory_last[7],
366 GAME_PANEL_KEY_WHITE,
367 &game.panel.key_white,
371 GAME_PANEL_KEY_WHITE_COUNT,
372 &game.panel.key_white_count,
381 GAME_PANEL_HIGHSCORE,
382 &game.panel.highscore,
411 GAME_PANEL_SHIELD_NORMAL,
412 &game.panel.shield_normal,
416 GAME_PANEL_SHIELD_NORMAL_TIME,
417 &game.panel.shield_normal_time,
421 GAME_PANEL_SHIELD_DEADLY,
422 &game.panel.shield_deadly,
426 GAME_PANEL_SHIELD_DEADLY_TIME,
427 &game.panel.shield_deadly_time,
436 GAME_PANEL_EMC_MAGIC_BALL,
437 &game.panel.emc_magic_ball,
441 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442 &game.panel.emc_magic_ball_switch,
446 GAME_PANEL_LIGHT_SWITCH,
447 &game.panel.light_switch,
451 GAME_PANEL_LIGHT_SWITCH_TIME,
452 &game.panel.light_switch_time,
456 GAME_PANEL_TIMEGATE_SWITCH,
457 &game.panel.timegate_switch,
461 GAME_PANEL_TIMEGATE_SWITCH_TIME,
462 &game.panel.timegate_switch_time,
466 GAME_PANEL_SWITCHGATE_SWITCH,
467 &game.panel.switchgate_switch,
471 GAME_PANEL_EMC_LENSES,
472 &game.panel.emc_lenses,
476 GAME_PANEL_EMC_LENSES_TIME,
477 &game.panel.emc_lenses_time,
481 GAME_PANEL_EMC_MAGNIFIER,
482 &game.panel.emc_magnifier,
486 GAME_PANEL_EMC_MAGNIFIER_TIME,
487 &game.panel.emc_magnifier_time,
491 GAME_PANEL_BALLOON_SWITCH,
492 &game.panel.balloon_switch,
496 GAME_PANEL_DYNABOMB_NUMBER,
497 &game.panel.dynabomb_number,
501 GAME_PANEL_DYNABOMB_SIZE,
502 &game.panel.dynabomb_size,
506 GAME_PANEL_DYNABOMB_POWER,
507 &game.panel.dynabomb_power,
512 &game.panel.penguins,
516 GAME_PANEL_SOKOBAN_OBJECTS,
517 &game.panel.sokoban_objects,
521 GAME_PANEL_SOKOBAN_FIELDS,
522 &game.panel.sokoban_fields,
526 GAME_PANEL_ROBOT_WHEEL,
527 &game.panel.robot_wheel,
531 GAME_PANEL_CONVEYOR_BELT_1,
532 &game.panel.conveyor_belt[0],
536 GAME_PANEL_CONVEYOR_BELT_2,
537 &game.panel.conveyor_belt[1],
541 GAME_PANEL_CONVEYOR_BELT_3,
542 &game.panel.conveyor_belt[2],
546 GAME_PANEL_CONVEYOR_BELT_4,
547 &game.panel.conveyor_belt[3],
551 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552 &game.panel.conveyor_belt_switch[0],
556 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557 &game.panel.conveyor_belt_switch[1],
561 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562 &game.panel.conveyor_belt_switch[2],
566 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567 &game.panel.conveyor_belt_switch[3],
571 GAME_PANEL_MAGIC_WALL,
572 &game.panel.magic_wall,
576 GAME_PANEL_MAGIC_WALL_TIME,
577 &game.panel.magic_wall_time,
581 GAME_PANEL_GRAVITY_STATE,
582 &game.panel.gravity_state,
586 GAME_PANEL_GRAPHIC_1,
587 &game.panel.graphic[0],
591 GAME_PANEL_GRAPHIC_2,
592 &game.panel.graphic[1],
596 GAME_PANEL_GRAPHIC_3,
597 &game.panel.graphic[2],
601 GAME_PANEL_GRAPHIC_4,
602 &game.panel.graphic[3],
606 GAME_PANEL_GRAPHIC_5,
607 &game.panel.graphic[4],
611 GAME_PANEL_GRAPHIC_6,
612 &game.panel.graphic[5],
616 GAME_PANEL_GRAPHIC_7,
617 &game.panel.graphic[6],
621 GAME_PANEL_GRAPHIC_8,
622 &game.panel.graphic[7],
626 GAME_PANEL_ELEMENT_1,
627 &game.panel.element[0],
631 GAME_PANEL_ELEMENT_2,
632 &game.panel.element[1],
636 GAME_PANEL_ELEMENT_3,
637 &game.panel.element[2],
641 GAME_PANEL_ELEMENT_4,
642 &game.panel.element[3],
646 GAME_PANEL_ELEMENT_5,
647 &game.panel.element[4],
651 GAME_PANEL_ELEMENT_6,
652 &game.panel.element[5],
656 GAME_PANEL_ELEMENT_7,
657 &game.panel.element[6],
661 GAME_PANEL_ELEMENT_8,
662 &game.panel.element[7],
666 GAME_PANEL_ELEMENT_COUNT_1,
667 &game.panel.element_count[0],
671 GAME_PANEL_ELEMENT_COUNT_2,
672 &game.panel.element_count[1],
676 GAME_PANEL_ELEMENT_COUNT_3,
677 &game.panel.element_count[2],
681 GAME_PANEL_ELEMENT_COUNT_4,
682 &game.panel.element_count[3],
686 GAME_PANEL_ELEMENT_COUNT_5,
687 &game.panel.element_count[4],
691 GAME_PANEL_ELEMENT_COUNT_6,
692 &game.panel.element_count[5],
696 GAME_PANEL_ELEMENT_COUNT_7,
697 &game.panel.element_count[6],
701 GAME_PANEL_ELEMENT_COUNT_8,
702 &game.panel.element_count[7],
706 GAME_PANEL_CE_SCORE_1,
707 &game.panel.ce_score[0],
711 GAME_PANEL_CE_SCORE_2,
712 &game.panel.ce_score[1],
716 GAME_PANEL_CE_SCORE_3,
717 &game.panel.ce_score[2],
721 GAME_PANEL_CE_SCORE_4,
722 &game.panel.ce_score[3],
726 GAME_PANEL_CE_SCORE_5,
727 &game.panel.ce_score[4],
731 GAME_PANEL_CE_SCORE_6,
732 &game.panel.ce_score[5],
736 GAME_PANEL_CE_SCORE_7,
737 &game.panel.ce_score[6],
741 GAME_PANEL_CE_SCORE_8,
742 &game.panel.ce_score[7],
746 GAME_PANEL_CE_SCORE_1_ELEMENT,
747 &game.panel.ce_score_element[0],
751 GAME_PANEL_CE_SCORE_2_ELEMENT,
752 &game.panel.ce_score_element[1],
756 GAME_PANEL_CE_SCORE_3_ELEMENT,
757 &game.panel.ce_score_element[2],
761 GAME_PANEL_CE_SCORE_4_ELEMENT,
762 &game.panel.ce_score_element[3],
766 GAME_PANEL_CE_SCORE_5_ELEMENT,
767 &game.panel.ce_score_element[4],
771 GAME_PANEL_CE_SCORE_6_ELEMENT,
772 &game.panel.ce_score_element[5],
776 GAME_PANEL_CE_SCORE_7_ELEMENT,
777 &game.panel.ce_score_element[6],
781 GAME_PANEL_CE_SCORE_8_ELEMENT,
782 &game.panel.ce_score_element[7],
786 GAME_PANEL_PLAYER_NAME,
787 &game.panel.player_name,
791 GAME_PANEL_LEVEL_NAME,
792 &game.panel.level_name,
796 GAME_PANEL_LEVEL_AUTHOR,
797 &game.panel.level_author,
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING 3
810 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION 2
812 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF -1
816 #define INITIAL_MOVE_DELAY_ON 0
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED 32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED 4
822 #define MOVE_DELAY_MAX_SPEED 1
824 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
827 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
830 /* values for other actions */
831 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
832 #define MOVE_STEPSIZE_MIN (1)
833 #define MOVE_STEPSIZE_MAX (TILEX)
835 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
836 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
838 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
840 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
841 RND(element_info[e].push_delay_random))
842 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
843 RND(element_info[e].drop_delay_random))
844 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
845 RND(element_info[e].move_delay_random))
846 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
847 (element_info[e].move_delay_random))
848 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
849 RND(element_info[e].ce_value_random_initial))
850 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
851 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
852 RND((c)->delay_random * (c)->delay_frames))
853 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
854 RND((c)->delay_random))
857 #define GET_VALID_RUNTIME_ELEMENT(e) \
858 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
860 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
861 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
862 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
863 (be) + (e) - EL_SELF)
865 #define GET_PLAYER_FROM_BITS(p) \
866 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
868 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
869 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
870 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
871 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
872 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
873 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
874 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
875 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
876 RESOLVED_REFERENCE_ELEMENT(be, e) : \
879 #define CAN_GROW_INTO(e) \
880 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
882 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
883 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
886 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
887 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
888 (CAN_MOVE_INTO_ACID(e) && \
889 Feld[x][y] == EL_ACID) || \
892 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
893 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
894 (CAN_MOVE_INTO_ACID(e) && \
895 Feld[x][y] == EL_ACID) || \
898 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
899 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
901 (CAN_MOVE_INTO_ACID(e) && \
902 Feld[x][y] == EL_ACID) || \
903 (DONT_COLLIDE_WITH(e) && \
905 !PLAYER_ENEMY_PROTECTED(x, y))))
907 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
908 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
910 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
911 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
913 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
914 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
916 #define ANDROID_CAN_CLONE_FIELD(x, y) \
917 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
918 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
920 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
921 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
923 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
924 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
926 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
927 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
929 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
930 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
932 #define PIG_CAN_ENTER_FIELD(e, x, y) \
933 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
935 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
936 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
937 Feld[x][y] == EL_EM_EXIT_OPEN || \
938 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
939 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
940 IS_FOOD_PENGUIN(Feld[x][y])))
941 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
944 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
947 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
950 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
952 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
954 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
956 #define CE_ENTER_FIELD_COND(e, x, y) \
957 (!IS_PLAYER(x, y) && \
958 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
960 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
963 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
964 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
966 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
967 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
968 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
969 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
971 /* game button identifiers */
972 #define GAME_CTRL_ID_STOP 0
973 #define GAME_CTRL_ID_PAUSE 1
974 #define GAME_CTRL_ID_PLAY 2
975 #define GAME_CTRL_ID_UNDO 3
976 #define GAME_CTRL_ID_REDO 4
977 #define GAME_CTRL_ID_SAVE 5
978 #define GAME_CTRL_ID_PAUSE2 6
979 #define GAME_CTRL_ID_LOAD 7
980 #define SOUND_CTRL_ID_MUSIC 8
981 #define SOUND_CTRL_ID_LOOPS 9
982 #define SOUND_CTRL_ID_SIMPLE 10
984 #define NUM_GAME_BUTTONS 11
987 /* forward declaration for internal use */
989 static void CreateField(int, int, int);
991 static void ResetGfxAnimation(int, int);
993 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
994 static void AdvanceFrameAndPlayerCounters(int);
996 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
997 static boolean MovePlayer(struct PlayerInfo *, int, int);
998 static void ScrollPlayer(struct PlayerInfo *, int);
999 static void ScrollScreen(struct PlayerInfo *, int);
1001 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1002 static boolean DigFieldByCE(int, int, int);
1003 static boolean SnapField(struct PlayerInfo *, int, int);
1004 static boolean DropElement(struct PlayerInfo *);
1006 static void InitBeltMovement(void);
1007 static void CloseAllOpenTimegates(void);
1008 static void CheckGravityMovement(struct PlayerInfo *);
1009 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1010 static void KillPlayerUnlessEnemyProtected(int, int);
1011 static void KillPlayerUnlessExplosionProtected(int, int);
1013 static void TestIfPlayerTouchesCustomElement(int, int);
1014 static void TestIfElementTouchesCustomElement(int, int);
1015 static void TestIfElementHitsCustomElement(int, int, int);
1017 static void HandleElementChange(int, int, int);
1018 static void ExecuteCustomElementAction(int, int, int, int);
1019 static boolean ChangeElement(int, int, int, int);
1021 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1022 #define CheckTriggeredElementChange(x, y, e, ev) \
1023 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1024 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1025 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1026 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1027 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1028 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1029 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1031 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1032 #define CheckElementChange(x, y, e, te, ev) \
1033 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1034 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1035 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1036 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1037 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1039 static void PlayLevelSound(int, int, int);
1040 static void PlayLevelSoundNearest(int, int, int);
1041 static void PlayLevelSoundAction(int, int, int);
1042 static void PlayLevelSoundElementAction(int, int, int, int);
1043 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1044 static void PlayLevelSoundActionIfLoop(int, int, int);
1045 static void StopLevelSoundActionIfLoop(int, int, int);
1046 static void PlayLevelMusic();
1048 static void HandleGameButtons(struct GadgetInfo *);
1050 int AmoebeNachbarNr(int, int);
1051 void AmoebeUmwandeln(int, int);
1052 void ContinueMoving(int, int);
1053 void Bang(int, int);
1054 void InitMovDir(int, int);
1055 void InitAmoebaNr(int, int);
1056 int NewHiScore(void);
1058 void TestIfGoodThingHitsBadThing(int, int, int);
1059 void TestIfBadThingHitsGoodThing(int, int, int);
1060 void TestIfPlayerTouchesBadThing(int, int);
1061 void TestIfPlayerRunsIntoBadThing(int, int, int);
1062 void TestIfBadThingTouchesPlayer(int, int);
1063 void TestIfBadThingRunsIntoPlayer(int, int, int);
1064 void TestIfFriendTouchesBadThing(int, int);
1065 void TestIfBadThingTouchesFriend(int, int);
1066 void TestIfBadThingTouchesOtherBadThing(int, int);
1067 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1069 void KillPlayer(struct PlayerInfo *);
1070 void BuryPlayer(struct PlayerInfo *);
1071 void RemovePlayer(struct PlayerInfo *);
1073 static int getInvisibleActiveFromInvisibleElement(int);
1074 static int getInvisibleFromInvisibleActiveElement(int);
1076 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1078 /* for detection of endless loops, caused by custom element programming */
1079 /* (using maximal playfield width x 10 is just a rough approximation) */
1080 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1082 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1084 if (recursion_loop_detected) \
1087 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1089 recursion_loop_detected = TRUE; \
1090 recursion_loop_element = (e); \
1093 recursion_loop_depth++; \
1096 #define RECURSION_LOOP_DETECTION_END() \
1098 recursion_loop_depth--; \
1101 static int recursion_loop_depth;
1102 static boolean recursion_loop_detected;
1103 static boolean recursion_loop_element;
1105 static int map_player_action[MAX_PLAYERS];
1108 /* ------------------------------------------------------------------------- */
1109 /* definition of elements that automatically change to other elements after */
1110 /* a specified time, eventually calling a function when changing */
1111 /* ------------------------------------------------------------------------- */
1113 /* forward declaration for changer functions */
1114 static void InitBuggyBase(int, int);
1115 static void WarnBuggyBase(int, int);
1117 static void InitTrap(int, int);
1118 static void ActivateTrap(int, int);
1119 static void ChangeActiveTrap(int, int);
1121 static void InitRobotWheel(int, int);
1122 static void RunRobotWheel(int, int);
1123 static void StopRobotWheel(int, int);
1125 static void InitTimegateWheel(int, int);
1126 static void RunTimegateWheel(int, int);
1128 static void InitMagicBallDelay(int, int);
1129 static void ActivateMagicBall(int, int);
1131 struct ChangingElementInfo
1136 void (*pre_change_function)(int x, int y);
1137 void (*change_function)(int x, int y);
1138 void (*post_change_function)(int x, int y);
1141 static struct ChangingElementInfo change_delay_list[] =
1176 EL_STEEL_EXIT_OPENING,
1184 EL_STEEL_EXIT_CLOSING,
1185 EL_STEEL_EXIT_CLOSED,
1208 EL_EM_STEEL_EXIT_OPENING,
1209 EL_EM_STEEL_EXIT_OPEN,
1216 EL_EM_STEEL_EXIT_CLOSING,
1240 EL_SWITCHGATE_OPENING,
1248 EL_SWITCHGATE_CLOSING,
1249 EL_SWITCHGATE_CLOSED,
1256 EL_TIMEGATE_OPENING,
1264 EL_TIMEGATE_CLOSING,
1273 EL_ACID_SPLASH_LEFT,
1281 EL_ACID_SPLASH_RIGHT,
1290 EL_SP_BUGGY_BASE_ACTIVATING,
1297 EL_SP_BUGGY_BASE_ACTIVATING,
1298 EL_SP_BUGGY_BASE_ACTIVE,
1305 EL_SP_BUGGY_BASE_ACTIVE,
1329 EL_ROBOT_WHEEL_ACTIVE,
1337 EL_TIMEGATE_SWITCH_ACTIVE,
1345 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1346 EL_DC_TIMEGATE_SWITCH,
1353 EL_EMC_MAGIC_BALL_ACTIVE,
1354 EL_EMC_MAGIC_BALL_ACTIVE,
1361 EL_EMC_SPRING_BUMPER_ACTIVE,
1362 EL_EMC_SPRING_BUMPER,
1369 EL_DIAGONAL_SHRINKING,
1377 EL_DIAGONAL_GROWING,
1398 int push_delay_fixed, push_delay_random;
1402 { EL_SPRING, 0, 0 },
1403 { EL_BALLOON, 0, 0 },
1405 { EL_SOKOBAN_OBJECT, 2, 0 },
1406 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1407 { EL_SATELLITE, 2, 0 },
1408 { EL_SP_DISK_YELLOW, 2, 0 },
1410 { EL_UNDEFINED, 0, 0 },
1418 move_stepsize_list[] =
1420 { EL_AMOEBA_DROP, 2 },
1421 { EL_AMOEBA_DROPPING, 2 },
1422 { EL_QUICKSAND_FILLING, 1 },
1423 { EL_QUICKSAND_EMPTYING, 1 },
1424 { EL_QUICKSAND_FAST_FILLING, 2 },
1425 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1426 { EL_MAGIC_WALL_FILLING, 2 },
1427 { EL_MAGIC_WALL_EMPTYING, 2 },
1428 { EL_BD_MAGIC_WALL_FILLING, 2 },
1429 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1430 { EL_DC_MAGIC_WALL_FILLING, 2 },
1431 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1433 { EL_UNDEFINED, 0 },
1441 collect_count_list[] =
1444 { EL_BD_DIAMOND, 1 },
1445 { EL_EMERALD_YELLOW, 1 },
1446 { EL_EMERALD_RED, 1 },
1447 { EL_EMERALD_PURPLE, 1 },
1449 { EL_SP_INFOTRON, 1 },
1453 { EL_UNDEFINED, 0 },
1461 access_direction_list[] =
1463 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1464 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1465 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1466 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1467 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1468 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1469 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1470 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1471 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1472 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1473 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1475 { EL_SP_PORT_LEFT, MV_RIGHT },
1476 { EL_SP_PORT_RIGHT, MV_LEFT },
1477 { EL_SP_PORT_UP, MV_DOWN },
1478 { EL_SP_PORT_DOWN, MV_UP },
1479 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1480 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1481 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1482 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1483 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1484 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1485 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1486 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1487 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1488 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1489 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1490 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1491 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1492 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1493 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1495 { EL_UNDEFINED, MV_NONE }
1498 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1500 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1501 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1502 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1503 IS_JUST_CHANGING(x, y))
1505 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1507 /* static variables for playfield scan mode (scanning forward or backward) */
1508 static int playfield_scan_start_x = 0;
1509 static int playfield_scan_start_y = 0;
1510 static int playfield_scan_delta_x = 1;
1511 static int playfield_scan_delta_y = 1;
1513 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1514 (y) >= 0 && (y) <= lev_fieldy - 1; \
1515 (y) += playfield_scan_delta_y) \
1516 for ((x) = playfield_scan_start_x; \
1517 (x) >= 0 && (x) <= lev_fieldx - 1; \
1518 (x) += playfield_scan_delta_x)
1521 void DEBUG_SetMaximumDynamite()
1525 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1526 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1527 local_player->inventory_element[local_player->inventory_size++] =
1532 static void InitPlayfieldScanModeVars()
1534 if (game.use_reverse_scan_direction)
1536 playfield_scan_start_x = lev_fieldx - 1;
1537 playfield_scan_start_y = lev_fieldy - 1;
1539 playfield_scan_delta_x = -1;
1540 playfield_scan_delta_y = -1;
1544 playfield_scan_start_x = 0;
1545 playfield_scan_start_y = 0;
1547 playfield_scan_delta_x = 1;
1548 playfield_scan_delta_y = 1;
1552 static void InitPlayfieldScanMode(int mode)
1554 game.use_reverse_scan_direction =
1555 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1557 InitPlayfieldScanModeVars();
1560 static int get_move_delay_from_stepsize(int move_stepsize)
1563 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1565 /* make sure that stepsize value is always a power of 2 */
1566 move_stepsize = (1 << log_2(move_stepsize));
1568 return TILEX / move_stepsize;
1571 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1574 int player_nr = player->index_nr;
1575 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1576 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1578 /* do no immediately change move delay -- the player might just be moving */
1579 player->move_delay_value_next = move_delay;
1581 /* information if player can move must be set separately */
1582 player->cannot_move = cannot_move;
1586 player->move_delay = game.initial_move_delay[player_nr];
1587 player->move_delay_value = game.initial_move_delay_value[player_nr];
1589 player->move_delay_value_next = -1;
1591 player->move_delay_reset_counter = 0;
1595 void GetPlayerConfig()
1597 GameFrameDelay = setup.game_frame_delay;
1599 if (!audio.sound_available)
1600 setup.sound_simple = FALSE;
1602 if (!audio.loops_available)
1603 setup.sound_loops = FALSE;
1605 if (!audio.music_available)
1606 setup.sound_music = FALSE;
1608 if (!video.fullscreen_available)
1609 setup.fullscreen = FALSE;
1611 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1613 SetAudioMode(setup.sound);
1617 int GetElementFromGroupElement(int element)
1619 if (IS_GROUP_ELEMENT(element))
1621 struct ElementGroupInfo *group = element_info[element].group;
1622 int last_anim_random_frame = gfx.anim_random_frame;
1625 if (group->choice_mode == ANIM_RANDOM)
1626 gfx.anim_random_frame = RND(group->num_elements_resolved);
1628 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1629 group->choice_mode, 0,
1632 if (group->choice_mode == ANIM_RANDOM)
1633 gfx.anim_random_frame = last_anim_random_frame;
1635 group->choice_pos++;
1637 element = group->element_resolved[element_pos];
1643 static void InitPlayerField(int x, int y, int element, boolean init_game)
1645 if (element == EL_SP_MURPHY)
1649 if (stored_player[0].present)
1651 Feld[x][y] = EL_SP_MURPHY_CLONE;
1657 stored_player[0].initial_element = element;
1658 stored_player[0].use_murphy = TRUE;
1660 if (!level.use_artwork_element[0])
1661 stored_player[0].artwork_element = EL_SP_MURPHY;
1664 Feld[x][y] = EL_PLAYER_1;
1670 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1671 int jx = player->jx, jy = player->jy;
1673 player->present = TRUE;
1675 player->block_last_field = (element == EL_SP_MURPHY ?
1676 level.sp_block_last_field :
1677 level.block_last_field);
1679 /* ---------- initialize player's last field block delay --------------- */
1681 /* always start with reliable default value (no adjustment needed) */
1682 player->block_delay_adjustment = 0;
1684 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1685 if (player->block_last_field && element == EL_SP_MURPHY)
1686 player->block_delay_adjustment = 1;
1688 /* special case 2: in game engines before 3.1.1, blocking was different */
1689 if (game.use_block_last_field_bug)
1690 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1692 if (!options.network || player->connected)
1694 player->active = TRUE;
1696 /* remove potentially duplicate players */
1697 if (StorePlayer[jx][jy] == Feld[x][y])
1698 StorePlayer[jx][jy] = 0;
1700 StorePlayer[x][y] = Feld[x][y];
1702 #if DEBUG_INIT_PLAYER
1705 printf("- player element %d activated", player->element_nr);
1706 printf(" (local player is %d and currently %s)\n",
1707 local_player->element_nr,
1708 local_player->active ? "active" : "not active");
1713 Feld[x][y] = EL_EMPTY;
1715 player->jx = player->last_jx = x;
1716 player->jy = player->last_jy = y;
1721 int player_nr = GET_PLAYER_NR(element);
1722 struct PlayerInfo *player = &stored_player[player_nr];
1724 if (player->active && player->killed)
1725 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1729 static void InitField(int x, int y, boolean init_game)
1731 int element = Feld[x][y];
1740 InitPlayerField(x, y, element, init_game);
1743 case EL_SOKOBAN_FIELD_PLAYER:
1744 element = Feld[x][y] = EL_PLAYER_1;
1745 InitField(x, y, init_game);
1747 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1748 InitField(x, y, init_game);
1751 case EL_SOKOBAN_FIELD_EMPTY:
1752 local_player->sokobanfields_still_needed++;
1756 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1757 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1758 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1759 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1760 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1761 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1762 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1763 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1764 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1765 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1774 case EL_SPACESHIP_RIGHT:
1775 case EL_SPACESHIP_UP:
1776 case EL_SPACESHIP_LEFT:
1777 case EL_SPACESHIP_DOWN:
1778 case EL_BD_BUTTERFLY:
1779 case EL_BD_BUTTERFLY_RIGHT:
1780 case EL_BD_BUTTERFLY_UP:
1781 case EL_BD_BUTTERFLY_LEFT:
1782 case EL_BD_BUTTERFLY_DOWN:
1784 case EL_BD_FIREFLY_RIGHT:
1785 case EL_BD_FIREFLY_UP:
1786 case EL_BD_FIREFLY_LEFT:
1787 case EL_BD_FIREFLY_DOWN:
1788 case EL_PACMAN_RIGHT:
1790 case EL_PACMAN_LEFT:
1791 case EL_PACMAN_DOWN:
1793 case EL_YAMYAM_LEFT:
1794 case EL_YAMYAM_RIGHT:
1796 case EL_YAMYAM_DOWN:
1797 case EL_DARK_YAMYAM:
1800 case EL_SP_SNIKSNAK:
1801 case EL_SP_ELECTRON:
1810 case EL_AMOEBA_FULL:
1815 case EL_AMOEBA_DROP:
1816 if (y == lev_fieldy - 1)
1818 Feld[x][y] = EL_AMOEBA_GROWING;
1819 Store[x][y] = EL_AMOEBA_WET;
1823 case EL_DYNAMITE_ACTIVE:
1824 case EL_SP_DISK_RED_ACTIVE:
1825 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1826 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1827 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1828 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1829 MovDelay[x][y] = 96;
1832 case EL_EM_DYNAMITE_ACTIVE:
1833 MovDelay[x][y] = 32;
1837 local_player->lights_still_needed++;
1841 local_player->friends_still_needed++;
1846 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1849 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1850 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1851 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1852 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1853 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1854 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1855 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1856 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1857 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1858 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1859 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1860 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1863 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1864 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1865 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1867 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1869 game.belt_dir[belt_nr] = belt_dir;
1870 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1872 else /* more than one switch -- set it like the first switch */
1874 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1879 case EL_LIGHT_SWITCH_ACTIVE:
1881 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1884 case EL_INVISIBLE_STEELWALL:
1885 case EL_INVISIBLE_WALL:
1886 case EL_INVISIBLE_SAND:
1887 if (game.light_time_left > 0 ||
1888 game.lenses_time_left > 0)
1889 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1892 case EL_EMC_MAGIC_BALL:
1893 if (game.ball_state)
1894 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1897 case EL_EMC_MAGIC_BALL_SWITCH:
1898 if (game.ball_state)
1899 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1902 case EL_TRIGGER_PLAYER:
1903 case EL_TRIGGER_ELEMENT:
1904 case EL_TRIGGER_CE_VALUE:
1905 case EL_TRIGGER_CE_SCORE:
1907 case EL_ANY_ELEMENT:
1908 case EL_CURRENT_CE_VALUE:
1909 case EL_CURRENT_CE_SCORE:
1926 /* reference elements should not be used on the playfield */
1927 Feld[x][y] = EL_EMPTY;
1931 if (IS_CUSTOM_ELEMENT(element))
1933 if (CAN_MOVE(element))
1936 if (!element_info[element].use_last_ce_value || init_game)
1937 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1939 else if (IS_GROUP_ELEMENT(element))
1941 Feld[x][y] = GetElementFromGroupElement(element);
1943 InitField(x, y, init_game);
1950 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1953 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1955 InitField(x, y, init_game);
1957 /* not needed to call InitMovDir() -- already done by InitField()! */
1958 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1959 CAN_MOVE(Feld[x][y]))
1963 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1965 int old_element = Feld[x][y];
1967 InitField(x, y, init_game);
1969 /* not needed to call InitMovDir() -- already done by InitField()! */
1970 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971 CAN_MOVE(old_element) &&
1972 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1975 /* this case is in fact a combination of not less than three bugs:
1976 first, it calls InitMovDir() for elements that can move, although this is
1977 already done by InitField(); then, it checks the element that was at this
1978 field _before_ the call to InitField() (which can change it); lastly, it
1979 was not called for "mole with direction" elements, which were treated as
1980 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1984 static int get_key_element_from_nr(int key_nr)
1986 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1987 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1988 EL_EM_KEY_1 : EL_KEY_1);
1990 return key_base_element + key_nr;
1993 static int get_next_dropped_element(struct PlayerInfo *player)
1995 return (player->inventory_size > 0 ?
1996 player->inventory_element[player->inventory_size - 1] :
1997 player->inventory_infinite_element != EL_UNDEFINED ?
1998 player->inventory_infinite_element :
1999 player->dynabombs_left > 0 ?
2000 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2004 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2006 /* pos >= 0: get element from bottom of the stack;
2007 pos < 0: get element from top of the stack */
2011 int min_inventory_size = -pos;
2012 int inventory_pos = player->inventory_size - min_inventory_size;
2013 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2015 return (player->inventory_size >= min_inventory_size ?
2016 player->inventory_element[inventory_pos] :
2017 player->inventory_infinite_element != EL_UNDEFINED ?
2018 player->inventory_infinite_element :
2019 player->dynabombs_left >= min_dynabombs_left ?
2020 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2025 int min_dynabombs_left = pos + 1;
2026 int min_inventory_size = pos + 1 - player->dynabombs_left;
2027 int inventory_pos = pos - player->dynabombs_left;
2029 return (player->inventory_infinite_element != EL_UNDEFINED ?
2030 player->inventory_infinite_element :
2031 player->dynabombs_left >= min_dynabombs_left ?
2032 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2033 player->inventory_size >= min_inventory_size ?
2034 player->inventory_element[inventory_pos] :
2039 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2041 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2042 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2045 if (gpo1->sort_priority != gpo2->sort_priority)
2046 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2048 compare_result = gpo1->nr - gpo2->nr;
2050 return compare_result;
2053 int getPlayerInventorySize(int player_nr)
2055 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2056 return level.native_em_level->ply[player_nr]->dynamite;
2057 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2058 return level.native_sp_level->game_sp->red_disk_count;
2060 return stored_player[player_nr].inventory_size;
2063 void InitGameControlValues()
2067 for (i = 0; game_panel_controls[i].nr != -1; i++)
2069 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2070 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2071 struct TextPosInfo *pos = gpc->pos;
2073 int type = gpc->type;
2077 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2078 Error(ERR_EXIT, "this should not happen -- please debug");
2081 /* force update of game controls after initialization */
2082 gpc->value = gpc->last_value = -1;
2083 gpc->frame = gpc->last_frame = -1;
2084 gpc->gfx_frame = -1;
2086 /* determine panel value width for later calculation of alignment */
2087 if (type == TYPE_INTEGER || type == TYPE_STRING)
2089 pos->width = pos->size * getFontWidth(pos->font);
2090 pos->height = getFontHeight(pos->font);
2092 else if (type == TYPE_ELEMENT)
2094 pos->width = pos->size;
2095 pos->height = pos->size;
2098 /* fill structure for game panel draw order */
2100 gpo->sort_priority = pos->sort_priority;
2103 /* sort game panel controls according to sort_priority and control number */
2104 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2105 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2108 void UpdatePlayfieldElementCount()
2110 boolean use_element_count = FALSE;
2113 /* first check if it is needed at all to calculate playfield element count */
2114 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2115 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2116 use_element_count = TRUE;
2118 if (!use_element_count)
2121 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2122 element_info[i].element_count = 0;
2124 SCAN_PLAYFIELD(x, y)
2126 element_info[Feld[x][y]].element_count++;
2129 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2130 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2131 if (IS_IN_GROUP(j, i))
2132 element_info[EL_GROUP_START + i].element_count +=
2133 element_info[j].element_count;
2136 void UpdateGameControlValues()
2139 int time = (local_player->LevelSolved ?
2140 local_player->LevelSolved_CountingTime :
2141 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142 level.native_em_level->lev->time :
2143 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144 level.native_sp_level->game_sp->time_played :
2145 game.no_time_limit ? TimePlayed : TimeLeft);
2146 int score = (local_player->LevelSolved ?
2147 local_player->LevelSolved_CountingScore :
2148 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149 level.native_em_level->lev->score :
2150 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151 level.native_sp_level->game_sp->score :
2152 local_player->score);
2153 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2154 level.native_em_level->lev->required :
2155 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2156 level.native_sp_level->game_sp->infotrons_still_needed :
2157 local_player->gems_still_needed);
2158 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2159 level.native_em_level->lev->required > 0 :
2160 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2161 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2162 local_player->gems_still_needed > 0 ||
2163 local_player->sokobanfields_still_needed > 0 ||
2164 local_player->lights_still_needed > 0);
2166 UpdatePlayfieldElementCount();
2168 /* update game panel control values */
2170 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2171 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2173 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2174 for (i = 0; i < MAX_NUM_KEYS; i++)
2175 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2176 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2177 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2179 if (game.centered_player_nr == -1)
2181 for (i = 0; i < MAX_PLAYERS; i++)
2183 /* only one player in Supaplex game engine */
2184 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2187 for (k = 0; k < MAX_NUM_KEYS; k++)
2189 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2191 if (level.native_em_level->ply[i]->keys & (1 << k))
2192 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2193 get_key_element_from_nr(k);
2195 else if (stored_player[i].key[k])
2196 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2197 get_key_element_from_nr(k);
2200 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2201 getPlayerInventorySize(i);
2203 if (stored_player[i].num_white_keys > 0)
2204 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2207 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2208 stored_player[i].num_white_keys;
2213 int player_nr = game.centered_player_nr;
2215 for (k = 0; k < MAX_NUM_KEYS; k++)
2217 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2219 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2220 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221 get_key_element_from_nr(k);
2223 else if (stored_player[player_nr].key[k])
2224 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2225 get_key_element_from_nr(k);
2228 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229 getPlayerInventorySize(player_nr);
2231 if (stored_player[player_nr].num_white_keys > 0)
2232 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2234 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2235 stored_player[player_nr].num_white_keys;
2238 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2240 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2241 get_inventory_element_from_pos(local_player, i);
2242 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2243 get_inventory_element_from_pos(local_player, -i - 1);
2246 game_panel_controls[GAME_PANEL_SCORE].value = score;
2247 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2249 game_panel_controls[GAME_PANEL_TIME].value = time;
2251 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2252 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2253 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2255 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2257 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2258 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2260 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2261 local_player->shield_normal_time_left;
2262 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2263 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2265 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2266 local_player->shield_deadly_time_left;
2268 game_panel_controls[GAME_PANEL_EXIT].value =
2269 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2271 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2272 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2273 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2274 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2275 EL_EMC_MAGIC_BALL_SWITCH);
2277 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2278 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2279 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2280 game.light_time_left;
2282 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2283 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2284 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2285 game.timegate_time_left;
2287 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2288 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2290 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2291 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2292 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2293 game.lenses_time_left;
2295 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2296 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2297 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2298 game.magnify_time_left;
2300 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2301 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2302 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2303 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2304 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2305 EL_BALLOON_SWITCH_NONE);
2307 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2308 local_player->dynabomb_count;
2309 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2310 local_player->dynabomb_size;
2311 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2312 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2314 game_panel_controls[GAME_PANEL_PENGUINS].value =
2315 local_player->friends_still_needed;
2317 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2318 local_player->sokobanfields_still_needed;
2319 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2320 local_player->sokobanfields_still_needed;
2322 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2323 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2325 for (i = 0; i < NUM_BELTS; i++)
2327 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2328 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2329 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2330 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2331 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2334 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2335 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2336 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2337 game.magic_wall_time_left;
2339 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2340 local_player->gravity;
2342 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2343 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2345 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2346 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2347 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2348 game.panel.element[i].id : EL_UNDEFINED);
2350 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2351 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2352 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2353 element_info[game.panel.element_count[i].id].element_count : 0);
2355 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2356 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2357 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2358 element_info[game.panel.ce_score[i].id].collect_score : 0);
2360 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2361 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2362 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2363 element_info[game.panel.ce_score_element[i].id].collect_score :
2366 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2367 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2368 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2370 /* update game panel control frames */
2372 for (i = 0; game_panel_controls[i].nr != -1; i++)
2374 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2376 if (gpc->type == TYPE_ELEMENT)
2378 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2380 int last_anim_random_frame = gfx.anim_random_frame;
2381 int element = gpc->value;
2382 int graphic = el2panelimg(element);
2384 if (gpc->value != gpc->last_value)
2387 gpc->gfx_random = INIT_GFX_RANDOM();
2393 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2394 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2395 gpc->gfx_random = INIT_GFX_RANDOM();
2398 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2399 gfx.anim_random_frame = gpc->gfx_random;
2401 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2402 gpc->gfx_frame = element_info[element].collect_score;
2404 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2407 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2408 gfx.anim_random_frame = last_anim_random_frame;
2414 void DisplayGameControlValues()
2416 boolean redraw_panel = FALSE;
2419 for (i = 0; game_panel_controls[i].nr != -1; i++)
2421 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2423 if (PANEL_DEACTIVATED(gpc->pos))
2426 if (gpc->value == gpc->last_value &&
2427 gpc->frame == gpc->last_frame)
2430 redraw_panel = TRUE;
2436 /* copy default game door content to main double buffer */
2438 /* !!! CHECK AGAIN !!! */
2439 SetPanelBackground();
2440 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2441 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2443 /* redraw game control buttons */
2444 RedrawGameButtons();
2446 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2448 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2450 int nr = game_panel_order[i].nr;
2451 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2452 struct TextPosInfo *pos = gpc->pos;
2453 int type = gpc->type;
2454 int value = gpc->value;
2455 int frame = gpc->frame;
2456 int size = pos->size;
2457 int font = pos->font;
2458 boolean draw_masked = pos->draw_masked;
2459 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2461 if (PANEL_DEACTIVATED(pos))
2464 gpc->last_value = value;
2465 gpc->last_frame = frame;
2467 if (type == TYPE_INTEGER)
2469 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2470 nr == GAME_PANEL_TIME)
2472 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2474 if (use_dynamic_size) /* use dynamic number of digits */
2476 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2477 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2478 int size2 = size1 + 1;
2479 int font1 = pos->font;
2480 int font2 = pos->font_alt;
2482 size = (value < value_change ? size1 : size2);
2483 font = (value < value_change ? font1 : font2);
2487 /* correct text size if "digits" is zero or less */
2489 size = strlen(int2str(value, size));
2491 /* dynamically correct text alignment */
2492 pos->width = size * getFontWidth(font);
2494 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2495 int2str(value, size), font, mask_mode);
2497 else if (type == TYPE_ELEMENT)
2499 int element, graphic;
2503 int dst_x = PANEL_XPOS(pos);
2504 int dst_y = PANEL_YPOS(pos);
2506 if (value != EL_UNDEFINED && value != EL_EMPTY)
2509 graphic = el2panelimg(value);
2511 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2513 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2516 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2519 width = graphic_info[graphic].width * size / TILESIZE;
2520 height = graphic_info[graphic].height * size / TILESIZE;
2523 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2526 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530 else if (type == TYPE_STRING)
2532 boolean active = (value != 0);
2533 char *state_normal = "off";
2534 char *state_active = "on";
2535 char *state = (active ? state_active : state_normal);
2536 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2537 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2538 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2539 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2541 if (nr == GAME_PANEL_GRAVITY_STATE)
2543 int font1 = pos->font; /* (used for normal state) */
2544 int font2 = pos->font_alt; /* (used for active state) */
2546 font = (active ? font2 : font1);
2555 /* don't truncate output if "chars" is zero or less */
2558 /* dynamically correct text alignment */
2559 pos->width = size * getFontWidth(font);
2562 s_cut = getStringCopyN(s, size);
2564 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2565 s_cut, font, mask_mode);
2571 redraw_mask |= REDRAW_DOOR_1;
2574 SetGameStatus(GAME_MODE_PLAYING);
2577 void UpdateAndDisplayGameControlValues()
2579 if (tape.deactivate_display)
2582 UpdateGameControlValues();
2583 DisplayGameControlValues();
2586 void UpdateGameDoorValues()
2588 UpdateGameControlValues();
2591 void DrawGameDoorValues()
2593 DisplayGameControlValues();
2598 =============================================================================
2600 -----------------------------------------------------------------------------
2601 initialize game engine due to level / tape version number
2602 =============================================================================
2605 static void InitGameEngine()
2607 int i, j, k, l, x, y;
2609 /* set game engine from tape file when re-playing, else from level file */
2610 game.engine_version = (tape.playing ? tape.engine_version :
2611 level.game_version);
2613 /* set single or multi-player game mode (needed for re-playing tapes) */
2614 game.team_mode = setup.team_mode;
2618 int num_players = 0;
2620 for (i = 0; i < MAX_PLAYERS; i++)
2621 if (tape.player_participates[i])
2624 /* multi-player tapes contain input data for more than one player */
2625 game.team_mode = (num_players > 1);
2628 /* ---------------------------------------------------------------------- */
2629 /* set flags for bugs and changes according to active game engine version */
2630 /* ---------------------------------------------------------------------- */
2633 Summary of bugfix/change:
2634 Fixed handling for custom elements that change when pushed by the player.
2636 Fixed/changed in version:
2640 Before 3.1.0, custom elements that "change when pushing" changed directly
2641 after the player started pushing them (until then handled in "DigField()").
2642 Since 3.1.0, these custom elements are not changed until the "pushing"
2643 move of the element is finished (now handled in "ContinueMoving()").
2645 Affected levels/tapes:
2646 The first condition is generally needed for all levels/tapes before version
2647 3.1.0, which might use the old behaviour before it was changed; known tapes
2648 that are affected are some tapes from the level set "Walpurgis Gardens" by
2650 The second condition is an exception from the above case and is needed for
2651 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2652 above (including some development versions of 3.1.0), but before it was
2653 known that this change would break tapes like the above and was fixed in
2654 3.1.1, so that the changed behaviour was active although the engine version
2655 while recording maybe was before 3.1.0. There is at least one tape that is
2656 affected by this exception, which is the tape for the one-level set "Bug
2657 Machine" by Juergen Bonhagen.
2660 game.use_change_when_pushing_bug =
2661 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2663 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2664 tape.game_version < VERSION_IDENT(3,1,1,0)));
2667 Summary of bugfix/change:
2668 Fixed handling for blocking the field the player leaves when moving.
2670 Fixed/changed in version:
2674 Before 3.1.1, when "block last field when moving" was enabled, the field
2675 the player is leaving when moving was blocked for the time of the move,
2676 and was directly unblocked afterwards. This resulted in the last field
2677 being blocked for exactly one less than the number of frames of one player
2678 move. Additionally, even when blocking was disabled, the last field was
2679 blocked for exactly one frame.
2680 Since 3.1.1, due to changes in player movement handling, the last field
2681 is not blocked at all when blocking is disabled. When blocking is enabled,
2682 the last field is blocked for exactly the number of frames of one player
2683 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2684 last field is blocked for exactly one more than the number of frames of
2687 Affected levels/tapes:
2688 (!!! yet to be determined -- probably many !!!)
2691 game.use_block_last_field_bug =
2692 (game.engine_version < VERSION_IDENT(3,1,1,0));
2694 /* ---------------------------------------------------------------------- */
2696 /* set maximal allowed number of custom element changes per game frame */
2697 game.max_num_changes_per_frame = 1;
2699 /* default scan direction: scan playfield from top/left to bottom/right */
2700 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2702 /* dynamically adjust element properties according to game engine version */
2703 InitElementPropertiesEngine(game.engine_version);
2706 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2707 printf(" tape version == %06d [%s] [file: %06d]\n",
2708 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2710 printf(" => game.engine_version == %06d\n", game.engine_version);
2713 /* ---------- initialize player's initial move delay --------------------- */
2715 /* dynamically adjust player properties according to level information */
2716 for (i = 0; i < MAX_PLAYERS; i++)
2717 game.initial_move_delay_value[i] =
2718 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2720 /* dynamically adjust player properties according to game engine version */
2721 for (i = 0; i < MAX_PLAYERS; i++)
2722 game.initial_move_delay[i] =
2723 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2724 game.initial_move_delay_value[i] : 0);
2726 /* ---------- initialize player's initial push delay --------------------- */
2728 /* dynamically adjust player properties according to game engine version */
2729 game.initial_push_delay_value =
2730 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2732 /* ---------- initialize changing elements ------------------------------- */
2734 /* initialize changing elements information */
2735 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2737 struct ElementInfo *ei = &element_info[i];
2739 /* this pointer might have been changed in the level editor */
2740 ei->change = &ei->change_page[0];
2742 if (!IS_CUSTOM_ELEMENT(i))
2744 ei->change->target_element = EL_EMPTY_SPACE;
2745 ei->change->delay_fixed = 0;
2746 ei->change->delay_random = 0;
2747 ei->change->delay_frames = 1;
2750 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2752 ei->has_change_event[j] = FALSE;
2754 ei->event_page_nr[j] = 0;
2755 ei->event_page[j] = &ei->change_page[0];
2759 /* add changing elements from pre-defined list */
2760 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2762 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2763 struct ElementInfo *ei = &element_info[ch_delay->element];
2765 ei->change->target_element = ch_delay->target_element;
2766 ei->change->delay_fixed = ch_delay->change_delay;
2768 ei->change->pre_change_function = ch_delay->pre_change_function;
2769 ei->change->change_function = ch_delay->change_function;
2770 ei->change->post_change_function = ch_delay->post_change_function;
2772 ei->change->can_change = TRUE;
2773 ei->change->can_change_or_has_action = TRUE;
2775 ei->has_change_event[CE_DELAY] = TRUE;
2777 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2778 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2781 /* ---------- initialize internal run-time variables --------------------- */
2783 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2785 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2787 for (j = 0; j < ei->num_change_pages; j++)
2789 ei->change_page[j].can_change_or_has_action =
2790 (ei->change_page[j].can_change |
2791 ei->change_page[j].has_action);
2795 /* add change events from custom element configuration */
2796 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2798 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2800 for (j = 0; j < ei->num_change_pages; j++)
2802 if (!ei->change_page[j].can_change_or_has_action)
2805 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2807 /* only add event page for the first page found with this event */
2808 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2810 ei->has_change_event[k] = TRUE;
2812 ei->event_page_nr[k] = j;
2813 ei->event_page[k] = &ei->change_page[j];
2819 /* ---------- initialize reference elements in change conditions --------- */
2821 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2823 int element = EL_CUSTOM_START + i;
2824 struct ElementInfo *ei = &element_info[element];
2826 for (j = 0; j < ei->num_change_pages; j++)
2828 int trigger_element = ei->change_page[j].initial_trigger_element;
2830 if (trigger_element >= EL_PREV_CE_8 &&
2831 trigger_element <= EL_NEXT_CE_8)
2832 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2834 ei->change_page[j].trigger_element = trigger_element;
2838 /* ---------- initialize run-time trigger player and element ------------- */
2840 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2842 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2844 for (j = 0; j < ei->num_change_pages; j++)
2846 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2847 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2848 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2849 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2850 ei->change_page[j].actual_trigger_ce_value = 0;
2851 ei->change_page[j].actual_trigger_ce_score = 0;
2855 /* ---------- initialize trigger events ---------------------------------- */
2857 /* initialize trigger events information */
2858 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2859 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2860 trigger_events[i][j] = FALSE;
2862 /* add trigger events from element change event properties */
2863 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2865 struct ElementInfo *ei = &element_info[i];
2867 for (j = 0; j < ei->num_change_pages; j++)
2869 if (!ei->change_page[j].can_change_or_has_action)
2872 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2874 int trigger_element = ei->change_page[j].trigger_element;
2876 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2878 if (ei->change_page[j].has_event[k])
2880 if (IS_GROUP_ELEMENT(trigger_element))
2882 struct ElementGroupInfo *group =
2883 element_info[trigger_element].group;
2885 for (l = 0; l < group->num_elements_resolved; l++)
2886 trigger_events[group->element_resolved[l]][k] = TRUE;
2888 else if (trigger_element == EL_ANY_ELEMENT)
2889 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2890 trigger_events[l][k] = TRUE;
2892 trigger_events[trigger_element][k] = TRUE;
2899 /* ---------- initialize push delay -------------------------------------- */
2901 /* initialize push delay values to default */
2902 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2904 if (!IS_CUSTOM_ELEMENT(i))
2906 /* set default push delay values (corrected since version 3.0.7-1) */
2907 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2909 element_info[i].push_delay_fixed = 2;
2910 element_info[i].push_delay_random = 8;
2914 element_info[i].push_delay_fixed = 8;
2915 element_info[i].push_delay_random = 8;
2920 /* set push delay value for certain elements from pre-defined list */
2921 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2923 int e = push_delay_list[i].element;
2925 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2926 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2929 /* set push delay value for Supaplex elements for newer engine versions */
2930 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2932 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2934 if (IS_SP_ELEMENT(i))
2936 /* set SP push delay to just enough to push under a falling zonk */
2937 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2939 element_info[i].push_delay_fixed = delay;
2940 element_info[i].push_delay_random = 0;
2945 /* ---------- initialize move stepsize ----------------------------------- */
2947 /* initialize move stepsize values to default */
2948 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2949 if (!IS_CUSTOM_ELEMENT(i))
2950 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2952 /* set move stepsize value for certain elements from pre-defined list */
2953 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2955 int e = move_stepsize_list[i].element;
2957 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2960 /* ---------- initialize collect score ----------------------------------- */
2962 /* initialize collect score values for custom elements from initial value */
2963 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2964 if (IS_CUSTOM_ELEMENT(i))
2965 element_info[i].collect_score = element_info[i].collect_score_initial;
2967 /* ---------- initialize collect count ----------------------------------- */
2969 /* initialize collect count values for non-custom elements */
2970 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2971 if (!IS_CUSTOM_ELEMENT(i))
2972 element_info[i].collect_count_initial = 0;
2974 /* add collect count values for all elements from pre-defined list */
2975 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2976 element_info[collect_count_list[i].element].collect_count_initial =
2977 collect_count_list[i].count;
2979 /* ---------- initialize access direction -------------------------------- */
2981 /* initialize access direction values to default (access from every side) */
2982 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2983 if (!IS_CUSTOM_ELEMENT(i))
2984 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2986 /* set access direction value for certain elements from pre-defined list */
2987 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2988 element_info[access_direction_list[i].element].access_direction =
2989 access_direction_list[i].direction;
2991 /* ---------- initialize explosion content ------------------------------- */
2992 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2994 if (IS_CUSTOM_ELEMENT(i))
2997 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2999 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3001 element_info[i].content.e[x][y] =
3002 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3003 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3004 i == EL_PLAYER_3 ? EL_EMERALD :
3005 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3006 i == EL_MOLE ? EL_EMERALD_RED :
3007 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3008 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3009 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3010 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3011 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3012 i == EL_WALL_EMERALD ? EL_EMERALD :
3013 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3014 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3015 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3016 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3017 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3018 i == EL_WALL_PEARL ? EL_PEARL :
3019 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3024 /* ---------- initialize recursion detection ------------------------------ */
3025 recursion_loop_depth = 0;
3026 recursion_loop_detected = FALSE;
3027 recursion_loop_element = EL_UNDEFINED;
3029 /* ---------- initialize graphics engine ---------------------------------- */
3030 game.scroll_delay_value =
3031 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3032 setup.scroll_delay ? setup.scroll_delay_value : 0);
3033 game.scroll_delay_value =
3034 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3036 /* ---------- initialize game engine snapshots ---------------------------- */
3037 for (i = 0; i < MAX_PLAYERS; i++)
3038 game.snapshot.last_action[i] = 0;
3039 game.snapshot.changed_action = FALSE;
3040 game.snapshot.collected_item = FALSE;
3041 game.snapshot.mode =
3042 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3043 SNAPSHOT_MODE_EVERY_STEP :
3044 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3045 SNAPSHOT_MODE_EVERY_MOVE :
3046 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3047 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3048 game.snapshot.save_snapshot = FALSE;
3051 int get_num_special_action(int element, int action_first, int action_last)
3053 int num_special_action = 0;
3056 for (i = action_first; i <= action_last; i++)
3058 boolean found = FALSE;
3060 for (j = 0; j < NUM_DIRECTIONS; j++)
3061 if (el_act_dir2img(element, i, j) !=
3062 el_act_dir2img(element, ACTION_DEFAULT, j))
3066 num_special_action++;
3071 return num_special_action;
3076 =============================================================================
3078 -----------------------------------------------------------------------------
3079 initialize and start new game
3080 =============================================================================
3085 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3086 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3087 int fade_mask = REDRAW_FIELD;
3089 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3090 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3091 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3092 int initial_move_dir = MV_DOWN;
3095 // required here to update video display before fading (FIX THIS)
3096 DrawMaskedBorder(REDRAW_DOOR_2);
3098 if (!game.restart_level)
3099 CloseDoor(DOOR_CLOSE_1);
3101 SetGameStatus(GAME_MODE_PLAYING);
3103 if (level_editor_test_game)
3104 FadeSkipNextFadeIn();
3106 FadeSetEnterScreen();
3108 if (CheckIfGlobalBorderHasChanged())
3109 fade_mask = REDRAW_ALL;
3111 FadeSoundsAndMusic();
3113 ExpireSoundLoops(TRUE);
3117 /* needed if different viewport properties defined for playing */
3118 ChangeViewportPropertiesIfNeeded();
3122 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3124 DrawCompleteVideoDisplay();
3127 InitGameControlValues();
3129 /* don't play tapes over network */
3130 network_playing = (options.network && !tape.playing);
3132 for (i = 0; i < MAX_PLAYERS; i++)
3134 struct PlayerInfo *player = &stored_player[i];
3136 player->index_nr = i;
3137 player->index_bit = (1 << i);
3138 player->element_nr = EL_PLAYER_1 + i;
3140 player->present = FALSE;
3141 player->active = FALSE;
3142 player->mapped = FALSE;
3144 player->killed = FALSE;
3145 player->reanimated = FALSE;
3148 player->effective_action = 0;
3149 player->programmed_action = 0;
3152 player->score_final = 0;
3154 player->gems_still_needed = level.gems_needed;
3155 player->sokobanfields_still_needed = 0;
3156 player->lights_still_needed = 0;
3157 player->friends_still_needed = 0;
3159 for (j = 0; j < MAX_NUM_KEYS; j++)
3160 player->key[j] = FALSE;
3162 player->num_white_keys = 0;
3164 player->dynabomb_count = 0;
3165 player->dynabomb_size = 1;
3166 player->dynabombs_left = 0;
3167 player->dynabomb_xl = FALSE;
3169 player->MovDir = initial_move_dir;
3172 player->GfxDir = initial_move_dir;
3173 player->GfxAction = ACTION_DEFAULT;
3175 player->StepFrame = 0;
3177 player->initial_element = player->element_nr;
3178 player->artwork_element =
3179 (level.use_artwork_element[i] ? level.artwork_element[i] :
3180 player->element_nr);
3181 player->use_murphy = FALSE;
3183 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3184 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3186 player->gravity = level.initial_player_gravity[i];
3188 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3190 player->actual_frame_counter = 0;
3192 player->step_counter = 0;
3194 player->last_move_dir = initial_move_dir;
3196 player->is_active = FALSE;
3198 player->is_waiting = FALSE;
3199 player->is_moving = FALSE;
3200 player->is_auto_moving = FALSE;
3201 player->is_digging = FALSE;
3202 player->is_snapping = FALSE;
3203 player->is_collecting = FALSE;
3204 player->is_pushing = FALSE;
3205 player->is_switching = FALSE;
3206 player->is_dropping = FALSE;
3207 player->is_dropping_pressed = FALSE;
3209 player->is_bored = FALSE;
3210 player->is_sleeping = FALSE;
3212 player->was_waiting = TRUE;
3213 player->was_moving = FALSE;
3214 player->was_snapping = FALSE;
3215 player->was_dropping = FALSE;
3217 player->frame_counter_bored = -1;
3218 player->frame_counter_sleeping = -1;
3220 player->anim_delay_counter = 0;
3221 player->post_delay_counter = 0;
3223 player->dir_waiting = initial_move_dir;
3224 player->action_waiting = ACTION_DEFAULT;
3225 player->last_action_waiting = ACTION_DEFAULT;
3226 player->special_action_bored = ACTION_DEFAULT;
3227 player->special_action_sleeping = ACTION_DEFAULT;
3229 player->switch_x = -1;
3230 player->switch_y = -1;
3232 player->drop_x = -1;
3233 player->drop_y = -1;
3235 player->show_envelope = 0;
3237 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3239 player->push_delay = -1; /* initialized when pushing starts */
3240 player->push_delay_value = game.initial_push_delay_value;
3242 player->drop_delay = 0;
3243 player->drop_pressed_delay = 0;
3245 player->last_jx = -1;
3246 player->last_jy = -1;
3250 player->shield_normal_time_left = 0;
3251 player->shield_deadly_time_left = 0;
3253 player->inventory_infinite_element = EL_UNDEFINED;
3254 player->inventory_size = 0;
3256 if (level.use_initial_inventory[i])
3258 for (j = 0; j < level.initial_inventory_size[i]; j++)
3260 int element = level.initial_inventory_content[i][j];
3261 int collect_count = element_info[element].collect_count_initial;
3264 if (!IS_CUSTOM_ELEMENT(element))
3267 if (collect_count == 0)
3268 player->inventory_infinite_element = element;
3270 for (k = 0; k < collect_count; k++)
3271 if (player->inventory_size < MAX_INVENTORY_SIZE)
3272 player->inventory_element[player->inventory_size++] = element;
3276 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3277 SnapField(player, 0, 0);
3279 player->LevelSolved = FALSE;
3280 player->GameOver = FALSE;
3282 player->LevelSolved_GameWon = FALSE;
3283 player->LevelSolved_GameEnd = FALSE;
3284 player->LevelSolved_PanelOff = FALSE;
3285 player->LevelSolved_SaveTape = FALSE;
3286 player->LevelSolved_SaveScore = FALSE;
3287 player->LevelSolved_CountingTime = 0;
3288 player->LevelSolved_CountingScore = 0;
3290 map_player_action[i] = i;
3293 network_player_action_received = FALSE;
3295 #if defined(NETWORK_AVALIABLE)
3296 /* initial null action */
3297 if (network_playing)
3298 SendToServer_MovePlayer(MV_NONE);
3307 TimeLeft = level.time;
3310 ScreenMovDir = MV_NONE;
3314 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3316 AllPlayersGone = FALSE;
3318 game.no_time_limit = (level.time == 0);
3320 game.yamyam_content_nr = 0;
3321 game.robot_wheel_active = FALSE;
3322 game.magic_wall_active = FALSE;
3323 game.magic_wall_time_left = 0;
3324 game.light_time_left = 0;
3325 game.timegate_time_left = 0;
3326 game.switchgate_pos = 0;
3327 game.wind_direction = level.wind_direction_initial;
3329 game.lenses_time_left = 0;
3330 game.magnify_time_left = 0;
3332 game.ball_state = level.ball_state_initial;
3333 game.ball_content_nr = 0;
3335 game.envelope_active = FALSE;
3337 /* set focus to local player for network games, else to all players */
3338 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3339 game.centered_player_nr_next = game.centered_player_nr;
3340 game.set_centered_player = FALSE;
3342 if (network_playing && tape.recording)
3344 /* store client dependent player focus when recording network games */
3345 tape.centered_player_nr_next = game.centered_player_nr_next;
3346 tape.set_centered_player = TRUE;
3349 for (i = 0; i < NUM_BELTS; i++)
3351 game.belt_dir[i] = MV_NONE;
3352 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3355 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3356 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3358 #if DEBUG_INIT_PLAYER
3361 printf("Player status at level initialization:\n");
3365 SCAN_PLAYFIELD(x, y)
3367 Feld[x][y] = level.field[x][y];
3368 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3369 ChangeDelay[x][y] = 0;
3370 ChangePage[x][y] = -1;
3371 CustomValue[x][y] = 0; /* initialized in InitField() */
3372 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3374 WasJustMoving[x][y] = 0;
3375 WasJustFalling[x][y] = 0;
3376 CheckCollision[x][y] = 0;
3377 CheckImpact[x][y] = 0;
3379 Pushed[x][y] = FALSE;
3381 ChangeCount[x][y] = 0;
3382 ChangeEvent[x][y] = -1;
3384 ExplodePhase[x][y] = 0;
3385 ExplodeDelay[x][y] = 0;
3386 ExplodeField[x][y] = EX_TYPE_NONE;
3388 RunnerVisit[x][y] = 0;
3389 PlayerVisit[x][y] = 0;
3392 GfxRandom[x][y] = INIT_GFX_RANDOM();
3393 GfxElement[x][y] = EL_UNDEFINED;
3394 GfxAction[x][y] = ACTION_DEFAULT;
3395 GfxDir[x][y] = MV_NONE;
3396 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3399 SCAN_PLAYFIELD(x, y)
3401 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3403 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3405 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3408 InitField(x, y, TRUE);
3410 ResetGfxAnimation(x, y);
3415 for (i = 0; i < MAX_PLAYERS; i++)
3417 struct PlayerInfo *player = &stored_player[i];
3419 /* set number of special actions for bored and sleeping animation */
3420 player->num_special_action_bored =
3421 get_num_special_action(player->artwork_element,
3422 ACTION_BORING_1, ACTION_BORING_LAST);
3423 player->num_special_action_sleeping =
3424 get_num_special_action(player->artwork_element,
3425 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3428 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3429 emulate_sb ? EMU_SOKOBAN :
3430 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3432 /* initialize type of slippery elements */
3433 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435 if (!IS_CUSTOM_ELEMENT(i))
3437 /* default: elements slip down either to the left or right randomly */
3438 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3440 /* SP style elements prefer to slip down on the left side */
3441 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3442 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3444 /* BD style elements prefer to slip down on the left side */
3445 if (game.emulation == EMU_BOULDERDASH)
3446 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3450 /* initialize explosion and ignition delay */
3451 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3453 if (!IS_CUSTOM_ELEMENT(i))
3456 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3457 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3458 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3459 int last_phase = (num_phase + 1) * delay;
3460 int half_phase = (num_phase / 2) * delay;
3462 element_info[i].explosion_delay = last_phase - 1;
3463 element_info[i].ignition_delay = half_phase;
3465 if (i == EL_BLACK_ORB)
3466 element_info[i].ignition_delay = 1;
3470 /* correct non-moving belts to start moving left */
3471 for (i = 0; i < NUM_BELTS; i++)
3472 if (game.belt_dir[i] == MV_NONE)
3473 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3475 #if USE_NEW_PLAYER_ASSIGNMENTS
3476 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3477 /* choose default local player */
3478 local_player = &stored_player[0];
3480 for (i = 0; i < MAX_PLAYERS; i++)
3481 stored_player[i].connected = FALSE;
3483 local_player->connected = TRUE;
3484 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3488 for (i = 0; i < MAX_PLAYERS; i++)
3489 stored_player[i].connected = tape.player_participates[i];
3491 else if (game.team_mode && !options.network)
3493 /* try to guess locally connected team mode players (needed for correct
3494 assignment of player figures from level to locally playing players) */
3496 for (i = 0; i < MAX_PLAYERS; i++)
3497 if (setup.input[i].use_joystick ||
3498 setup.input[i].key.left != KSYM_UNDEFINED)
3499 stored_player[i].connected = TRUE;
3502 #if DEBUG_INIT_PLAYER
3505 printf("Player status after level initialization:\n");
3507 for (i = 0; i < MAX_PLAYERS; i++)
3509 struct PlayerInfo *player = &stored_player[i];
3511 printf("- player %d: present == %d, connected == %d, active == %d",
3517 if (local_player == player)
3518 printf(" (local player)");
3525 #if DEBUG_INIT_PLAYER
3527 printf("Reassigning players ...\n");
3530 /* check if any connected player was not found in playfield */
3531 for (i = 0; i < MAX_PLAYERS; i++)
3533 struct PlayerInfo *player = &stored_player[i];
3535 if (player->connected && !player->present)
3537 struct PlayerInfo *field_player = NULL;
3539 #if DEBUG_INIT_PLAYER
3541 printf("- looking for field player for player %d ...\n", i + 1);
3544 /* assign first free player found that is present in the playfield */
3546 /* first try: look for unmapped playfield player that is not connected */
3547 for (j = 0; j < MAX_PLAYERS; j++)
3548 if (field_player == NULL &&
3549 stored_player[j].present &&
3550 !stored_player[j].mapped &&
3551 !stored_player[j].connected)
3552 field_player = &stored_player[j];
3554 /* second try: look for *any* unmapped playfield player */
3555 for (j = 0; j < MAX_PLAYERS; j++)
3556 if (field_player == NULL &&
3557 stored_player[j].present &&
3558 !stored_player[j].mapped)
3559 field_player = &stored_player[j];
3561 if (field_player != NULL)
3563 int jx = field_player->jx, jy = field_player->jy;
3565 #if DEBUG_INIT_PLAYER
3567 printf("- found player %d\n", field_player->index_nr + 1);
3570 player->present = FALSE;
3571 player->active = FALSE;
3573 field_player->present = TRUE;
3574 field_player->active = TRUE;
3577 player->initial_element = field_player->initial_element;
3578 player->artwork_element = field_player->artwork_element;
3580 player->block_last_field = field_player->block_last_field;
3581 player->block_delay_adjustment = field_player->block_delay_adjustment;
3584 StorePlayer[jx][jy] = field_player->element_nr;
3586 field_player->jx = field_player->last_jx = jx;
3587 field_player->jy = field_player->last_jy = jy;
3589 if (local_player == player)
3590 local_player = field_player;
3592 map_player_action[field_player->index_nr] = i;
3594 field_player->mapped = TRUE;
3596 #if DEBUG_INIT_PLAYER
3598 printf("- map_player_action[%d] == %d\n",
3599 field_player->index_nr + 1, i + 1);
3604 if (player->connected && player->present)
3605 player->mapped = TRUE;
3608 #if DEBUG_INIT_PLAYER
3611 printf("Player status after player assignment (first stage):\n");
3613 for (i = 0; i < MAX_PLAYERS; i++)
3615 struct PlayerInfo *player = &stored_player[i];
3617 printf("- player %d: present == %d, connected == %d, active == %d",
3623 if (local_player == player)
3624 printf(" (local player)");
3633 /* check if any connected player was not found in playfield */
3634 for (i = 0; i < MAX_PLAYERS; i++)
3636 struct PlayerInfo *player = &stored_player[i];
3638 if (player->connected && !player->present)
3640 for (j = 0; j < MAX_PLAYERS; j++)
3642 struct PlayerInfo *field_player = &stored_player[j];
3643 int jx = field_player->jx, jy = field_player->jy;
3645 /* assign first free player found that is present in the playfield */
3646 if (field_player->present && !field_player->connected)
3648 player->present = TRUE;
3649 player->active = TRUE;
3651 field_player->present = FALSE;
3652 field_player->active = FALSE;
3654 player->initial_element = field_player->initial_element;
3655 player->artwork_element = field_player->artwork_element;
3657 player->block_last_field = field_player->block_last_field;
3658 player->block_delay_adjustment = field_player->block_delay_adjustment;
3660 StorePlayer[jx][jy] = player->element_nr;
3662 player->jx = player->last_jx = jx;
3663 player->jy = player->last_jy = jy;
3673 printf("::: local_player->present == %d\n", local_player->present);
3678 /* when playing a tape, eliminate all players who do not participate */
3680 #if USE_NEW_PLAYER_ASSIGNMENTS
3682 if (!game.team_mode)
3684 for (i = 0; i < MAX_PLAYERS; i++)
3686 if (stored_player[i].active &&
3687 !tape.player_participates[map_player_action[i]])
3689 struct PlayerInfo *player = &stored_player[i];
3690 int jx = player->jx, jy = player->jy;
3692 #if DEBUG_INIT_PLAYER
3694 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3697 player->active = FALSE;
3698 StorePlayer[jx][jy] = 0;
3699 Feld[jx][jy] = EL_EMPTY;
3706 for (i = 0; i < MAX_PLAYERS; i++)
3708 if (stored_player[i].active &&
3709 !tape.player_participates[i])
3711 struct PlayerInfo *player = &stored_player[i];
3712 int jx = player->jx, jy = player->jy;
3714 player->active = FALSE;
3715 StorePlayer[jx][jy] = 0;
3716 Feld[jx][jy] = EL_EMPTY;
3721 else if (!options.network && !game.team_mode) /* && !tape.playing */
3723 /* when in single player mode, eliminate all but the first active player */
3725 for (i = 0; i < MAX_PLAYERS; i++)
3727 if (stored_player[i].active)
3729 for (j = i + 1; j < MAX_PLAYERS; j++)
3731 if (stored_player[j].active)
3733 struct PlayerInfo *player = &stored_player[j];
3734 int jx = player->jx, jy = player->jy;
3736 player->active = FALSE;
3737 player->present = FALSE;
3739 StorePlayer[jx][jy] = 0;
3740 Feld[jx][jy] = EL_EMPTY;
3747 /* when recording the game, store which players take part in the game */
3750 #if USE_NEW_PLAYER_ASSIGNMENTS
3751 for (i = 0; i < MAX_PLAYERS; i++)
3752 if (stored_player[i].connected)
3753 tape.player_participates[i] = TRUE;
3755 for (i = 0; i < MAX_PLAYERS; i++)
3756 if (stored_player[i].active)
3757 tape.player_participates[i] = TRUE;
3761 #if DEBUG_INIT_PLAYER
3764 printf("Player status after player assignment (final stage):\n");
3766 for (i = 0; i < MAX_PLAYERS; i++)
3768 struct PlayerInfo *player = &stored_player[i];
3770 printf("- player %d: present == %d, connected == %d, active == %d",
3776 if (local_player == player)
3777 printf(" (local player)");
3784 if (BorderElement == EL_EMPTY)
3787 SBX_Right = lev_fieldx - SCR_FIELDX;
3789 SBY_Lower = lev_fieldy - SCR_FIELDY;
3794 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3796 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3799 if (full_lev_fieldx <= SCR_FIELDX)
3800 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3801 if (full_lev_fieldy <= SCR_FIELDY)
3802 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3804 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3806 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3809 /* if local player not found, look for custom element that might create
3810 the player (make some assumptions about the right custom element) */
3811 if (!local_player->present)
3813 int start_x = 0, start_y = 0;
3814 int found_rating = 0;
3815 int found_element = EL_UNDEFINED;
3816 int player_nr = local_player->index_nr;
3818 SCAN_PLAYFIELD(x, y)
3820 int element = Feld[x][y];
3825 if (level.use_start_element[player_nr] &&
3826 level.start_element[player_nr] == element &&
3833 found_element = element;
3836 if (!IS_CUSTOM_ELEMENT(element))
3839 if (CAN_CHANGE(element))
3841 for (i = 0; i < element_info[element].num_change_pages; i++)
3843 /* check for player created from custom element as single target */
3844 content = element_info[element].change_page[i].target_element;
3845 is_player = ELEM_IS_PLAYER(content);
3847 if (is_player && (found_rating < 3 ||
3848 (found_rating == 3 && element < found_element)))
3854 found_element = element;
3859 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3861 /* check for player created from custom element as explosion content */
3862 content = element_info[element].content.e[xx][yy];
3863 is_player = ELEM_IS_PLAYER(content);
3865 if (is_player && (found_rating < 2 ||
3866 (found_rating == 2 && element < found_element)))
3868 start_x = x + xx - 1;
3869 start_y = y + yy - 1;
3872 found_element = element;
3875 if (!CAN_CHANGE(element))
3878 for (i = 0; i < element_info[element].num_change_pages; i++)
3880 /* check for player created from custom element as extended target */
3882 element_info[element].change_page[i].target_content.e[xx][yy];
3884 is_player = ELEM_IS_PLAYER(content);
3886 if (is_player && (found_rating < 1 ||
3887 (found_rating == 1 && element < found_element)))
3889 start_x = x + xx - 1;
3890 start_y = y + yy - 1;
3893 found_element = element;
3899 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3900 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3903 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3904 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3909 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3910 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3911 local_player->jx - MIDPOSX);
3913 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3914 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3915 local_player->jy - MIDPOSY);
3918 /* !!! FIX THIS (START) !!! */
3919 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3921 InitGameEngine_EM();
3923 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3925 InitGameEngine_SP();
3929 DrawLevel(REDRAW_FIELD);
3932 /* after drawing the level, correct some elements */
3933 if (game.timegate_time_left == 0)
3934 CloseAllOpenTimegates();
3937 /* blit playfield from scroll buffer to normal back buffer for fading in */
3938 BlitScreenToBitmap(backbuffer);
3939 /* !!! FIX THIS (END) !!! */
3941 DrawMaskedBorder(fade_mask);
3946 // full screen redraw is required at this point in the following cases:
3947 // - special editor door undrawn when game was started from level editor
3948 // - drawing area (playfield) was changed and has to be removed completely
3949 redraw_mask = REDRAW_ALL;
3953 if (!game.restart_level)
3955 /* copy default game door content to main double buffer */
3957 /* !!! CHECK AGAIN !!! */
3958 SetPanelBackground();
3959 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3960 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3963 SetPanelBackground();
3964 SetDrawBackgroundMask(REDRAW_DOOR_1);
3966 UpdateAndDisplayGameControlValues();
3968 if (!game.restart_level)
3974 CreateGameButtons();
3976 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3977 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3978 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3983 /* copy actual game door content to door double buffer for OpenDoor() */
3984 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3986 OpenDoor(DOOR_OPEN_ALL);
3988 PlaySound(SND_GAME_STARTING);
3990 if (setup.sound_music)
3993 KeyboardAutoRepeatOffUnlessAutoplay();
3995 #if DEBUG_INIT_PLAYER
3998 printf("Player status (final):\n");
4000 for (i = 0; i < MAX_PLAYERS; i++)
4002 struct PlayerInfo *player = &stored_player[i];
4004 printf("- player %d: present == %d, connected == %d, active == %d",
4010 if (local_player == player)
4011 printf(" (local player)");
4024 if (!game.restart_level && !tape.playing)
4026 LevelStats_incPlayed(level_nr);
4028 SaveLevelSetup_SeriesInfo();
4031 game.restart_level = FALSE;
4033 SaveEngineSnapshotToListInitial();
4036 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4037 int actual_player_x, int actual_player_y)
4039 /* this is used for non-R'n'D game engines to update certain engine values */
4041 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4043 actual_player_x = correctLevelPosX_EM(actual_player_x);
4044 actual_player_y = correctLevelPosY_EM(actual_player_y);
4047 /* needed to determine if sounds are played within the visible screen area */
4048 scroll_x = actual_scroll_x;
4049 scroll_y = actual_scroll_y;
4051 /* needed to get player position for "follow finger" playing input method */
4052 local_player->jx = actual_player_x;
4053 local_player->jy = actual_player_y;
4056 void InitMovDir(int x, int y)
4058 int i, element = Feld[x][y];
4059 static int xy[4][2] =
4066 static int direction[3][4] =
4068 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4069 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4070 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4079 Feld[x][y] = EL_BUG;
4080 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4083 case EL_SPACESHIP_RIGHT:
4084 case EL_SPACESHIP_UP:
4085 case EL_SPACESHIP_LEFT:
4086 case EL_SPACESHIP_DOWN:
4087 Feld[x][y] = EL_SPACESHIP;
4088 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4091 case EL_BD_BUTTERFLY_RIGHT:
4092 case EL_BD_BUTTERFLY_UP:
4093 case EL_BD_BUTTERFLY_LEFT:
4094 case EL_BD_BUTTERFLY_DOWN:
4095 Feld[x][y] = EL_BD_BUTTERFLY;
4096 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4099 case EL_BD_FIREFLY_RIGHT:
4100 case EL_BD_FIREFLY_UP:
4101 case EL_BD_FIREFLY_LEFT:
4102 case EL_BD_FIREFLY_DOWN:
4103 Feld[x][y] = EL_BD_FIREFLY;
4104 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4107 case EL_PACMAN_RIGHT:
4109 case EL_PACMAN_LEFT:
4110 case EL_PACMAN_DOWN:
4111 Feld[x][y] = EL_PACMAN;
4112 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4115 case EL_YAMYAM_LEFT:
4116 case EL_YAMYAM_RIGHT:
4118 case EL_YAMYAM_DOWN:
4119 Feld[x][y] = EL_YAMYAM;
4120 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4123 case EL_SP_SNIKSNAK:
4124 MovDir[x][y] = MV_UP;
4127 case EL_SP_ELECTRON:
4128 MovDir[x][y] = MV_LEFT;
4135 Feld[x][y] = EL_MOLE;
4136 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4140 if (IS_CUSTOM_ELEMENT(element))
4142 struct ElementInfo *ei = &element_info[element];
4143 int move_direction_initial = ei->move_direction_initial;
4144 int move_pattern = ei->move_pattern;
4146 if (move_direction_initial == MV_START_PREVIOUS)
4148 if (MovDir[x][y] != MV_NONE)
4151 move_direction_initial = MV_START_AUTOMATIC;
4154 if (move_direction_initial == MV_START_RANDOM)
4155 MovDir[x][y] = 1 << RND(4);
4156 else if (move_direction_initial & MV_ANY_DIRECTION)
4157 MovDir[x][y] = move_direction_initial;
4158 else if (move_pattern == MV_ALL_DIRECTIONS ||
4159 move_pattern == MV_TURNING_LEFT ||
4160 move_pattern == MV_TURNING_RIGHT ||
4161 move_pattern == MV_TURNING_LEFT_RIGHT ||
4162 move_pattern == MV_TURNING_RIGHT_LEFT ||
4163 move_pattern == MV_TURNING_RANDOM)
4164 MovDir[x][y] = 1 << RND(4);
4165 else if (move_pattern == MV_HORIZONTAL)
4166 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4167 else if (move_pattern == MV_VERTICAL)
4168 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4169 else if (move_pattern & MV_ANY_DIRECTION)
4170 MovDir[x][y] = element_info[element].move_pattern;
4171 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4172 move_pattern == MV_ALONG_RIGHT_SIDE)
4174 /* use random direction as default start direction */
4175 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4176 MovDir[x][y] = 1 << RND(4);
4178 for (i = 0; i < NUM_DIRECTIONS; i++)
4180 int x1 = x + xy[i][0];
4181 int y1 = y + xy[i][1];
4183 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4185 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4186 MovDir[x][y] = direction[0][i];
4188 MovDir[x][y] = direction[1][i];
4197 MovDir[x][y] = 1 << RND(4);
4199 if (element != EL_BUG &&
4200 element != EL_SPACESHIP &&
4201 element != EL_BD_BUTTERFLY &&
4202 element != EL_BD_FIREFLY)
4205 for (i = 0; i < NUM_DIRECTIONS; i++)
4207 int x1 = x + xy[i][0];
4208 int y1 = y + xy[i][1];
4210 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4212 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4214 MovDir[x][y] = direction[0][i];
4217 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4218 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4220 MovDir[x][y] = direction[1][i];
4229 GfxDir[x][y] = MovDir[x][y];
4232 void InitAmoebaNr(int x, int y)
4235 int group_nr = AmoebeNachbarNr(x, y);
4239 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4241 if (AmoebaCnt[i] == 0)
4249 AmoebaNr[x][y] = group_nr;
4250 AmoebaCnt[group_nr]++;
4251 AmoebaCnt2[group_nr]++;
4254 static void PlayerWins(struct PlayerInfo *player)
4256 player->LevelSolved = TRUE;
4257 player->GameOver = TRUE;
4259 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4260 level.native_em_level->lev->score : player->score);
4262 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4264 player->LevelSolved_CountingScore = player->score_final;
4269 static int time, time_final;
4270 static int score, score_final;
4271 static int game_over_delay_1 = 0;
4272 static int game_over_delay_2 = 0;
4273 int game_over_delay_value_1 = 50;
4274 int game_over_delay_value_2 = 50;
4276 if (!local_player->LevelSolved_GameWon)
4280 /* do not start end game actions before the player stops moving (to exit) */
4281 if (local_player->MovPos)
4284 local_player->LevelSolved_GameWon = TRUE;
4285 local_player->LevelSolved_SaveTape = tape.recording;
4286 local_player->LevelSolved_SaveScore = !tape.playing;
4290 LevelStats_incSolved(level_nr);
4292 SaveLevelSetup_SeriesInfo();
4295 if (tape.auto_play) /* tape might already be stopped here */
4296 tape.auto_play_level_solved = TRUE;
4300 game_over_delay_1 = game_over_delay_value_1;
4301 game_over_delay_2 = game_over_delay_value_2;
4303 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4304 score = score_final = local_player->score_final;
4309 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4311 else if (game.no_time_limit && TimePlayed < 999)
4314 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4317 local_player->score_final = score_final;
4319 if (level_editor_test_game)
4322 score = score_final;
4324 local_player->LevelSolved_CountingTime = time;
4325 local_player->LevelSolved_CountingScore = score;
4327 game_panel_controls[GAME_PANEL_TIME].value = time;
4328 game_panel_controls[GAME_PANEL_SCORE].value = score;
4330 DisplayGameControlValues();
4333 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4335 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4337 /* close exit door after last player */
4338 if ((AllPlayersGone &&
4339 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4340 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4341 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4342 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4343 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4345 int element = Feld[ExitX][ExitY];
4347 Feld[ExitX][ExitY] =
4348 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4349 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4350 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4351 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4352 EL_EM_STEEL_EXIT_CLOSING);
4354 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4357 /* player disappears */
4358 DrawLevelField(ExitX, ExitY);
4361 for (i = 0; i < MAX_PLAYERS; i++)
4363 struct PlayerInfo *player = &stored_player[i];
4365 if (player->present)
4367 RemovePlayer(player);
4369 /* player disappears */
4370 DrawLevelField(player->jx, player->jy);
4375 PlaySound(SND_GAME_WINNING);
4378 if (game_over_delay_1 > 0)
4380 game_over_delay_1--;
4385 if (time != time_final)
4387 int time_to_go = ABS(time_final - time);
4388 int time_count_dir = (time < time_final ? +1 : -1);
4389 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4391 time += time_count_steps * time_count_dir;
4392 score += time_count_steps * level.score[SC_TIME_BONUS];
4394 local_player->LevelSolved_CountingTime = time;
4395 local_player->LevelSolved_CountingScore = score;
4397 game_panel_controls[GAME_PANEL_TIME].value = time;
4398 game_panel_controls[GAME_PANEL_SCORE].value = score;
4400 DisplayGameControlValues();
4402 if (time == time_final)
4403 StopSound(SND_GAME_LEVELTIME_BONUS);
4404 else if (setup.sound_loops)
4405 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4407 PlaySound(SND_GAME_LEVELTIME_BONUS);
4412 local_player->LevelSolved_PanelOff = TRUE;
4414 if (game_over_delay_2 > 0)
4416 game_over_delay_2--;
4427 boolean raise_level = FALSE;
4429 local_player->LevelSolved_GameEnd = TRUE;
4431 if (!global.use_envelope_request)
4432 CloseDoor(DOOR_CLOSE_1);
4434 if (local_player->LevelSolved_SaveTape)
4436 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4439 CloseDoor(DOOR_CLOSE_ALL);
4441 if (level_editor_test_game)
4443 SetGameStatus(GAME_MODE_MAIN);
4450 if (!local_player->LevelSolved_SaveScore)
4452 SetGameStatus(GAME_MODE_MAIN);
4459 if (level_nr == leveldir_current->handicap_level)
4461 leveldir_current->handicap_level++;
4463 SaveLevelSetup_SeriesInfo();
4466 if (level_nr < leveldir_current->last_level)
4467 raise_level = TRUE; /* advance to next level */
4469 if ((hi_pos = NewHiScore()) >= 0)
4471 SetGameStatus(GAME_MODE_SCORES);
4473 DrawHallOfFame(hi_pos);
4483 SetGameStatus(GAME_MODE_MAIN);
4500 LoadScore(level_nr);
4502 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4503 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4506 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4508 if (local_player->score_final > highscore[k].Score)
4510 /* player has made it to the hall of fame */
4512 if (k < MAX_SCORE_ENTRIES - 1)
4514 int m = MAX_SCORE_ENTRIES - 1;
4517 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4518 if (strEqual(setup.player_name, highscore[l].Name))
4520 if (m == k) /* player's new highscore overwrites his old one */
4524 for (l = m; l > k; l--)
4526 strcpy(highscore[l].Name, highscore[l - 1].Name);
4527 highscore[l].Score = highscore[l - 1].Score;
4534 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4535 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4536 highscore[k].Score = local_player->score_final;
4542 else if (!strncmp(setup.player_name, highscore[k].Name,
4543 MAX_PLAYER_NAME_LEN))
4544 break; /* player already there with a higher score */
4550 SaveScore(level_nr);
4555 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4557 int element = Feld[x][y];
4558 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4559 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4560 int horiz_move = (dx != 0);
4561 int sign = (horiz_move ? dx : dy);
4562 int step = sign * element_info[element].move_stepsize;
4564 /* special values for move stepsize for spring and things on conveyor belt */
4567 if (CAN_FALL(element) &&
4568 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4569 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4570 else if (element == EL_SPRING)
4571 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4577 inline static int getElementMoveStepsize(int x, int y)
4579 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4582 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4584 if (player->GfxAction != action || player->GfxDir != dir)
4586 player->GfxAction = action;
4587 player->GfxDir = dir;
4589 player->StepFrame = 0;
4593 static void ResetGfxFrame(int x, int y, boolean redraw)
4595 int element = Feld[x][y];
4596 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4597 int last_gfx_frame = GfxFrame[x][y];
4599 if (graphic_info[graphic].anim_global_sync)
4600 GfxFrame[x][y] = FrameCounter;
4601 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4602 GfxFrame[x][y] = CustomValue[x][y];
4603 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4604 GfxFrame[x][y] = element_info[element].collect_score;
4605 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4606 GfxFrame[x][y] = ChangeDelay[x][y];
4608 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4609 DrawLevelGraphicAnimation(x, y, graphic);
4612 static void ResetGfxAnimation(int x, int y)
4614 GfxAction[x][y] = ACTION_DEFAULT;
4615 GfxDir[x][y] = MovDir[x][y];
4618 ResetGfxFrame(x, y, FALSE);
4621 static void ResetRandomAnimationValue(int x, int y)
4623 GfxRandom[x][y] = INIT_GFX_RANDOM();
4626 void InitMovingField(int x, int y, int direction)
4628 int element = Feld[x][y];
4629 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4630 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4633 boolean is_moving_before, is_moving_after;
4635 /* check if element was/is moving or being moved before/after mode change */
4636 is_moving_before = (WasJustMoving[x][y] != 0);
4637 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4639 /* reset animation only for moving elements which change direction of moving
4640 or which just started or stopped moving
4641 (else CEs with property "can move" / "not moving" are reset each frame) */
4642 if (is_moving_before != is_moving_after ||
4643 direction != MovDir[x][y])
4644 ResetGfxAnimation(x, y);
4646 MovDir[x][y] = direction;
4647 GfxDir[x][y] = direction;
4649 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4650 direction == MV_DOWN && CAN_FALL(element) ?
4651 ACTION_FALLING : ACTION_MOVING);
4653 /* this is needed for CEs with property "can move" / "not moving" */
4655 if (is_moving_after)
4657 if (Feld[newx][newy] == EL_EMPTY)
4658 Feld[newx][newy] = EL_BLOCKED;
4660 MovDir[newx][newy] = MovDir[x][y];
4662 CustomValue[newx][newy] = CustomValue[x][y];
4664 GfxFrame[newx][newy] = GfxFrame[x][y];
4665 GfxRandom[newx][newy] = GfxRandom[x][y];
4666 GfxAction[newx][newy] = GfxAction[x][y];
4667 GfxDir[newx][newy] = GfxDir[x][y];
4671 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4673 int direction = MovDir[x][y];
4674 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4675 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4681 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4683 int oldx = x, oldy = y;
4684 int direction = MovDir[x][y];
4686 if (direction == MV_LEFT)
4688 else if (direction == MV_RIGHT)
4690 else if (direction == MV_UP)
4692 else if (direction == MV_DOWN)
4695 *comes_from_x = oldx;
4696 *comes_from_y = oldy;
4699 int MovingOrBlocked2Element(int x, int y)
4701 int element = Feld[x][y];
4703 if (element == EL_BLOCKED)
4707 Blocked2Moving(x, y, &oldx, &oldy);
4708 return Feld[oldx][oldy];
4714 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4716 /* like MovingOrBlocked2Element(), but if element is moving
4717 and (x,y) is the field the moving element is just leaving,
4718 return EL_BLOCKED instead of the element value */
4719 int element = Feld[x][y];
4721 if (IS_MOVING(x, y))
4723 if (element == EL_BLOCKED)
4727 Blocked2Moving(x, y, &oldx, &oldy);
4728 return Feld[oldx][oldy];
4737 static void RemoveField(int x, int y)
4739 Feld[x][y] = EL_EMPTY;
4745 CustomValue[x][y] = 0;
4748 ChangeDelay[x][y] = 0;
4749 ChangePage[x][y] = -1;
4750 Pushed[x][y] = FALSE;
4752 GfxElement[x][y] = EL_UNDEFINED;
4753 GfxAction[x][y] = ACTION_DEFAULT;
4754 GfxDir[x][y] = MV_NONE;
4757 void RemoveMovingField(int x, int y)
4759 int oldx = x, oldy = y, newx = x, newy = y;
4760 int element = Feld[x][y];
4761 int next_element = EL_UNDEFINED;
4763 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4766 if (IS_MOVING(x, y))
4768 Moving2Blocked(x, y, &newx, &newy);
4770 if (Feld[newx][newy] != EL_BLOCKED)
4772 /* element is moving, but target field is not free (blocked), but
4773 already occupied by something different (example: acid pool);
4774 in this case, only remove the moving field, but not the target */
4776 RemoveField(oldx, oldy);
4778 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4780 TEST_DrawLevelField(oldx, oldy);
4785 else if (element == EL_BLOCKED)
4787 Blocked2Moving(x, y, &oldx, &oldy);
4788 if (!IS_MOVING(oldx, oldy))
4792 if (element == EL_BLOCKED &&
4793 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4794 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4795 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4796 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4797 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4798 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4799 next_element = get_next_element(Feld[oldx][oldy]);
4801 RemoveField(oldx, oldy);
4802 RemoveField(newx, newy);
4804 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4806 if (next_element != EL_UNDEFINED)
4807 Feld[oldx][oldy] = next_element;
4809 TEST_DrawLevelField(oldx, oldy);
4810 TEST_DrawLevelField(newx, newy);
4813 void DrawDynamite(int x, int y)
4815 int sx = SCREENX(x), sy = SCREENY(y);
4816 int graphic = el2img(Feld[x][y]);
4819 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4822 if (IS_WALKABLE_INSIDE(Back[x][y]))
4826 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4827 else if (Store[x][y])
4828 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4830 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4832 if (Back[x][y] || Store[x][y])
4833 DrawGraphicThruMask(sx, sy, graphic, frame);
4835 DrawGraphic(sx, sy, graphic, frame);
4838 void CheckDynamite(int x, int y)
4840 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4844 if (MovDelay[x][y] != 0)
4847 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4853 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4858 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4860 boolean num_checked_players = 0;
4863 for (i = 0; i < MAX_PLAYERS; i++)
4865 if (stored_player[i].active)
4867 int sx = stored_player[i].jx;
4868 int sy = stored_player[i].jy;
4870 if (num_checked_players == 0)
4877 *sx1 = MIN(*sx1, sx);
4878 *sy1 = MIN(*sy1, sy);
4879 *sx2 = MAX(*sx2, sx);
4880 *sy2 = MAX(*sy2, sy);
4883 num_checked_players++;
4888 static boolean checkIfAllPlayersFitToScreen_RND()
4890 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4892 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4894 return (sx2 - sx1 < SCR_FIELDX &&
4895 sy2 - sy1 < SCR_FIELDY);
4898 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4900 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4902 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4904 *sx = (sx1 + sx2) / 2;
4905 *sy = (sy1 + sy2) / 2;
4908 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4909 boolean center_screen, boolean quick_relocation)
4911 unsigned int frame_delay_value_old = GetVideoFrameDelay();
4912 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4913 boolean no_delay = (tape.warp_forward);
4914 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4915 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4916 int new_scroll_x, new_scroll_y;
4918 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4920 /* case 1: quick relocation inside visible screen (without scrolling) */
4927 if (!level.shifted_relocation || center_screen)
4929 /* relocation _with_ centering of screen */
4931 new_scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4932 x > SBX_Right + MIDPOSX ? SBX_Right :
4935 new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4936 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4941 /* relocation _without_ centering of screen */
4943 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4944 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4947 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4948 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4951 int offset_x = x + (scroll_x - center_scroll_x);
4952 int offset_y = y + (scroll_y - center_scroll_y);
4954 new_scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4955 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4956 offset_x - MIDPOSX);
4958 new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4959 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4960 offset_y - MIDPOSY);
4963 if (quick_relocation)
4965 /* case 2: quick relocation (redraw without visible scrolling) */
4967 scroll_x = new_scroll_x;
4968 scroll_y = new_scroll_y;
4975 /* case 3: visible relocation (with scrolling to new position) */
4977 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4979 SetVideoFrameDelay(wait_delay_value);
4981 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4984 int fx = FX, fy = FY;
4986 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4987 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4989 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4995 fx += dx * TILEX / 2;
4996 fy += dy * TILEY / 2;
4998 ScrollLevel(dx, dy);
5001 /* scroll in two steps of half tile size to make things smoother */
5002 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5004 /* scroll second step to align at full tile size */
5005 BlitScreenToBitmap(window);
5011 SetVideoFrameDelay(frame_delay_value_old);
5014 void RelocatePlayer(int jx, int jy, int el_player_raw)
5016 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5017 int player_nr = GET_PLAYER_NR(el_player);
5018 struct PlayerInfo *player = &stored_player[player_nr];
5019 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5020 boolean no_delay = (tape.warp_forward);
5021 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5022 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5023 int old_jx = player->jx;
5024 int old_jy = player->jy;
5025 int old_element = Feld[old_jx][old_jy];
5026 int element = Feld[jx][jy];
5027 boolean player_relocated = (old_jx != jx || old_jy != jy);
5029 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5030 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5031 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5032 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5033 int leave_side_horiz = move_dir_horiz;
5034 int leave_side_vert = move_dir_vert;
5035 int enter_side = enter_side_horiz | enter_side_vert;
5036 int leave_side = leave_side_horiz | leave_side_vert;
5038 if (player->GameOver) /* do not reanimate dead player */
5041 if (!player_relocated) /* no need to relocate the player */
5044 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5046 RemoveField(jx, jy); /* temporarily remove newly placed player */
5047 DrawLevelField(jx, jy);
5050 if (player->present)
5052 while (player->MovPos)
5054 ScrollPlayer(player, SCROLL_GO_ON);
5055 ScrollScreen(NULL, SCROLL_GO_ON);
5057 AdvanceFrameAndPlayerCounters(player->index_nr);
5061 BackToFront_WithFrameDelay(wait_delay_value);
5064 DrawPlayer(player); /* needed here only to cleanup last field */
5065 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5067 player->is_moving = FALSE;
5070 if (IS_CUSTOM_ELEMENT(old_element))
5071 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5073 player->index_bit, leave_side);
5075 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5077 player->index_bit, leave_side);
5079 Feld[jx][jy] = el_player;
5080 InitPlayerField(jx, jy, el_player, TRUE);
5082 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5083 possible that the relocation target field did not contain a player element,
5084 but a walkable element, to which the new player was relocated -- in this
5085 case, restore that (already initialized!) element on the player field */
5086 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5088 Feld[jx][jy] = element; /* restore previously existing element */
5091 /* only visually relocate centered player */
5092 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5093 FALSE, level.instant_relocation);
5095 TestIfPlayerTouchesBadThing(jx, jy);
5096 TestIfPlayerTouchesCustomElement(jx, jy);
5098 if (IS_CUSTOM_ELEMENT(element))
5099 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5100 player->index_bit, enter_side);
5102 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5103 player->index_bit, enter_side);
5105 if (player->is_switching)
5107 /* ensure that relocation while still switching an element does not cause
5108 a new element to be treated as also switched directly after relocation
5109 (this is important for teleporter switches that teleport the player to
5110 a place where another teleporter switch is in the same direction, which
5111 would then incorrectly be treated as immediately switched before the
5112 direction key that caused the switch was released) */
5114 player->switch_x += jx - old_jx;
5115 player->switch_y += jy - old_jy;
5119 void Explode(int ex, int ey, int phase, int mode)
5125 /* !!! eliminate this variable !!! */
5126 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5128 if (game.explosions_delayed)
5130 ExplodeField[ex][ey] = mode;
5134 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5136 int center_element = Feld[ex][ey];
5137 int artwork_element, explosion_element; /* set these values later */
5139 /* remove things displayed in background while burning dynamite */
5140 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5143 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5145 /* put moving element to center field (and let it explode there) */
5146 center_element = MovingOrBlocked2Element(ex, ey);
5147 RemoveMovingField(ex, ey);
5148 Feld[ex][ey] = center_element;
5151 /* now "center_element" is finally determined -- set related values now */
5152 artwork_element = center_element; /* for custom player artwork */
5153 explosion_element = center_element; /* for custom player artwork */
5155 if (IS_PLAYER(ex, ey))
5157 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5159 artwork_element = stored_player[player_nr].artwork_element;
5161 if (level.use_explosion_element[player_nr])
5163 explosion_element = level.explosion_element[player_nr];
5164 artwork_element = explosion_element;
5168 if (mode == EX_TYPE_NORMAL ||
5169 mode == EX_TYPE_CENTER ||
5170 mode == EX_TYPE_CROSS)
5171 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5173 last_phase = element_info[explosion_element].explosion_delay + 1;
5175 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5177 int xx = x - ex + 1;
5178 int yy = y - ey + 1;
5181 if (!IN_LEV_FIELD(x, y) ||
5182 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5183 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5186 element = Feld[x][y];
5188 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5190 element = MovingOrBlocked2Element(x, y);
5192 if (!IS_EXPLOSION_PROOF(element))
5193 RemoveMovingField(x, y);
5196 /* indestructible elements can only explode in center (but not flames) */
5197 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5198 mode == EX_TYPE_BORDER)) ||
5199 element == EL_FLAMES)
5202 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5203 behaviour, for example when touching a yamyam that explodes to rocks
5204 with active deadly shield, a rock is created under the player !!! */
5205 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5207 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5208 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5209 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5211 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5214 if (IS_ACTIVE_BOMB(element))
5216 /* re-activate things under the bomb like gate or penguin */
5217 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5224 /* save walkable background elements while explosion on same tile */
5225 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5226 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5227 Back[x][y] = element;
5229 /* ignite explodable elements reached by other explosion */
5230 if (element == EL_EXPLOSION)
5231 element = Store2[x][y];
5233 if (AmoebaNr[x][y] &&
5234 (element == EL_AMOEBA_FULL ||
5235 element == EL_BD_AMOEBA ||
5236 element == EL_AMOEBA_GROWING))
5238 AmoebaCnt[AmoebaNr[x][y]]--;
5239 AmoebaCnt2[AmoebaNr[x][y]]--;
5244 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5246 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5248 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5250 if (PLAYERINFO(ex, ey)->use_murphy)
5251 Store[x][y] = EL_EMPTY;
5254 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5255 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5256 else if (ELEM_IS_PLAYER(center_element))
5257 Store[x][y] = EL_EMPTY;
5258 else if (center_element == EL_YAMYAM)
5259 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5260 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5261 Store[x][y] = element_info[center_element].content.e[xx][yy];
5263 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5264 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5265 otherwise) -- FIX THIS !!! */
5266 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5267 Store[x][y] = element_info[element].content.e[1][1];
5269 else if (!CAN_EXPLODE(element))
5270 Store[x][y] = element_info[element].content.e[1][1];
5273 Store[x][y] = EL_EMPTY;
5275 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5276 center_element == EL_AMOEBA_TO_DIAMOND)
5277 Store2[x][y] = element;
5279 Feld[x][y] = EL_EXPLOSION;
5280 GfxElement[x][y] = artwork_element;
5282 ExplodePhase[x][y] = 1;
5283 ExplodeDelay[x][y] = last_phase;
5288 if (center_element == EL_YAMYAM)
5289 game.yamyam_content_nr =
5290 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5302 GfxFrame[x][y] = 0; /* restart explosion animation */
5304 last_phase = ExplodeDelay[x][y];
5306 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5308 /* this can happen if the player leaves an explosion just in time */
5309 if (GfxElement[x][y] == EL_UNDEFINED)
5310 GfxElement[x][y] = EL_EMPTY;
5312 border_element = Store2[x][y];
5313 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5314 border_element = StorePlayer[x][y];
5316 if (phase == element_info[border_element].ignition_delay ||
5317 phase == last_phase)
5319 boolean border_explosion = FALSE;
5321 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5322 !PLAYER_EXPLOSION_PROTECTED(x, y))
5324 KillPlayerUnlessExplosionProtected(x, y);
5325 border_explosion = TRUE;
5327 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5329 Feld[x][y] = Store2[x][y];
5332 border_explosion = TRUE;
5334 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5336 AmoebeUmwandeln(x, y);
5338 border_explosion = TRUE;
5341 /* if an element just explodes due to another explosion (chain-reaction),
5342 do not immediately end the new explosion when it was the last frame of
5343 the explosion (as it would be done in the following "if"-statement!) */
5344 if (border_explosion && phase == last_phase)
5348 if (phase == last_phase)
5352 element = Feld[x][y] = Store[x][y];
5353 Store[x][y] = Store2[x][y] = 0;
5354 GfxElement[x][y] = EL_UNDEFINED;
5356 /* player can escape from explosions and might therefore be still alive */
5357 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5358 element <= EL_PLAYER_IS_EXPLODING_4)
5360 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5361 int explosion_element = EL_PLAYER_1 + player_nr;
5362 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5363 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5365 if (level.use_explosion_element[player_nr])
5366 explosion_element = level.explosion_element[player_nr];
5368 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5369 element_info[explosion_element].content.e[xx][yy]);
5372 /* restore probably existing indestructible background element */
5373 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5374 element = Feld[x][y] = Back[x][y];
5377 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5378 GfxDir[x][y] = MV_NONE;
5379 ChangeDelay[x][y] = 0;
5380 ChangePage[x][y] = -1;
5382 CustomValue[x][y] = 0;
5384 InitField_WithBug2(x, y, FALSE);
5386 TEST_DrawLevelField(x, y);
5388 TestIfElementTouchesCustomElement(x, y);
5390 if (GFX_CRUMBLED(element))
5391 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5393 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5394 StorePlayer[x][y] = 0;
5396 if (ELEM_IS_PLAYER(element))
5397 RelocatePlayer(x, y, element);
5399 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5401 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5402 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5405 TEST_DrawLevelFieldCrumbled(x, y);
5407 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5409 DrawLevelElement(x, y, Back[x][y]);
5410 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5412 else if (IS_WALKABLE_UNDER(Back[x][y]))
5414 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5415 DrawLevelElementThruMask(x, y, Back[x][y]);
5417 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5418 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5422 void DynaExplode(int ex, int ey)
5425 int dynabomb_element = Feld[ex][ey];
5426 int dynabomb_size = 1;
5427 boolean dynabomb_xl = FALSE;
5428 struct PlayerInfo *player;
5429 static int xy[4][2] =
5437 if (IS_ACTIVE_BOMB(dynabomb_element))
5439 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5440 dynabomb_size = player->dynabomb_size;
5441 dynabomb_xl = player->dynabomb_xl;
5442 player->dynabombs_left++;
5445 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5447 for (i = 0; i < NUM_DIRECTIONS; i++)
5449 for (j = 1; j <= dynabomb_size; j++)
5451 int x = ex + j * xy[i][0];
5452 int y = ey + j * xy[i][1];
5455 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5458 element = Feld[x][y];
5460 /* do not restart explosions of fields with active bombs */
5461 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5464 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5466 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5467 !IS_DIGGABLE(element) && !dynabomb_xl)
5473 void Bang(int x, int y)
5475 int element = MovingOrBlocked2Element(x, y);
5476 int explosion_type = EX_TYPE_NORMAL;
5478 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5480 struct PlayerInfo *player = PLAYERINFO(x, y);
5482 element = Feld[x][y] = player->initial_element;
5484 if (level.use_explosion_element[player->index_nr])
5486 int explosion_element = level.explosion_element[player->index_nr];
5488 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5489 explosion_type = EX_TYPE_CROSS;
5490 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5491 explosion_type = EX_TYPE_CENTER;
5499 case EL_BD_BUTTERFLY:
5502 case EL_DARK_YAMYAM:
5506 RaiseScoreElement(element);
5509 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5510 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5511 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5512 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5513 case EL_DYNABOMB_INCREASE_NUMBER:
5514 case EL_DYNABOMB_INCREASE_SIZE:
5515 case EL_DYNABOMB_INCREASE_POWER:
5516 explosion_type = EX_TYPE_DYNA;
5519 case EL_DC_LANDMINE:
5520 explosion_type = EX_TYPE_CENTER;
5525 case EL_LAMP_ACTIVE:
5526 case EL_AMOEBA_TO_DIAMOND:
5527 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5528 explosion_type = EX_TYPE_CENTER;
5532 if (element_info[element].explosion_type == EXPLODES_CROSS)
5533 explosion_type = EX_TYPE_CROSS;
5534 else if (element_info[element].explosion_type == EXPLODES_1X1)
5535 explosion_type = EX_TYPE_CENTER;
5539 if (explosion_type == EX_TYPE_DYNA)
5542 Explode(x, y, EX_PHASE_START, explosion_type);
5544 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5547 void SplashAcid(int x, int y)
5549 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5550 (!IN_LEV_FIELD(x - 1, y - 2) ||
5551 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5552 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5554 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5555 (!IN_LEV_FIELD(x + 1, y - 2) ||
5556 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5557 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5559 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5562 static void InitBeltMovement()
5564 static int belt_base_element[4] =
5566 EL_CONVEYOR_BELT_1_LEFT,
5567 EL_CONVEYOR_BELT_2_LEFT,
5568 EL_CONVEYOR_BELT_3_LEFT,
5569 EL_CONVEYOR_BELT_4_LEFT
5571 static int belt_base_active_element[4] =
5573 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5574 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5575 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5576 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5581 /* set frame order for belt animation graphic according to belt direction */
5582 for (i = 0; i < NUM_BELTS; i++)
5586 for (j = 0; j < NUM_BELT_PARTS; j++)
5588 int element = belt_base_active_element[belt_nr] + j;
5589 int graphic_1 = el2img(element);
5590 int graphic_2 = el2panelimg(element);
5592 if (game.belt_dir[i] == MV_LEFT)
5594 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5595 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5599 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5600 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5605 SCAN_PLAYFIELD(x, y)
5607 int element = Feld[x][y];
5609 for (i = 0; i < NUM_BELTS; i++)
5611 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5613 int e_belt_nr = getBeltNrFromBeltElement(element);
5616 if (e_belt_nr == belt_nr)
5618 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5620 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5627 static void ToggleBeltSwitch(int x, int y)
5629 static int belt_base_element[4] =
5631 EL_CONVEYOR_BELT_1_LEFT,
5632 EL_CONVEYOR_BELT_2_LEFT,
5633 EL_CONVEYOR_BELT_3_LEFT,
5634 EL_CONVEYOR_BELT_4_LEFT
5636 static int belt_base_active_element[4] =
5638 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5639 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5640 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5641 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5643 static int belt_base_switch_element[4] =
5645 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5646 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5647 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5648 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5650 static int belt_move_dir[4] =
5658 int element = Feld[x][y];
5659 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5660 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5661 int belt_dir = belt_move_dir[belt_dir_nr];
5664 if (!IS_BELT_SWITCH(element))
5667 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5668 game.belt_dir[belt_nr] = belt_dir;
5670 if (belt_dir_nr == 3)
5673 /* set frame order for belt animation graphic according to belt direction */
5674 for (i = 0; i < NUM_BELT_PARTS; i++)
5676 int element = belt_base_active_element[belt_nr] + i;
5677 int graphic_1 = el2img(element);
5678 int graphic_2 = el2panelimg(element);
5680 if (belt_dir == MV_LEFT)
5682 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5683 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5687 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5688 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5692 SCAN_PLAYFIELD(xx, yy)
5694 int element = Feld[xx][yy];
5696 if (IS_BELT_SWITCH(element))
5698 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5700 if (e_belt_nr == belt_nr)
5702 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5703 TEST_DrawLevelField(xx, yy);
5706 else if (IS_BELT(element) && belt_dir != MV_NONE)
5708 int e_belt_nr = getBeltNrFromBeltElement(element);
5710 if (e_belt_nr == belt_nr)
5712 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5714 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5715 TEST_DrawLevelField(xx, yy);
5718 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5720 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5722 if (e_belt_nr == belt_nr)
5724 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5726 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5727 TEST_DrawLevelField(xx, yy);
5733 static void ToggleSwitchgateSwitch(int x, int y)
5737 game.switchgate_pos = !game.switchgate_pos;
5739 SCAN_PLAYFIELD(xx, yy)
5741 int element = Feld[xx][yy];
5743 if (element == EL_SWITCHGATE_SWITCH_UP)
5745 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5746 TEST_DrawLevelField(xx, yy);
5748 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5750 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5751 TEST_DrawLevelField(xx, yy);
5753 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5755 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5756 TEST_DrawLevelField(xx, yy);
5758 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5760 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5761 TEST_DrawLevelField(xx, yy);
5763 else if (element == EL_SWITCHGATE_OPEN ||
5764 element == EL_SWITCHGATE_OPENING)
5766 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5768 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5770 else if (element == EL_SWITCHGATE_CLOSED ||
5771 element == EL_SWITCHGATE_CLOSING)
5773 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5775 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5780 static int getInvisibleActiveFromInvisibleElement(int element)
5782 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5783 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5784 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5788 static int getInvisibleFromInvisibleActiveElement(int element)
5790 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5791 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5792 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5796 static void RedrawAllLightSwitchesAndInvisibleElements()
5800 SCAN_PLAYFIELD(x, y)
5802 int element = Feld[x][y];
5804 if (element == EL_LIGHT_SWITCH &&
5805 game.light_time_left > 0)
5807 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5808 TEST_DrawLevelField(x, y);
5810 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5811 game.light_time_left == 0)
5813 Feld[x][y] = EL_LIGHT_SWITCH;
5814 TEST_DrawLevelField(x, y);
5816 else if (element == EL_EMC_DRIPPER &&
5817 game.light_time_left > 0)
5819 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5820 TEST_DrawLevelField(x, y);
5822 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5823 game.light_time_left == 0)
5825 Feld[x][y] = EL_EMC_DRIPPER;
5826 TEST_DrawLevelField(x, y);
5828 else if (element == EL_INVISIBLE_STEELWALL ||
5829 element == EL_INVISIBLE_WALL ||
5830 element == EL_INVISIBLE_SAND)
5832 if (game.light_time_left > 0)
5833 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5835 TEST_DrawLevelField(x, y);
5837 /* uncrumble neighbour fields, if needed */
5838 if (element == EL_INVISIBLE_SAND)
5839 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5841 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5842 element == EL_INVISIBLE_WALL_ACTIVE ||
5843 element == EL_INVISIBLE_SAND_ACTIVE)
5845 if (game.light_time_left == 0)
5846 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5848 TEST_DrawLevelField(x, y);
5850 /* re-crumble neighbour fields, if needed */
5851 if (element == EL_INVISIBLE_SAND)
5852 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5857 static void RedrawAllInvisibleElementsForLenses()
5861 SCAN_PLAYFIELD(x, y)
5863 int element = Feld[x][y];
5865 if (element == EL_EMC_DRIPPER &&
5866 game.lenses_time_left > 0)
5868 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5869 TEST_DrawLevelField(x, y);
5871 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5872 game.lenses_time_left == 0)
5874 Feld[x][y] = EL_EMC_DRIPPER;
5875 TEST_DrawLevelField(x, y);
5877 else if (element == EL_INVISIBLE_STEELWALL ||
5878 element == EL_INVISIBLE_WALL ||
5879 element == EL_INVISIBLE_SAND)
5881 if (game.lenses_time_left > 0)
5882 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5884 TEST_DrawLevelField(x, y);
5886 /* uncrumble neighbour fields, if needed */
5887 if (element == EL_INVISIBLE_SAND)
5888 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5890 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5891 element == EL_INVISIBLE_WALL_ACTIVE ||
5892 element == EL_INVISIBLE_SAND_ACTIVE)
5894 if (game.lenses_time_left == 0)
5895 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5897 TEST_DrawLevelField(x, y);
5899 /* re-crumble neighbour fields, if needed */
5900 if (element == EL_INVISIBLE_SAND)
5901 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5906 static void RedrawAllInvisibleElementsForMagnifier()
5910 SCAN_PLAYFIELD(x, y)
5912 int element = Feld[x][y];
5914 if (element == EL_EMC_FAKE_GRASS &&
5915 game.magnify_time_left > 0)
5917 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5918 TEST_DrawLevelField(x, y);
5920 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5921 game.magnify_time_left == 0)
5923 Feld[x][y] = EL_EMC_FAKE_GRASS;
5924 TEST_DrawLevelField(x, y);
5926 else if (IS_GATE_GRAY(element) &&
5927 game.magnify_time_left > 0)
5929 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5930 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5931 IS_EM_GATE_GRAY(element) ?
5932 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5933 IS_EMC_GATE_GRAY(element) ?
5934 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5935 IS_DC_GATE_GRAY(element) ?
5936 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5938 TEST_DrawLevelField(x, y);
5940 else if (IS_GATE_GRAY_ACTIVE(element) &&
5941 game.magnify_time_left == 0)
5943 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5944 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5945 IS_EM_GATE_GRAY_ACTIVE(element) ?
5946 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5947 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5948 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5949 IS_DC_GATE_GRAY_ACTIVE(element) ?
5950 EL_DC_GATE_WHITE_GRAY :
5952 TEST_DrawLevelField(x, y);
5957 static void ToggleLightSwitch(int x, int y)
5959 int element = Feld[x][y];
5961 game.light_time_left =
5962 (element == EL_LIGHT_SWITCH ?
5963 level.time_light * FRAMES_PER_SECOND : 0);
5965 RedrawAllLightSwitchesAndInvisibleElements();
5968 static void ActivateTimegateSwitch(int x, int y)
5972 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5974 SCAN_PLAYFIELD(xx, yy)
5976 int element = Feld[xx][yy];
5978 if (element == EL_TIMEGATE_CLOSED ||
5979 element == EL_TIMEGATE_CLOSING)
5981 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5982 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5986 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5988 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5989 TEST_DrawLevelField(xx, yy);
5995 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5996 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5999 void Impact(int x, int y)
6001 boolean last_line = (y == lev_fieldy - 1);
6002 boolean object_hit = FALSE;
6003 boolean impact = (last_line || object_hit);
6004 int element = Feld[x][y];
6005 int smashed = EL_STEELWALL;
6007 if (!last_line) /* check if element below was hit */
6009 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6012 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6013 MovDir[x][y + 1] != MV_DOWN ||
6014 MovPos[x][y + 1] <= TILEY / 2));
6016 /* do not smash moving elements that left the smashed field in time */
6017 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6018 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6021 #if USE_QUICKSAND_IMPACT_BUGFIX
6022 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6024 RemoveMovingField(x, y + 1);
6025 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6026 Feld[x][y + 2] = EL_ROCK;
6027 TEST_DrawLevelField(x, y + 2);
6032 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6034 RemoveMovingField(x, y + 1);
6035 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6036 Feld[x][y + 2] = EL_ROCK;
6037 TEST_DrawLevelField(x, y + 2);
6044 smashed = MovingOrBlocked2Element(x, y + 1);
6046 impact = (last_line || object_hit);
6049 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6051 SplashAcid(x, y + 1);
6055 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6056 /* only reset graphic animation if graphic really changes after impact */
6058 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6060 ResetGfxAnimation(x, y);
6061 TEST_DrawLevelField(x, y);
6064 if (impact && CAN_EXPLODE_IMPACT(element))
6069 else if (impact && element == EL_PEARL &&
6070 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6072 ResetGfxAnimation(x, y);
6074 Feld[x][y] = EL_PEARL_BREAKING;
6075 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6078 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6080 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6085 if (impact && element == EL_AMOEBA_DROP)
6087 if (object_hit && IS_PLAYER(x, y + 1))
6088 KillPlayerUnlessEnemyProtected(x, y + 1);
6089 else if (object_hit && smashed == EL_PENGUIN)
6093 Feld[x][y] = EL_AMOEBA_GROWING;
6094 Store[x][y] = EL_AMOEBA_WET;
6096 ResetRandomAnimationValue(x, y);
6101 if (object_hit) /* check which object was hit */
6103 if ((CAN_PASS_MAGIC_WALL(element) &&
6104 (smashed == EL_MAGIC_WALL ||
6105 smashed == EL_BD_MAGIC_WALL)) ||
6106 (CAN_PASS_DC_MAGIC_WALL(element) &&
6107 smashed == EL_DC_MAGIC_WALL))
6110 int activated_magic_wall =
6111 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6112 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6113 EL_DC_MAGIC_WALL_ACTIVE);
6115 /* activate magic wall / mill */
6116 SCAN_PLAYFIELD(xx, yy)
6118 if (Feld[xx][yy] == smashed)
6119 Feld[xx][yy] = activated_magic_wall;
6122 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6123 game.magic_wall_active = TRUE;
6125 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6126 SND_MAGIC_WALL_ACTIVATING :
6127 smashed == EL_BD_MAGIC_WALL ?
6128 SND_BD_MAGIC_WALL_ACTIVATING :
6129 SND_DC_MAGIC_WALL_ACTIVATING));
6132 if (IS_PLAYER(x, y + 1))
6134 if (CAN_SMASH_PLAYER(element))
6136 KillPlayerUnlessEnemyProtected(x, y + 1);
6140 else if (smashed == EL_PENGUIN)
6142 if (CAN_SMASH_PLAYER(element))
6148 else if (element == EL_BD_DIAMOND)
6150 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6156 else if (((element == EL_SP_INFOTRON ||
6157 element == EL_SP_ZONK) &&
6158 (smashed == EL_SP_SNIKSNAK ||
6159 smashed == EL_SP_ELECTRON ||
6160 smashed == EL_SP_DISK_ORANGE)) ||
6161 (element == EL_SP_INFOTRON &&
6162 smashed == EL_SP_DISK_YELLOW))
6167 else if (CAN_SMASH_EVERYTHING(element))
6169 if (IS_CLASSIC_ENEMY(smashed) ||
6170 CAN_EXPLODE_SMASHED(smashed))
6175 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6177 if (smashed == EL_LAMP ||
6178 smashed == EL_LAMP_ACTIVE)
6183 else if (smashed == EL_NUT)
6185 Feld[x][y + 1] = EL_NUT_BREAKING;
6186 PlayLevelSound(x, y, SND_NUT_BREAKING);
6187 RaiseScoreElement(EL_NUT);
6190 else if (smashed == EL_PEARL)
6192 ResetGfxAnimation(x, y);
6194 Feld[x][y + 1] = EL_PEARL_BREAKING;
6195 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6198 else if (smashed == EL_DIAMOND)
6200 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6201 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6204 else if (IS_BELT_SWITCH(smashed))
6206 ToggleBeltSwitch(x, y + 1);
6208 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6209 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6210 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6211 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6213 ToggleSwitchgateSwitch(x, y + 1);
6215 else if (smashed == EL_LIGHT_SWITCH ||
6216 smashed == EL_LIGHT_SWITCH_ACTIVE)
6218 ToggleLightSwitch(x, y + 1);
6222 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6224 CheckElementChangeBySide(x, y + 1, smashed, element,
6225 CE_SWITCHED, CH_SIDE_TOP);
6226 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6232 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6237 /* play sound of magic wall / mill */
6239 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6240 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6241 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6243 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6244 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6245 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6246 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6247 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6248 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6253 /* play sound of object that hits the ground */
6254 if (last_line || object_hit)
6255 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6258 inline static void TurnRoundExt(int x, int y)
6270 { 0, 0 }, { 0, 0 }, { 0, 0 },
6275 int left, right, back;
6279 { MV_DOWN, MV_UP, MV_RIGHT },
6280 { MV_UP, MV_DOWN, MV_LEFT },
6282 { MV_LEFT, MV_RIGHT, MV_DOWN },
6286 { MV_RIGHT, MV_LEFT, MV_UP }
6289 int element = Feld[x][y];
6290 int move_pattern = element_info[element].move_pattern;
6292 int old_move_dir = MovDir[x][y];
6293 int left_dir = turn[old_move_dir].left;
6294 int right_dir = turn[old_move_dir].right;
6295 int back_dir = turn[old_move_dir].back;
6297 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6298 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6299 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6300 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6302 int left_x = x + left_dx, left_y = y + left_dy;
6303 int right_x = x + right_dx, right_y = y + right_dy;
6304 int move_x = x + move_dx, move_y = y + move_dy;
6308 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6310 TestIfBadThingTouchesOtherBadThing(x, y);
6312 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6313 MovDir[x][y] = right_dir;
6314 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6315 MovDir[x][y] = left_dir;
6317 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6319 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6322 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6324 TestIfBadThingTouchesOtherBadThing(x, y);
6326 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6327 MovDir[x][y] = left_dir;
6328 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6329 MovDir[x][y] = right_dir;
6331 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6333 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6336 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6338 TestIfBadThingTouchesOtherBadThing(x, y);
6340 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6341 MovDir[x][y] = left_dir;
6342 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6343 MovDir[x][y] = right_dir;
6345 if (MovDir[x][y] != old_move_dir)
6348 else if (element == EL_YAMYAM)
6350 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6351 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6353 if (can_turn_left && can_turn_right)
6354 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6355 else if (can_turn_left)
6356 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6357 else if (can_turn_right)
6358 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6360 MovDir[x][y] = back_dir;
6362 MovDelay[x][y] = 16 + 16 * RND(3);
6364 else if (element == EL_DARK_YAMYAM)
6366 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6368 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6371 if (can_turn_left && can_turn_right)
6372 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6373 else if (can_turn_left)
6374 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6375 else if (can_turn_right)
6376 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6378 MovDir[x][y] = back_dir;
6380 MovDelay[x][y] = 16 + 16 * RND(3);
6382 else if (element == EL_PACMAN)
6384 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6385 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6387 if (can_turn_left && can_turn_right)
6388 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6389 else if (can_turn_left)
6390 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6391 else if (can_turn_right)
6392 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6394 MovDir[x][y] = back_dir;
6396 MovDelay[x][y] = 6 + RND(40);
6398 else if (element == EL_PIG)
6400 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6401 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6402 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6403 boolean should_turn_left, should_turn_right, should_move_on;
6405 int rnd = RND(rnd_value);
6407 should_turn_left = (can_turn_left &&
6409 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6410 y + back_dy + left_dy)));
6411 should_turn_right = (can_turn_right &&
6413 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6414 y + back_dy + right_dy)));
6415 should_move_on = (can_move_on &&
6418 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6419 y + move_dy + left_dy) ||
6420 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6421 y + move_dy + right_dy)));
6423 if (should_turn_left || should_turn_right || should_move_on)
6425 if (should_turn_left && should_turn_right && should_move_on)
6426 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6427 rnd < 2 * rnd_value / 3 ? right_dir :
6429 else if (should_turn_left && should_turn_right)
6430 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6431 else if (should_turn_left && should_move_on)
6432 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6433 else if (should_turn_right && should_move_on)
6434 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6435 else if (should_turn_left)
6436 MovDir[x][y] = left_dir;
6437 else if (should_turn_right)
6438 MovDir[x][y] = right_dir;
6439 else if (should_move_on)
6440 MovDir[x][y] = old_move_dir;
6442 else if (can_move_on && rnd > rnd_value / 8)
6443 MovDir[x][y] = old_move_dir;
6444 else if (can_turn_left && can_turn_right)
6445 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6446 else if (can_turn_left && rnd > rnd_value / 8)
6447 MovDir[x][y] = left_dir;
6448 else if (can_turn_right && rnd > rnd_value/8)
6449 MovDir[x][y] = right_dir;
6451 MovDir[x][y] = back_dir;
6453 xx = x + move_xy[MovDir[x][y]].dx;
6454 yy = y + move_xy[MovDir[x][y]].dy;
6456 if (!IN_LEV_FIELD(xx, yy) ||
6457 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6458 MovDir[x][y] = old_move_dir;
6462 else if (element == EL_DRAGON)
6464 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6465 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6466 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6468 int rnd = RND(rnd_value);
6470 if (can_move_on && rnd > rnd_value / 8)
6471 MovDir[x][y] = old_move_dir;
6472 else if (can_turn_left && can_turn_right)
6473 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6474 else if (can_turn_left && rnd > rnd_value / 8)
6475 MovDir[x][y] = left_dir;
6476 else if (can_turn_right && rnd > rnd_value / 8)
6477 MovDir[x][y] = right_dir;
6479 MovDir[x][y] = back_dir;
6481 xx = x + move_xy[MovDir[x][y]].dx;
6482 yy = y + move_xy[MovDir[x][y]].dy;
6484 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6485 MovDir[x][y] = old_move_dir;
6489 else if (element == EL_MOLE)
6491 boolean can_move_on =
6492 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6493 IS_AMOEBOID(Feld[move_x][move_y]) ||
6494 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6497 boolean can_turn_left =
6498 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6499 IS_AMOEBOID(Feld[left_x][left_y])));
6501 boolean can_turn_right =
6502 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6503 IS_AMOEBOID(Feld[right_x][right_y])));
6505 if (can_turn_left && can_turn_right)
6506 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6507 else if (can_turn_left)
6508 MovDir[x][y] = left_dir;
6510 MovDir[x][y] = right_dir;
6513 if (MovDir[x][y] != old_move_dir)
6516 else if (element == EL_BALLOON)
6518 MovDir[x][y] = game.wind_direction;
6521 else if (element == EL_SPRING)
6523 if (MovDir[x][y] & MV_HORIZONTAL)
6525 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6526 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6528 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6529 ResetGfxAnimation(move_x, move_y);
6530 TEST_DrawLevelField(move_x, move_y);
6532 MovDir[x][y] = back_dir;
6534 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6535 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6536 MovDir[x][y] = MV_NONE;
6541 else if (element == EL_ROBOT ||
6542 element == EL_SATELLITE ||
6543 element == EL_PENGUIN ||
6544 element == EL_EMC_ANDROID)
6546 int attr_x = -1, attr_y = -1;
6557 for (i = 0; i < MAX_PLAYERS; i++)
6559 struct PlayerInfo *player = &stored_player[i];
6560 int jx = player->jx, jy = player->jy;
6562 if (!player->active)
6566 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6574 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6575 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6576 game.engine_version < VERSION_IDENT(3,1,0,0)))
6582 if (element == EL_PENGUIN)
6585 static int xy[4][2] =
6593 for (i = 0; i < NUM_DIRECTIONS; i++)
6595 int ex = x + xy[i][0];
6596 int ey = y + xy[i][1];
6598 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6599 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6600 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6601 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6610 MovDir[x][y] = MV_NONE;
6612 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6613 else if (attr_x > x)
6614 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6616 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6617 else if (attr_y > y)
6618 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6620 if (element == EL_ROBOT)
6624 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6625 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6626 Moving2Blocked(x, y, &newx, &newy);
6628 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6629 MovDelay[x][y] = 8 + 8 * !RND(3);
6631 MovDelay[x][y] = 16;
6633 else if (element == EL_PENGUIN)
6639 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6641 boolean first_horiz = RND(2);
6642 int new_move_dir = MovDir[x][y];
6645 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6646 Moving2Blocked(x, y, &newx, &newy);
6648 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6652 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6653 Moving2Blocked(x, y, &newx, &newy);
6655 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6658 MovDir[x][y] = old_move_dir;
6662 else if (element == EL_SATELLITE)
6668 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6670 boolean first_horiz = RND(2);
6671 int new_move_dir = MovDir[x][y];
6674 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6675 Moving2Blocked(x, y, &newx, &newy);
6677 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6681 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6682 Moving2Blocked(x, y, &newx, &newy);
6684 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6687 MovDir[x][y] = old_move_dir;
6691 else if (element == EL_EMC_ANDROID)
6693 static int check_pos[16] =
6695 -1, /* 0 => (invalid) */
6696 7, /* 1 => MV_LEFT */
6697 3, /* 2 => MV_RIGHT */
6698 -1, /* 3 => (invalid) */
6700 0, /* 5 => MV_LEFT | MV_UP */
6701 2, /* 6 => MV_RIGHT | MV_UP */
6702 -1, /* 7 => (invalid) */
6703 5, /* 8 => MV_DOWN */
6704 6, /* 9 => MV_LEFT | MV_DOWN */
6705 4, /* 10 => MV_RIGHT | MV_DOWN */
6706 -1, /* 11 => (invalid) */
6707 -1, /* 12 => (invalid) */
6708 -1, /* 13 => (invalid) */
6709 -1, /* 14 => (invalid) */
6710 -1, /* 15 => (invalid) */
6718 { -1, -1, MV_LEFT | MV_UP },
6720 { +1, -1, MV_RIGHT | MV_UP },
6721 { +1, 0, MV_RIGHT },
6722 { +1, +1, MV_RIGHT | MV_DOWN },
6724 { -1, +1, MV_LEFT | MV_DOWN },
6727 int start_pos, check_order;
6728 boolean can_clone = FALSE;
6731 /* check if there is any free field around current position */
6732 for (i = 0; i < 8; i++)
6734 int newx = x + check_xy[i].dx;
6735 int newy = y + check_xy[i].dy;
6737 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6745 if (can_clone) /* randomly find an element to clone */
6749 start_pos = check_pos[RND(8)];
6750 check_order = (RND(2) ? -1 : +1);
6752 for (i = 0; i < 8; i++)
6754 int pos_raw = start_pos + i * check_order;
6755 int pos = (pos_raw + 8) % 8;
6756 int newx = x + check_xy[pos].dx;
6757 int newy = y + check_xy[pos].dy;
6759 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6761 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6762 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6764 Store[x][y] = Feld[newx][newy];
6773 if (can_clone) /* randomly find a direction to move */
6777 start_pos = check_pos[RND(8)];
6778 check_order = (RND(2) ? -1 : +1);
6780 for (i = 0; i < 8; i++)
6782 int pos_raw = start_pos + i * check_order;
6783 int pos = (pos_raw + 8) % 8;
6784 int newx = x + check_xy[pos].dx;
6785 int newy = y + check_xy[pos].dy;
6786 int new_move_dir = check_xy[pos].dir;
6788 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6790 MovDir[x][y] = new_move_dir;
6791 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6800 if (can_clone) /* cloning and moving successful */
6803 /* cannot clone -- try to move towards player */
6805 start_pos = check_pos[MovDir[x][y] & 0x0f];
6806 check_order = (RND(2) ? -1 : +1);
6808 for (i = 0; i < 3; i++)
6810 /* first check start_pos, then previous/next or (next/previous) pos */
6811 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6812 int pos = (pos_raw + 8) % 8;
6813 int newx = x + check_xy[pos].dx;
6814 int newy = y + check_xy[pos].dy;
6815 int new_move_dir = check_xy[pos].dir;
6817 if (IS_PLAYER(newx, newy))
6820 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6822 MovDir[x][y] = new_move_dir;
6823 MovDelay[x][y] = level.android_move_time * 8 + 1;
6830 else if (move_pattern == MV_TURNING_LEFT ||
6831 move_pattern == MV_TURNING_RIGHT ||
6832 move_pattern == MV_TURNING_LEFT_RIGHT ||
6833 move_pattern == MV_TURNING_RIGHT_LEFT ||
6834 move_pattern == MV_TURNING_RANDOM ||
6835 move_pattern == MV_ALL_DIRECTIONS)
6837 boolean can_turn_left =
6838 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6839 boolean can_turn_right =
6840 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6842 if (element_info[element].move_stepsize == 0) /* "not moving" */
6845 if (move_pattern == MV_TURNING_LEFT)
6846 MovDir[x][y] = left_dir;
6847 else if (move_pattern == MV_TURNING_RIGHT)
6848 MovDir[x][y] = right_dir;
6849 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6850 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6851 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6852 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6853 else if (move_pattern == MV_TURNING_RANDOM)
6854 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6855 can_turn_right && !can_turn_left ? right_dir :
6856 RND(2) ? left_dir : right_dir);
6857 else if (can_turn_left && can_turn_right)
6858 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6859 else if (can_turn_left)
6860 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6861 else if (can_turn_right)
6862 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6864 MovDir[x][y] = back_dir;
6866 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6868 else if (move_pattern == MV_HORIZONTAL ||
6869 move_pattern == MV_VERTICAL)
6871 if (move_pattern & old_move_dir)
6872 MovDir[x][y] = back_dir;
6873 else if (move_pattern == MV_HORIZONTAL)
6874 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6875 else if (move_pattern == MV_VERTICAL)
6876 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6878 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6880 else if (move_pattern & MV_ANY_DIRECTION)
6882 MovDir[x][y] = move_pattern;
6883 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6885 else if (move_pattern & MV_WIND_DIRECTION)
6887 MovDir[x][y] = game.wind_direction;
6888 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6890 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6892 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6893 MovDir[x][y] = left_dir;
6894 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6895 MovDir[x][y] = right_dir;
6897 if (MovDir[x][y] != old_move_dir)
6898 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6900 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6902 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6903 MovDir[x][y] = right_dir;
6904 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6905 MovDir[x][y] = left_dir;
6907 if (MovDir[x][y] != old_move_dir)
6908 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6910 else if (move_pattern == MV_TOWARDS_PLAYER ||
6911 move_pattern == MV_AWAY_FROM_PLAYER)
6913 int attr_x = -1, attr_y = -1;
6915 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6926 for (i = 0; i < MAX_PLAYERS; i++)
6928 struct PlayerInfo *player = &stored_player[i];
6929 int jx = player->jx, jy = player->jy;
6931 if (!player->active)
6935 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6943 MovDir[x][y] = MV_NONE;
6945 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6946 else if (attr_x > x)
6947 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6949 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6950 else if (attr_y > y)
6951 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6953 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6955 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6957 boolean first_horiz = RND(2);
6958 int new_move_dir = MovDir[x][y];
6960 if (element_info[element].move_stepsize == 0) /* "not moving" */
6962 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6963 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6969 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6970 Moving2Blocked(x, y, &newx, &newy);
6972 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6976 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6977 Moving2Blocked(x, y, &newx, &newy);
6979 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6982 MovDir[x][y] = old_move_dir;
6985 else if (move_pattern == MV_WHEN_PUSHED ||
6986 move_pattern == MV_WHEN_DROPPED)
6988 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6989 MovDir[x][y] = MV_NONE;
6993 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6995 static int test_xy[7][2] =
7005 static int test_dir[7] =
7015 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7016 int move_preference = -1000000; /* start with very low preference */
7017 int new_move_dir = MV_NONE;
7018 int start_test = RND(4);
7021 for (i = 0; i < NUM_DIRECTIONS; i++)
7023 int move_dir = test_dir[start_test + i];
7024 int move_dir_preference;
7026 xx = x + test_xy[start_test + i][0];
7027 yy = y + test_xy[start_test + i][1];
7029 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7030 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7032 new_move_dir = move_dir;
7037 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7040 move_dir_preference = -1 * RunnerVisit[xx][yy];
7041 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7042 move_dir_preference = PlayerVisit[xx][yy];
7044 if (move_dir_preference > move_preference)
7046 /* prefer field that has not been visited for the longest time */
7047 move_preference = move_dir_preference;
7048 new_move_dir = move_dir;
7050 else if (move_dir_preference == move_preference &&
7051 move_dir == old_move_dir)
7053 /* prefer last direction when all directions are preferred equally */
7054 move_preference = move_dir_preference;
7055 new_move_dir = move_dir;
7059 MovDir[x][y] = new_move_dir;
7060 if (old_move_dir != new_move_dir)
7061 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7065 static void TurnRound(int x, int y)
7067 int direction = MovDir[x][y];
7071 GfxDir[x][y] = MovDir[x][y];
7073 if (direction != MovDir[x][y])
7077 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7079 ResetGfxFrame(x, y, FALSE);
7082 static boolean JustBeingPushed(int x, int y)
7086 for (i = 0; i < MAX_PLAYERS; i++)
7088 struct PlayerInfo *player = &stored_player[i];
7090 if (player->active && player->is_pushing && player->MovPos)
7092 int next_jx = player->jx + (player->jx - player->last_jx);
7093 int next_jy = player->jy + (player->jy - player->last_jy);
7095 if (x == next_jx && y == next_jy)
7103 void StartMoving(int x, int y)
7105 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7106 int element = Feld[x][y];
7111 if (MovDelay[x][y] == 0)
7112 GfxAction[x][y] = ACTION_DEFAULT;
7114 if (CAN_FALL(element) && y < lev_fieldy - 1)
7116 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7117 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7118 if (JustBeingPushed(x, y))
7121 if (element == EL_QUICKSAND_FULL)
7123 if (IS_FREE(x, y + 1))
7125 InitMovingField(x, y, MV_DOWN);
7126 started_moving = TRUE;
7128 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7129 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7130 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7131 Store[x][y] = EL_ROCK;
7133 Store[x][y] = EL_ROCK;
7136 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7138 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7140 if (!MovDelay[x][y])
7142 MovDelay[x][y] = TILEY + 1;
7144 ResetGfxAnimation(x, y);
7145 ResetGfxAnimation(x, y + 1);
7150 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7151 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7158 Feld[x][y] = EL_QUICKSAND_EMPTY;
7159 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7160 Store[x][y + 1] = Store[x][y];
7163 PlayLevelSoundAction(x, y, ACTION_FILLING);
7165 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7167 if (!MovDelay[x][y])
7169 MovDelay[x][y] = TILEY + 1;
7171 ResetGfxAnimation(x, y);
7172 ResetGfxAnimation(x, y + 1);
7177 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7178 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7185 Feld[x][y] = EL_QUICKSAND_EMPTY;
7186 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7187 Store[x][y + 1] = Store[x][y];
7190 PlayLevelSoundAction(x, y, ACTION_FILLING);
7193 else if (element == EL_QUICKSAND_FAST_FULL)
7195 if (IS_FREE(x, y + 1))
7197 InitMovingField(x, y, MV_DOWN);
7198 started_moving = TRUE;
7200 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7201 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7202 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7203 Store[x][y] = EL_ROCK;
7205 Store[x][y] = EL_ROCK;
7208 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7210 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7212 if (!MovDelay[x][y])
7214 MovDelay[x][y] = TILEY + 1;
7216 ResetGfxAnimation(x, y);
7217 ResetGfxAnimation(x, y + 1);
7222 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7223 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7230 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7231 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7232 Store[x][y + 1] = Store[x][y];
7235 PlayLevelSoundAction(x, y, ACTION_FILLING);
7237 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7239 if (!MovDelay[x][y])
7241 MovDelay[x][y] = TILEY + 1;
7243 ResetGfxAnimation(x, y);
7244 ResetGfxAnimation(x, y + 1);
7249 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7250 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7257 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7258 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7259 Store[x][y + 1] = Store[x][y];
7262 PlayLevelSoundAction(x, y, ACTION_FILLING);
7265 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7266 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7268 InitMovingField(x, y, MV_DOWN);
7269 started_moving = TRUE;
7271 Feld[x][y] = EL_QUICKSAND_FILLING;
7272 Store[x][y] = element;
7274 PlayLevelSoundAction(x, y, ACTION_FILLING);
7276 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7277 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7279 InitMovingField(x, y, MV_DOWN);
7280 started_moving = TRUE;
7282 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7283 Store[x][y] = element;
7285 PlayLevelSoundAction(x, y, ACTION_FILLING);
7287 else if (element == EL_MAGIC_WALL_FULL)
7289 if (IS_FREE(x, y + 1))
7291 InitMovingField(x, y, MV_DOWN);
7292 started_moving = TRUE;
7294 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7295 Store[x][y] = EL_CHANGED(Store[x][y]);
7297 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7299 if (!MovDelay[x][y])
7300 MovDelay[x][y] = TILEY / 4 + 1;
7309 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7310 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7311 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7315 else if (element == EL_BD_MAGIC_WALL_FULL)
7317 if (IS_FREE(x, y + 1))
7319 InitMovingField(x, y, MV_DOWN);
7320 started_moving = TRUE;
7322 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7323 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7325 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7327 if (!MovDelay[x][y])
7328 MovDelay[x][y] = TILEY / 4 + 1;
7337 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7338 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7339 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7343 else if (element == EL_DC_MAGIC_WALL_FULL)
7345 if (IS_FREE(x, y + 1))
7347 InitMovingField(x, y, MV_DOWN);
7348 started_moving = TRUE;
7350 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7351 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7353 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7355 if (!MovDelay[x][y])
7356 MovDelay[x][y] = TILEY / 4 + 1;
7365 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7366 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7367 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7371 else if ((CAN_PASS_MAGIC_WALL(element) &&
7372 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7373 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7374 (CAN_PASS_DC_MAGIC_WALL(element) &&
7375 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7378 InitMovingField(x, y, MV_DOWN);
7379 started_moving = TRUE;
7382 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7383 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7384 EL_DC_MAGIC_WALL_FILLING);
7385 Store[x][y] = element;
7387 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7389 SplashAcid(x, y + 1);
7391 InitMovingField(x, y, MV_DOWN);
7392 started_moving = TRUE;
7394 Store[x][y] = EL_ACID;
7397 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7398 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7399 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7400 CAN_FALL(element) && WasJustFalling[x][y] &&
7401 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7403 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7404 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7405 (Feld[x][y + 1] == EL_BLOCKED)))
7407 /* this is needed for a special case not covered by calling "Impact()"
7408 from "ContinueMoving()": if an element moves to a tile directly below
7409 another element which was just falling on that tile (which was empty
7410 in the previous frame), the falling element above would just stop
7411 instead of smashing the element below (in previous version, the above
7412 element was just checked for "moving" instead of "falling", resulting
7413 in incorrect smashes caused by horizontal movement of the above
7414 element; also, the case of the player being the element to smash was
7415 simply not covered here... :-/ ) */
7417 CheckCollision[x][y] = 0;
7418 CheckImpact[x][y] = 0;
7422 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7424 if (MovDir[x][y] == MV_NONE)
7426 InitMovingField(x, y, MV_DOWN);
7427 started_moving = TRUE;
7430 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7432 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7433 MovDir[x][y] = MV_DOWN;
7435 InitMovingField(x, y, MV_DOWN);
7436 started_moving = TRUE;
7438 else if (element == EL_AMOEBA_DROP)
7440 Feld[x][y] = EL_AMOEBA_GROWING;
7441 Store[x][y] = EL_AMOEBA_WET;
7443 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7444 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7445 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7446 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7448 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7449 (IS_FREE(x - 1, y + 1) ||
7450 Feld[x - 1][y + 1] == EL_ACID));
7451 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7452 (IS_FREE(x + 1, y + 1) ||
7453 Feld[x + 1][y + 1] == EL_ACID));
7454 boolean can_fall_any = (can_fall_left || can_fall_right);
7455 boolean can_fall_both = (can_fall_left && can_fall_right);
7456 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7458 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7460 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7461 can_fall_right = FALSE;
7462 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7463 can_fall_left = FALSE;
7464 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7465 can_fall_right = FALSE;
7466 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7467 can_fall_left = FALSE;
7469 can_fall_any = (can_fall_left || can_fall_right);
7470 can_fall_both = FALSE;
7475 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7476 can_fall_right = FALSE; /* slip down on left side */
7478 can_fall_left = !(can_fall_right = RND(2));
7480 can_fall_both = FALSE;
7485 /* if not determined otherwise, prefer left side for slipping down */
7486 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7487 started_moving = TRUE;
7490 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7492 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7493 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7494 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7495 int belt_dir = game.belt_dir[belt_nr];
7497 if ((belt_dir == MV_LEFT && left_is_free) ||
7498 (belt_dir == MV_RIGHT && right_is_free))
7500 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7502 InitMovingField(x, y, belt_dir);
7503 started_moving = TRUE;
7505 Pushed[x][y] = TRUE;
7506 Pushed[nextx][y] = TRUE;
7508 GfxAction[x][y] = ACTION_DEFAULT;
7512 MovDir[x][y] = 0; /* if element was moving, stop it */
7517 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7518 if (CAN_MOVE(element) && !started_moving)
7520 int move_pattern = element_info[element].move_pattern;
7523 Moving2Blocked(x, y, &newx, &newy);
7525 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7528 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7529 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7531 WasJustMoving[x][y] = 0;
7532 CheckCollision[x][y] = 0;
7534 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7536 if (Feld[x][y] != element) /* element has changed */
7540 if (!MovDelay[x][y]) /* start new movement phase */
7542 /* all objects that can change their move direction after each step
7543 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7545 if (element != EL_YAMYAM &&
7546 element != EL_DARK_YAMYAM &&
7547 element != EL_PACMAN &&
7548 !(move_pattern & MV_ANY_DIRECTION) &&
7549 move_pattern != MV_TURNING_LEFT &&
7550 move_pattern != MV_TURNING_RIGHT &&
7551 move_pattern != MV_TURNING_LEFT_RIGHT &&
7552 move_pattern != MV_TURNING_RIGHT_LEFT &&
7553 move_pattern != MV_TURNING_RANDOM)
7557 if (MovDelay[x][y] && (element == EL_BUG ||
7558 element == EL_SPACESHIP ||
7559 element == EL_SP_SNIKSNAK ||
7560 element == EL_SP_ELECTRON ||
7561 element == EL_MOLE))
7562 TEST_DrawLevelField(x, y);
7566 if (MovDelay[x][y]) /* wait some time before next movement */
7570 if (element == EL_ROBOT ||
7571 element == EL_YAMYAM ||
7572 element == EL_DARK_YAMYAM)
7574 DrawLevelElementAnimationIfNeeded(x, y, element);
7575 PlayLevelSoundAction(x, y, ACTION_WAITING);
7577 else if (element == EL_SP_ELECTRON)
7578 DrawLevelElementAnimationIfNeeded(x, y, element);
7579 else if (element == EL_DRAGON)
7582 int dir = MovDir[x][y];
7583 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7584 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7585 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7586 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7587 dir == MV_UP ? IMG_FLAMES_1_UP :
7588 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7589 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7591 GfxAction[x][y] = ACTION_ATTACKING;
7593 if (IS_PLAYER(x, y))
7594 DrawPlayerField(x, y);
7596 TEST_DrawLevelField(x, y);
7598 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7600 for (i = 1; i <= 3; i++)
7602 int xx = x + i * dx;
7603 int yy = y + i * dy;
7604 int sx = SCREENX(xx);
7605 int sy = SCREENY(yy);
7606 int flame_graphic = graphic + (i - 1);
7608 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7613 int flamed = MovingOrBlocked2Element(xx, yy);
7615 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7618 RemoveMovingField(xx, yy);
7620 ChangeDelay[xx][yy] = 0;
7622 Feld[xx][yy] = EL_FLAMES;
7624 if (IN_SCR_FIELD(sx, sy))
7626 TEST_DrawLevelFieldCrumbled(xx, yy);
7627 DrawGraphic(sx, sy, flame_graphic, frame);
7632 if (Feld[xx][yy] == EL_FLAMES)
7633 Feld[xx][yy] = EL_EMPTY;
7634 TEST_DrawLevelField(xx, yy);
7639 if (MovDelay[x][y]) /* element still has to wait some time */
7641 PlayLevelSoundAction(x, y, ACTION_WAITING);
7647 /* now make next step */
7649 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7651 if (DONT_COLLIDE_WITH(element) &&
7652 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7653 !PLAYER_ENEMY_PROTECTED(newx, newy))
7655 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7660 else if (CAN_MOVE_INTO_ACID(element) &&
7661 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7662 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7663 (MovDir[x][y] == MV_DOWN ||
7664 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7666 SplashAcid(newx, newy);
7667 Store[x][y] = EL_ACID;
7669 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7671 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7672 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7673 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7674 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7677 TEST_DrawLevelField(x, y);
7679 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7680 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7681 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7683 local_player->friends_still_needed--;
7684 if (!local_player->friends_still_needed &&
7685 !local_player->GameOver && AllPlayersGone)
7686 PlayerWins(local_player);
7690 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7692 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7693 TEST_DrawLevelField(newx, newy);
7695 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7697 else if (!IS_FREE(newx, newy))
7699 GfxAction[x][y] = ACTION_WAITING;
7701 if (IS_PLAYER(x, y))
7702 DrawPlayerField(x, y);
7704 TEST_DrawLevelField(x, y);
7709 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7711 if (IS_FOOD_PIG(Feld[newx][newy]))
7713 if (IS_MOVING(newx, newy))
7714 RemoveMovingField(newx, newy);
7717 Feld[newx][newy] = EL_EMPTY;
7718 TEST_DrawLevelField(newx, newy);
7721 PlayLevelSound(x, y, SND_PIG_DIGGING);
7723 else if (!IS_FREE(newx, newy))
7725 if (IS_PLAYER(x, y))
7726 DrawPlayerField(x, y);
7728 TEST_DrawLevelField(x, y);
7733 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7735 if (Store[x][y] != EL_EMPTY)
7737 boolean can_clone = FALSE;
7740 /* check if element to clone is still there */
7741 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7743 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7751 /* cannot clone or target field not free anymore -- do not clone */
7752 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7753 Store[x][y] = EL_EMPTY;
7756 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7758 if (IS_MV_DIAGONAL(MovDir[x][y]))
7760 int diagonal_move_dir = MovDir[x][y];
7761 int stored = Store[x][y];
7762 int change_delay = 8;
7765 /* android is moving diagonally */
7767 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7769 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7770 GfxElement[x][y] = EL_EMC_ANDROID;
7771 GfxAction[x][y] = ACTION_SHRINKING;
7772 GfxDir[x][y] = diagonal_move_dir;
7773 ChangeDelay[x][y] = change_delay;
7775 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7778 DrawLevelGraphicAnimation(x, y, graphic);
7779 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7781 if (Feld[newx][newy] == EL_ACID)
7783 SplashAcid(newx, newy);
7788 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7790 Store[newx][newy] = EL_EMC_ANDROID;
7791 GfxElement[newx][newy] = EL_EMC_ANDROID;
7792 GfxAction[newx][newy] = ACTION_GROWING;
7793 GfxDir[newx][newy] = diagonal_move_dir;
7794 ChangeDelay[newx][newy] = change_delay;
7796 graphic = el_act_dir2img(GfxElement[newx][newy],
7797 GfxAction[newx][newy], GfxDir[newx][newy]);
7799 DrawLevelGraphicAnimation(newx, newy, graphic);
7800 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7806 Feld[newx][newy] = EL_EMPTY;
7807 TEST_DrawLevelField(newx, newy);
7809 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7812 else if (!IS_FREE(newx, newy))
7817 else if (IS_CUSTOM_ELEMENT(element) &&
7818 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7820 if (!DigFieldByCE(newx, newy, element))
7823 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7825 RunnerVisit[x][y] = FrameCounter;
7826 PlayerVisit[x][y] /= 8; /* expire player visit path */
7829 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7831 if (!IS_FREE(newx, newy))
7833 if (IS_PLAYER(x, y))
7834 DrawPlayerField(x, y);
7836 TEST_DrawLevelField(x, y);
7842 boolean wanna_flame = !RND(10);
7843 int dx = newx - x, dy = newy - y;
7844 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7845 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7846 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7847 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7848 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7849 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7852 IS_CLASSIC_ENEMY(element1) ||
7853 IS_CLASSIC_ENEMY(element2)) &&
7854 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7855 element1 != EL_FLAMES && element2 != EL_FLAMES)
7857 ResetGfxAnimation(x, y);
7858 GfxAction[x][y] = ACTION_ATTACKING;
7860 if (IS_PLAYER(x, y))
7861 DrawPlayerField(x, y);
7863 TEST_DrawLevelField(x, y);
7865 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7867 MovDelay[x][y] = 50;
7869 Feld[newx][newy] = EL_FLAMES;
7870 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7871 Feld[newx1][newy1] = EL_FLAMES;
7872 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7873 Feld[newx2][newy2] = EL_FLAMES;
7879 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7880 Feld[newx][newy] == EL_DIAMOND)
7882 if (IS_MOVING(newx, newy))
7883 RemoveMovingField(newx, newy);
7886 Feld[newx][newy] = EL_EMPTY;
7887 TEST_DrawLevelField(newx, newy);
7890 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7892 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7893 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7895 if (AmoebaNr[newx][newy])
7897 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7898 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7899 Feld[newx][newy] == EL_BD_AMOEBA)
7900 AmoebaCnt[AmoebaNr[newx][newy]]--;
7903 if (IS_MOVING(newx, newy))
7905 RemoveMovingField(newx, newy);
7909 Feld[newx][newy] = EL_EMPTY;
7910 TEST_DrawLevelField(newx, newy);
7913 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7915 else if ((element == EL_PACMAN || element == EL_MOLE)
7916 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7918 if (AmoebaNr[newx][newy])
7920 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7921 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7922 Feld[newx][newy] == EL_BD_AMOEBA)
7923 AmoebaCnt[AmoebaNr[newx][newy]]--;
7926 if (element == EL_MOLE)
7928 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7929 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7931 ResetGfxAnimation(x, y);
7932 GfxAction[x][y] = ACTION_DIGGING;
7933 TEST_DrawLevelField(x, y);
7935 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7937 return; /* wait for shrinking amoeba */
7939 else /* element == EL_PACMAN */
7941 Feld[newx][newy] = EL_EMPTY;
7942 TEST_DrawLevelField(newx, newy);
7943 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7946 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7947 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7948 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7950 /* wait for shrinking amoeba to completely disappear */
7953 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7955 /* object was running against a wall */
7959 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7960 DrawLevelElementAnimation(x, y, element);
7962 if (DONT_TOUCH(element))
7963 TestIfBadThingTouchesPlayer(x, y);
7968 InitMovingField(x, y, MovDir[x][y]);
7970 PlayLevelSoundAction(x, y, ACTION_MOVING);
7974 ContinueMoving(x, y);
7977 void ContinueMoving(int x, int y)
7979 int element = Feld[x][y];
7980 struct ElementInfo *ei = &element_info[element];
7981 int direction = MovDir[x][y];
7982 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7983 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7984 int newx = x + dx, newy = y + dy;
7985 int stored = Store[x][y];
7986 int stored_new = Store[newx][newy];
7987 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7988 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7989 boolean last_line = (newy == lev_fieldy - 1);
7991 MovPos[x][y] += getElementMoveStepsize(x, y);
7993 if (pushed_by_player) /* special case: moving object pushed by player */
7994 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7996 if (ABS(MovPos[x][y]) < TILEX)
7998 TEST_DrawLevelField(x, y);
8000 return; /* element is still moving */
8003 /* element reached destination field */
8005 Feld[x][y] = EL_EMPTY;
8006 Feld[newx][newy] = element;
8007 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8009 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8011 element = Feld[newx][newy] = EL_ACID;
8013 else if (element == EL_MOLE)
8015 Feld[x][y] = EL_SAND;
8017 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8019 else if (element == EL_QUICKSAND_FILLING)
8021 element = Feld[newx][newy] = get_next_element(element);
8022 Store[newx][newy] = Store[x][y];
8024 else if (element == EL_QUICKSAND_EMPTYING)
8026 Feld[x][y] = get_next_element(element);
8027 element = Feld[newx][newy] = Store[x][y];
8029 else if (element == EL_QUICKSAND_FAST_FILLING)
8031 element = Feld[newx][newy] = get_next_element(element);
8032 Store[newx][newy] = Store[x][y];
8034 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8036 Feld[x][y] = get_next_element(element);
8037 element = Feld[newx][newy] = Store[x][y];
8039 else if (element == EL_MAGIC_WALL_FILLING)
8041 element = Feld[newx][newy] = get_next_element(element);
8042 if (!game.magic_wall_active)
8043 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8044 Store[newx][newy] = Store[x][y];
8046 else if (element == EL_MAGIC_WALL_EMPTYING)
8048 Feld[x][y] = get_next_element(element);
8049 if (!game.magic_wall_active)
8050 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8051 element = Feld[newx][newy] = Store[x][y];
8053 InitField(newx, newy, FALSE);
8055 else if (element == EL_BD_MAGIC_WALL_FILLING)
8057 element = Feld[newx][newy] = get_next_element(element);
8058 if (!game.magic_wall_active)
8059 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8060 Store[newx][newy] = Store[x][y];
8062 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8064 Feld[x][y] = get_next_element(element);
8065 if (!game.magic_wall_active)
8066 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8067 element = Feld[newx][newy] = Store[x][y];
8069 InitField(newx, newy, FALSE);
8071 else if (element == EL_DC_MAGIC_WALL_FILLING)
8073 element = Feld[newx][newy] = get_next_element(element);
8074 if (!game.magic_wall_active)
8075 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8076 Store[newx][newy] = Store[x][y];
8078 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8080 Feld[x][y] = get_next_element(element);
8081 if (!game.magic_wall_active)
8082 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8083 element = Feld[newx][newy] = Store[x][y];
8085 InitField(newx, newy, FALSE);
8087 else if (element == EL_AMOEBA_DROPPING)
8089 Feld[x][y] = get_next_element(element);
8090 element = Feld[newx][newy] = Store[x][y];
8092 else if (element == EL_SOKOBAN_OBJECT)
8095 Feld[x][y] = Back[x][y];
8097 if (Back[newx][newy])
8098 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8100 Back[x][y] = Back[newx][newy] = 0;
8103 Store[x][y] = EL_EMPTY;
8108 MovDelay[newx][newy] = 0;
8110 if (CAN_CHANGE_OR_HAS_ACTION(element))
8112 /* copy element change control values to new field */
8113 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8114 ChangePage[newx][newy] = ChangePage[x][y];
8115 ChangeCount[newx][newy] = ChangeCount[x][y];
8116 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8119 CustomValue[newx][newy] = CustomValue[x][y];
8121 ChangeDelay[x][y] = 0;
8122 ChangePage[x][y] = -1;
8123 ChangeCount[x][y] = 0;
8124 ChangeEvent[x][y] = -1;
8126 CustomValue[x][y] = 0;
8128 /* copy animation control values to new field */
8129 GfxFrame[newx][newy] = GfxFrame[x][y];
8130 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8131 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8132 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8134 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8136 /* some elements can leave other elements behind after moving */
8137 if (ei->move_leave_element != EL_EMPTY &&
8138 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8139 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8141 int move_leave_element = ei->move_leave_element;
8143 /* this makes it possible to leave the removed element again */
8144 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8145 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8147 Feld[x][y] = move_leave_element;
8149 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8150 MovDir[x][y] = direction;
8152 InitField(x, y, FALSE);
8154 if (GFX_CRUMBLED(Feld[x][y]))
8155 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8157 if (ELEM_IS_PLAYER(move_leave_element))
8158 RelocatePlayer(x, y, move_leave_element);
8161 /* do this after checking for left-behind element */
8162 ResetGfxAnimation(x, y); /* reset animation values for old field */
8164 if (!CAN_MOVE(element) ||
8165 (CAN_FALL(element) && direction == MV_DOWN &&
8166 (element == EL_SPRING ||
8167 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8168 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8169 GfxDir[x][y] = MovDir[newx][newy] = 0;
8171 TEST_DrawLevelField(x, y);
8172 TEST_DrawLevelField(newx, newy);
8174 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8176 /* prevent pushed element from moving on in pushed direction */
8177 if (pushed_by_player && CAN_MOVE(element) &&
8178 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8179 !(element_info[element].move_pattern & direction))
8180 TurnRound(newx, newy);
8182 /* prevent elements on conveyor belt from moving on in last direction */
8183 if (pushed_by_conveyor && CAN_FALL(element) &&
8184 direction & MV_HORIZONTAL)
8185 MovDir[newx][newy] = 0;
8187 if (!pushed_by_player)
8189 int nextx = newx + dx, nexty = newy + dy;
8190 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8192 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8194 if (CAN_FALL(element) && direction == MV_DOWN)
8195 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8197 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8198 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8200 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8201 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8204 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8206 TestIfBadThingTouchesPlayer(newx, newy);
8207 TestIfBadThingTouchesFriend(newx, newy);
8209 if (!IS_CUSTOM_ELEMENT(element))
8210 TestIfBadThingTouchesOtherBadThing(newx, newy);
8212 else if (element == EL_PENGUIN)
8213 TestIfFriendTouchesBadThing(newx, newy);
8215 if (DONT_GET_HIT_BY(element))
8217 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8220 /* give the player one last chance (one more frame) to move away */
8221 if (CAN_FALL(element) && direction == MV_DOWN &&
8222 (last_line || (!IS_FREE(x, newy + 1) &&
8223 (!IS_PLAYER(x, newy + 1) ||
8224 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8227 if (pushed_by_player && !game.use_change_when_pushing_bug)
8229 int push_side = MV_DIR_OPPOSITE(direction);
8230 struct PlayerInfo *player = PLAYERINFO(x, y);
8232 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8233 player->index_bit, push_side);
8234 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8235 player->index_bit, push_side);
8238 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8239 MovDelay[newx][newy] = 1;
8241 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8243 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8244 TestIfElementHitsCustomElement(newx, newy, direction);
8245 TestIfPlayerTouchesCustomElement(newx, newy);
8246 TestIfElementTouchesCustomElement(newx, newy);
8248 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8249 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8250 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8251 MV_DIR_OPPOSITE(direction));
8254 int AmoebeNachbarNr(int ax, int ay)
8257 int element = Feld[ax][ay];
8259 static int xy[4][2] =
8267 for (i = 0; i < NUM_DIRECTIONS; i++)
8269 int x = ax + xy[i][0];
8270 int y = ay + xy[i][1];
8272 if (!IN_LEV_FIELD(x, y))
8275 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8276 group_nr = AmoebaNr[x][y];
8282 void AmoebenVereinigen(int ax, int ay)
8284 int i, x, y, xx, yy;
8285 int new_group_nr = AmoebaNr[ax][ay];
8286 static int xy[4][2] =
8294 if (new_group_nr == 0)
8297 for (i = 0; i < NUM_DIRECTIONS; i++)
8302 if (!IN_LEV_FIELD(x, y))
8305 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8306 Feld[x][y] == EL_BD_AMOEBA ||
8307 Feld[x][y] == EL_AMOEBA_DEAD) &&
8308 AmoebaNr[x][y] != new_group_nr)
8310 int old_group_nr = AmoebaNr[x][y];
8312 if (old_group_nr == 0)
8315 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8316 AmoebaCnt[old_group_nr] = 0;
8317 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8318 AmoebaCnt2[old_group_nr] = 0;
8320 SCAN_PLAYFIELD(xx, yy)
8322 if (AmoebaNr[xx][yy] == old_group_nr)
8323 AmoebaNr[xx][yy] = new_group_nr;
8329 void AmoebeUmwandeln(int ax, int ay)
8333 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8335 int group_nr = AmoebaNr[ax][ay];
8340 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8341 printf("AmoebeUmwandeln(): This should never happen!\n");
8346 SCAN_PLAYFIELD(x, y)
8348 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8351 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8355 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8356 SND_AMOEBA_TURNING_TO_GEM :
8357 SND_AMOEBA_TURNING_TO_ROCK));
8362 static int xy[4][2] =
8370 for (i = 0; i < NUM_DIRECTIONS; i++)
8375 if (!IN_LEV_FIELD(x, y))
8378 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8380 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8381 SND_AMOEBA_TURNING_TO_GEM :
8382 SND_AMOEBA_TURNING_TO_ROCK));
8389 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8392 int group_nr = AmoebaNr[ax][ay];
8393 boolean done = FALSE;
8398 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8399 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8404 SCAN_PLAYFIELD(x, y)
8406 if (AmoebaNr[x][y] == group_nr &&
8407 (Feld[x][y] == EL_AMOEBA_DEAD ||
8408 Feld[x][y] == EL_BD_AMOEBA ||
8409 Feld[x][y] == EL_AMOEBA_GROWING))
8412 Feld[x][y] = new_element;
8413 InitField(x, y, FALSE);
8414 TEST_DrawLevelField(x, y);
8420 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8421 SND_BD_AMOEBA_TURNING_TO_ROCK :
8422 SND_BD_AMOEBA_TURNING_TO_GEM));
8425 void AmoebeWaechst(int x, int y)
8427 static unsigned int sound_delay = 0;
8428 static unsigned int sound_delay_value = 0;
8430 if (!MovDelay[x][y]) /* start new growing cycle */
8434 if (DelayReached(&sound_delay, sound_delay_value))
8436 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8437 sound_delay_value = 30;
8441 if (MovDelay[x][y]) /* wait some time before growing bigger */
8444 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8446 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8447 6 - MovDelay[x][y]);
8449 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8452 if (!MovDelay[x][y])
8454 Feld[x][y] = Store[x][y];
8456 TEST_DrawLevelField(x, y);
8461 void AmoebaDisappearing(int x, int y)
8463 static unsigned int sound_delay = 0;
8464 static unsigned int sound_delay_value = 0;
8466 if (!MovDelay[x][y]) /* start new shrinking cycle */
8470 if (DelayReached(&sound_delay, sound_delay_value))
8471 sound_delay_value = 30;
8474 if (MovDelay[x][y]) /* wait some time before shrinking */
8477 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8479 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8480 6 - MovDelay[x][y]);
8482 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8485 if (!MovDelay[x][y])
8487 Feld[x][y] = EL_EMPTY;
8488 TEST_DrawLevelField(x, y);
8490 /* don't let mole enter this field in this cycle;
8491 (give priority to objects falling to this field from above) */
8497 void AmoebeAbleger(int ax, int ay)
8500 int element = Feld[ax][ay];
8501 int graphic = el2img(element);
8502 int newax = ax, neway = ay;
8503 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8504 static int xy[4][2] =
8512 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8514 Feld[ax][ay] = EL_AMOEBA_DEAD;
8515 TEST_DrawLevelField(ax, ay);
8519 if (IS_ANIMATED(graphic))
8520 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8522 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8523 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8525 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8528 if (MovDelay[ax][ay])
8532 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8535 int x = ax + xy[start][0];
8536 int y = ay + xy[start][1];
8538 if (!IN_LEV_FIELD(x, y))
8541 if (IS_FREE(x, y) ||
8542 CAN_GROW_INTO(Feld[x][y]) ||
8543 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8544 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8550 if (newax == ax && neway == ay)
8553 else /* normal or "filled" (BD style) amoeba */
8556 boolean waiting_for_player = FALSE;
8558 for (i = 0; i < NUM_DIRECTIONS; i++)
8560 int j = (start + i) % 4;
8561 int x = ax + xy[j][0];
8562 int y = ay + xy[j][1];
8564 if (!IN_LEV_FIELD(x, y))
8567 if (IS_FREE(x, y) ||
8568 CAN_GROW_INTO(Feld[x][y]) ||
8569 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8570 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8576 else if (IS_PLAYER(x, y))
8577 waiting_for_player = TRUE;
8580 if (newax == ax && neway == ay) /* amoeba cannot grow */
8582 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8584 Feld[ax][ay] = EL_AMOEBA_DEAD;
8585 TEST_DrawLevelField(ax, ay);
8586 AmoebaCnt[AmoebaNr[ax][ay]]--;
8588 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8590 if (element == EL_AMOEBA_FULL)
8591 AmoebeUmwandeln(ax, ay);
8592 else if (element == EL_BD_AMOEBA)
8593 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8598 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8600 /* amoeba gets larger by growing in some direction */
8602 int new_group_nr = AmoebaNr[ax][ay];
8605 if (new_group_nr == 0)
8607 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8608 printf("AmoebeAbleger(): This should never happen!\n");
8613 AmoebaNr[newax][neway] = new_group_nr;
8614 AmoebaCnt[new_group_nr]++;
8615 AmoebaCnt2[new_group_nr]++;
8617 /* if amoeba touches other amoeba(s) after growing, unify them */
8618 AmoebenVereinigen(newax, neway);
8620 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8622 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8628 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8629 (neway == lev_fieldy - 1 && newax != ax))
8631 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8632 Store[newax][neway] = element;
8634 else if (neway == ay || element == EL_EMC_DRIPPER)
8636 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8638 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8642 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8643 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8644 Store[ax][ay] = EL_AMOEBA_DROP;
8645 ContinueMoving(ax, ay);
8649 TEST_DrawLevelField(newax, neway);
8652 void Life(int ax, int ay)
8656 int element = Feld[ax][ay];
8657 int graphic = el2img(element);
8658 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8660 boolean changed = FALSE;
8662 if (IS_ANIMATED(graphic))
8663 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8668 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8669 MovDelay[ax][ay] = life_time;
8671 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8674 if (MovDelay[ax][ay])
8678 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8680 int xx = ax+x1, yy = ay+y1;
8683 if (!IN_LEV_FIELD(xx, yy))
8686 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8688 int x = xx+x2, y = yy+y2;
8690 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8693 if (((Feld[x][y] == element ||
8694 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8696 (IS_FREE(x, y) && Stop[x][y]))
8700 if (xx == ax && yy == ay) /* field in the middle */
8702 if (nachbarn < life_parameter[0] ||
8703 nachbarn > life_parameter[1])
8705 Feld[xx][yy] = EL_EMPTY;
8707 TEST_DrawLevelField(xx, yy);
8708 Stop[xx][yy] = TRUE;
8712 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8713 { /* free border field */
8714 if (nachbarn >= life_parameter[2] &&
8715 nachbarn <= life_parameter[3])
8717 Feld[xx][yy] = element;
8718 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8720 TEST_DrawLevelField(xx, yy);
8721 Stop[xx][yy] = TRUE;
8728 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8729 SND_GAME_OF_LIFE_GROWING);
8732 static void InitRobotWheel(int x, int y)
8734 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8737 static void RunRobotWheel(int x, int y)
8739 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8742 static void StopRobotWheel(int x, int y)
8744 if (ZX == x && ZY == y)
8748 game.robot_wheel_active = FALSE;
8752 static void InitTimegateWheel(int x, int y)
8754 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8757 static void RunTimegateWheel(int x, int y)
8759 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8762 static void InitMagicBallDelay(int x, int y)
8764 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8767 static void ActivateMagicBall(int bx, int by)
8771 if (level.ball_random)
8773 int pos_border = RND(8); /* select one of the eight border elements */
8774 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8775 int xx = pos_content % 3;
8776 int yy = pos_content / 3;
8781 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8782 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8786 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8788 int xx = x - bx + 1;
8789 int yy = y - by + 1;
8791 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8792 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8796 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8799 void CheckExit(int x, int y)
8801 if (local_player->gems_still_needed > 0 ||
8802 local_player->sokobanfields_still_needed > 0 ||
8803 local_player->lights_still_needed > 0)
8805 int element = Feld[x][y];
8806 int graphic = el2img(element);
8808 if (IS_ANIMATED(graphic))
8809 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8814 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8817 Feld[x][y] = EL_EXIT_OPENING;
8819 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8822 void CheckExitEM(int x, int y)
8824 if (local_player->gems_still_needed > 0 ||
8825 local_player->sokobanfields_still_needed > 0 ||
8826 local_player->lights_still_needed > 0)
8828 int element = Feld[x][y];
8829 int graphic = el2img(element);
8831 if (IS_ANIMATED(graphic))
8832 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8837 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8840 Feld[x][y] = EL_EM_EXIT_OPENING;
8842 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8845 void CheckExitSteel(int x, int y)
8847 if (local_player->gems_still_needed > 0 ||
8848 local_player->sokobanfields_still_needed > 0 ||
8849 local_player->lights_still_needed > 0)
8851 int element = Feld[x][y];
8852 int graphic = el2img(element);
8854 if (IS_ANIMATED(graphic))
8855 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8860 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8863 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8865 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8868 void CheckExitSteelEM(int x, int y)
8870 if (local_player->gems_still_needed > 0 ||
8871 local_player->sokobanfields_still_needed > 0 ||
8872 local_player->lights_still_needed > 0)
8874 int element = Feld[x][y];
8875 int graphic = el2img(element);
8877 if (IS_ANIMATED(graphic))
8878 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8883 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8886 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8888 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8891 void CheckExitSP(int x, int y)
8893 if (local_player->gems_still_needed > 0)
8895 int element = Feld[x][y];
8896 int graphic = el2img(element);
8898 if (IS_ANIMATED(graphic))
8899 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8904 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8907 Feld[x][y] = EL_SP_EXIT_OPENING;
8909 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8912 static void CloseAllOpenTimegates()
8916 SCAN_PLAYFIELD(x, y)
8918 int element = Feld[x][y];
8920 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8922 Feld[x][y] = EL_TIMEGATE_CLOSING;
8924 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8929 void DrawTwinkleOnField(int x, int y)
8931 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8934 if (Feld[x][y] == EL_BD_DIAMOND)
8937 if (MovDelay[x][y] == 0) /* next animation frame */
8938 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8940 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8944 DrawLevelElementAnimation(x, y, Feld[x][y]);
8946 if (MovDelay[x][y] != 0)
8948 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8949 10 - MovDelay[x][y]);
8951 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8956 void MauerWaechst(int x, int y)
8960 if (!MovDelay[x][y]) /* next animation frame */
8961 MovDelay[x][y] = 3 * delay;
8963 if (MovDelay[x][y]) /* wait some time before next frame */
8967 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8969 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8970 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8972 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8975 if (!MovDelay[x][y])
8977 if (MovDir[x][y] == MV_LEFT)
8979 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8980 TEST_DrawLevelField(x - 1, y);
8982 else if (MovDir[x][y] == MV_RIGHT)
8984 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8985 TEST_DrawLevelField(x + 1, y);
8987 else if (MovDir[x][y] == MV_UP)
8989 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8990 TEST_DrawLevelField(x, y - 1);
8994 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8995 TEST_DrawLevelField(x, y + 1);
8998 Feld[x][y] = Store[x][y];
9000 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9001 TEST_DrawLevelField(x, y);
9006 void MauerAbleger(int ax, int ay)
9008 int element = Feld[ax][ay];
9009 int graphic = el2img(element);
9010 boolean oben_frei = FALSE, unten_frei = FALSE;
9011 boolean links_frei = FALSE, rechts_frei = FALSE;
9012 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9013 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9014 boolean new_wall = FALSE;
9016 if (IS_ANIMATED(graphic))
9017 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9019 if (!MovDelay[ax][ay]) /* start building new wall */
9020 MovDelay[ax][ay] = 6;
9022 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9025 if (MovDelay[ax][ay])
9029 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9031 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9033 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9035 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9038 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9039 element == EL_EXPANDABLE_WALL_ANY)
9043 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9044 Store[ax][ay-1] = element;
9045 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9046 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9047 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9048 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9053 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9054 Store[ax][ay+1] = element;
9055 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9056 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9057 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9058 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9063 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9064 element == EL_EXPANDABLE_WALL_ANY ||
9065 element == EL_EXPANDABLE_WALL ||
9066 element == EL_BD_EXPANDABLE_WALL)
9070 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9071 Store[ax-1][ay] = element;
9072 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9073 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9074 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9075 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9081 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9082 Store[ax+1][ay] = element;
9083 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9084 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9085 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9086 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9091 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9092 TEST_DrawLevelField(ax, ay);
9094 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9096 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9097 unten_massiv = TRUE;
9098 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9099 links_massiv = TRUE;
9100 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9101 rechts_massiv = TRUE;
9103 if (((oben_massiv && unten_massiv) ||
9104 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9105 element == EL_EXPANDABLE_WALL) &&
9106 ((links_massiv && rechts_massiv) ||
9107 element == EL_EXPANDABLE_WALL_VERTICAL))
9108 Feld[ax][ay] = EL_WALL;
9111 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9114 void MauerAblegerStahl(int ax, int ay)
9116 int element = Feld[ax][ay];
9117 int graphic = el2img(element);
9118 boolean oben_frei = FALSE, unten_frei = FALSE;
9119 boolean links_frei = FALSE, rechts_frei = FALSE;
9120 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9121 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9122 boolean new_wall = FALSE;
9124 if (IS_ANIMATED(graphic))
9125 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9127 if (!MovDelay[ax][ay]) /* start building new wall */
9128 MovDelay[ax][ay] = 6;
9130 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9133 if (MovDelay[ax][ay])
9137 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9139 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9141 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9143 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9146 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9147 element == EL_EXPANDABLE_STEELWALL_ANY)
9151 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9152 Store[ax][ay-1] = element;
9153 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9154 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9155 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9156 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9161 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9162 Store[ax][ay+1] = element;
9163 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9164 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9165 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9166 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9171 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9172 element == EL_EXPANDABLE_STEELWALL_ANY)
9176 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9177 Store[ax-1][ay] = element;
9178 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9179 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9180 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9181 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9187 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9188 Store[ax+1][ay] = element;
9189 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9190 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9191 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9192 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9197 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9199 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9200 unten_massiv = TRUE;
9201 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9202 links_massiv = TRUE;
9203 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9204 rechts_massiv = TRUE;
9206 if (((oben_massiv && unten_massiv) ||
9207 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9208 ((links_massiv && rechts_massiv) ||
9209 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9210 Feld[ax][ay] = EL_STEELWALL;
9213 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9216 void CheckForDragon(int x, int y)
9219 boolean dragon_found = FALSE;
9220 static int xy[4][2] =
9228 for (i = 0; i < NUM_DIRECTIONS; i++)
9230 for (j = 0; j < 4; j++)
9232 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9234 if (IN_LEV_FIELD(xx, yy) &&
9235 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9237 if (Feld[xx][yy] == EL_DRAGON)
9238 dragon_found = TRUE;
9247 for (i = 0; i < NUM_DIRECTIONS; i++)
9249 for (j = 0; j < 3; j++)
9251 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9253 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9255 Feld[xx][yy] = EL_EMPTY;
9256 TEST_DrawLevelField(xx, yy);
9265 static void InitBuggyBase(int x, int y)
9267 int element = Feld[x][y];
9268 int activating_delay = FRAMES_PER_SECOND / 4;
9271 (element == EL_SP_BUGGY_BASE ?
9272 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9273 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9275 element == EL_SP_BUGGY_BASE_ACTIVE ?
9276 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9279 static void WarnBuggyBase(int x, int y)
9282 static int xy[4][2] =
9290 for (i = 0; i < NUM_DIRECTIONS; i++)
9292 int xx = x + xy[i][0];
9293 int yy = y + xy[i][1];
9295 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9297 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9304 static void InitTrap(int x, int y)
9306 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9309 static void ActivateTrap(int x, int y)
9311 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9314 static void ChangeActiveTrap(int x, int y)
9316 int graphic = IMG_TRAP_ACTIVE;
9318 /* if new animation frame was drawn, correct crumbled sand border */
9319 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9320 TEST_DrawLevelFieldCrumbled(x, y);
9323 static int getSpecialActionElement(int element, int number, int base_element)
9325 return (element != EL_EMPTY ? element :
9326 number != -1 ? base_element + number - 1 :
9330 static int getModifiedActionNumber(int value_old, int operator, int operand,
9331 int value_min, int value_max)
9333 int value_new = (operator == CA_MODE_SET ? operand :
9334 operator == CA_MODE_ADD ? value_old + operand :
9335 operator == CA_MODE_SUBTRACT ? value_old - operand :
9336 operator == CA_MODE_MULTIPLY ? value_old * operand :
9337 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9338 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9341 return (value_new < value_min ? value_min :
9342 value_new > value_max ? value_max :
9346 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9348 struct ElementInfo *ei = &element_info[element];
9349 struct ElementChangeInfo *change = &ei->change_page[page];
9350 int target_element = change->target_element;
9351 int action_type = change->action_type;
9352 int action_mode = change->action_mode;
9353 int action_arg = change->action_arg;
9354 int action_element = change->action_element;
9357 if (!change->has_action)
9360 /* ---------- determine action paramater values -------------------------- */
9362 int level_time_value =
9363 (level.time > 0 ? TimeLeft :
9366 int action_arg_element_raw =
9367 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9368 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9369 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9370 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9371 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9372 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9373 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9375 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9377 int action_arg_direction =
9378 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9379 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9380 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9381 change->actual_trigger_side :
9382 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9383 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9386 int action_arg_number_min =
9387 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9390 int action_arg_number_max =
9391 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9392 action_type == CA_SET_LEVEL_GEMS ? 999 :
9393 action_type == CA_SET_LEVEL_TIME ? 9999 :
9394 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9395 action_type == CA_SET_CE_VALUE ? 9999 :
9396 action_type == CA_SET_CE_SCORE ? 9999 :
9399 int action_arg_number_reset =
9400 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9401 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9402 action_type == CA_SET_LEVEL_TIME ? level.time :
9403 action_type == CA_SET_LEVEL_SCORE ? 0 :
9404 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9405 action_type == CA_SET_CE_SCORE ? 0 :
9408 int action_arg_number =
9409 (action_arg <= CA_ARG_MAX ? action_arg :
9410 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9411 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9412 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9413 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9414 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9415 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9416 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9417 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9418 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9419 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9420 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9421 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9422 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9423 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9424 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9425 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9426 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9427 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9428 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9429 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9430 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9433 int action_arg_number_old =
9434 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9435 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9436 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9437 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9438 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9441 int action_arg_number_new =
9442 getModifiedActionNumber(action_arg_number_old,
9443 action_mode, action_arg_number,
9444 action_arg_number_min, action_arg_number_max);
9446 int trigger_player_bits =
9447 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9448 change->actual_trigger_player_bits : change->trigger_player);
9450 int action_arg_player_bits =
9451 (action_arg >= CA_ARG_PLAYER_1 &&
9452 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9453 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9454 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9457 /* ---------- execute action -------------------------------------------- */
9459 switch (action_type)
9466 /* ---------- level actions ------------------------------------------- */
9468 case CA_RESTART_LEVEL:
9470 game.restart_level = TRUE;
9475 case CA_SHOW_ENVELOPE:
9477 int element = getSpecialActionElement(action_arg_element,
9478 action_arg_number, EL_ENVELOPE_1);
9480 if (IS_ENVELOPE(element))
9481 local_player->show_envelope = element;
9486 case CA_SET_LEVEL_TIME:
9488 if (level.time > 0) /* only modify limited time value */
9490 TimeLeft = action_arg_number_new;
9492 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9494 DisplayGameControlValues();
9496 if (!TimeLeft && setup.time_limit)
9497 for (i = 0; i < MAX_PLAYERS; i++)
9498 KillPlayer(&stored_player[i]);
9504 case CA_SET_LEVEL_SCORE:
9506 local_player->score = action_arg_number_new;
9508 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9510 DisplayGameControlValues();
9515 case CA_SET_LEVEL_GEMS:
9517 local_player->gems_still_needed = action_arg_number_new;
9519 game.snapshot.collected_item = TRUE;
9521 game_panel_controls[GAME_PANEL_GEMS].value =
9522 local_player->gems_still_needed;
9524 DisplayGameControlValues();
9529 case CA_SET_LEVEL_WIND:
9531 game.wind_direction = action_arg_direction;
9536 case CA_SET_LEVEL_RANDOM_SEED:
9538 /* ensure that setting a new random seed while playing is predictable */
9539 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9544 /* ---------- player actions ------------------------------------------ */
9546 case CA_MOVE_PLAYER:
9548 /* automatically move to the next field in specified direction */
9549 for (i = 0; i < MAX_PLAYERS; i++)
9550 if (trigger_player_bits & (1 << i))
9551 stored_player[i].programmed_action = action_arg_direction;
9556 case CA_EXIT_PLAYER:
9558 for (i = 0; i < MAX_PLAYERS; i++)
9559 if (action_arg_player_bits & (1 << i))
9560 PlayerWins(&stored_player[i]);
9565 case CA_KILL_PLAYER:
9567 for (i = 0; i < MAX_PLAYERS; i++)
9568 if (action_arg_player_bits & (1 << i))
9569 KillPlayer(&stored_player[i]);
9574 case CA_SET_PLAYER_KEYS:
9576 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9577 int element = getSpecialActionElement(action_arg_element,
9578 action_arg_number, EL_KEY_1);
9580 if (IS_KEY(element))
9582 for (i = 0; i < MAX_PLAYERS; i++)
9584 if (trigger_player_bits & (1 << i))
9586 stored_player[i].key[KEY_NR(element)] = key_state;
9588 DrawGameDoorValues();
9596 case CA_SET_PLAYER_SPEED:
9598 for (i = 0; i < MAX_PLAYERS; i++)
9600 if (trigger_player_bits & (1 << i))
9602 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9604 if (action_arg == CA_ARG_SPEED_FASTER &&
9605 stored_player[i].cannot_move)
9607 action_arg_number = STEPSIZE_VERY_SLOW;
9609 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9610 action_arg == CA_ARG_SPEED_FASTER)
9612 action_arg_number = 2;
9613 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9616 else if (action_arg == CA_ARG_NUMBER_RESET)
9618 action_arg_number = level.initial_player_stepsize[i];
9622 getModifiedActionNumber(move_stepsize,
9625 action_arg_number_min,
9626 action_arg_number_max);
9628 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9635 case CA_SET_PLAYER_SHIELD:
9637 for (i = 0; i < MAX_PLAYERS; i++)
9639 if (trigger_player_bits & (1 << i))
9641 if (action_arg == CA_ARG_SHIELD_OFF)
9643 stored_player[i].shield_normal_time_left = 0;
9644 stored_player[i].shield_deadly_time_left = 0;
9646 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9648 stored_player[i].shield_normal_time_left = 999999;
9650 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9652 stored_player[i].shield_normal_time_left = 999999;
9653 stored_player[i].shield_deadly_time_left = 999999;
9661 case CA_SET_PLAYER_GRAVITY:
9663 for (i = 0; i < MAX_PLAYERS; i++)
9665 if (trigger_player_bits & (1 << i))
9667 stored_player[i].gravity =
9668 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9669 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9670 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9671 stored_player[i].gravity);
9678 case CA_SET_PLAYER_ARTWORK:
9680 for (i = 0; i < MAX_PLAYERS; i++)
9682 if (trigger_player_bits & (1 << i))
9684 int artwork_element = action_arg_element;
9686 if (action_arg == CA_ARG_ELEMENT_RESET)
9688 (level.use_artwork_element[i] ? level.artwork_element[i] :
9689 stored_player[i].element_nr);
9691 if (stored_player[i].artwork_element != artwork_element)
9692 stored_player[i].Frame = 0;
9694 stored_player[i].artwork_element = artwork_element;
9696 SetPlayerWaiting(&stored_player[i], FALSE);
9698 /* set number of special actions for bored and sleeping animation */
9699 stored_player[i].num_special_action_bored =
9700 get_num_special_action(artwork_element,
9701 ACTION_BORING_1, ACTION_BORING_LAST);
9702 stored_player[i].num_special_action_sleeping =
9703 get_num_special_action(artwork_element,
9704 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9711 case CA_SET_PLAYER_INVENTORY:
9713 for (i = 0; i < MAX_PLAYERS; i++)
9715 struct PlayerInfo *player = &stored_player[i];
9718 if (trigger_player_bits & (1 << i))
9720 int inventory_element = action_arg_element;
9722 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9723 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9724 action_arg == CA_ARG_ELEMENT_ACTION)
9726 int element = inventory_element;
9727 int collect_count = element_info[element].collect_count_initial;
9729 if (!IS_CUSTOM_ELEMENT(element))
9732 if (collect_count == 0)
9733 player->inventory_infinite_element = element;
9735 for (k = 0; k < collect_count; k++)
9736 if (player->inventory_size < MAX_INVENTORY_SIZE)
9737 player->inventory_element[player->inventory_size++] =
9740 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9741 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9742 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9744 if (player->inventory_infinite_element != EL_UNDEFINED &&
9745 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9746 action_arg_element_raw))
9747 player->inventory_infinite_element = EL_UNDEFINED;
9749 for (k = 0, j = 0; j < player->inventory_size; j++)
9751 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9752 action_arg_element_raw))
9753 player->inventory_element[k++] = player->inventory_element[j];
9756 player->inventory_size = k;
9758 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9760 if (player->inventory_size > 0)
9762 for (j = 0; j < player->inventory_size - 1; j++)
9763 player->inventory_element[j] = player->inventory_element[j + 1];
9765 player->inventory_size--;
9768 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9770 if (player->inventory_size > 0)
9771 player->inventory_size--;
9773 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9775 player->inventory_infinite_element = EL_UNDEFINED;
9776 player->inventory_size = 0;
9778 else if (action_arg == CA_ARG_INVENTORY_RESET)
9780 player->inventory_infinite_element = EL_UNDEFINED;
9781 player->inventory_size = 0;
9783 if (level.use_initial_inventory[i])
9785 for (j = 0; j < level.initial_inventory_size[i]; j++)
9787 int element = level.initial_inventory_content[i][j];
9788 int collect_count = element_info[element].collect_count_initial;
9790 if (!IS_CUSTOM_ELEMENT(element))
9793 if (collect_count == 0)
9794 player->inventory_infinite_element = element;
9796 for (k = 0; k < collect_count; k++)
9797 if (player->inventory_size < MAX_INVENTORY_SIZE)
9798 player->inventory_element[player->inventory_size++] =
9809 /* ---------- CE actions ---------------------------------------------- */
9811 case CA_SET_CE_VALUE:
9813 int last_ce_value = CustomValue[x][y];
9815 CustomValue[x][y] = action_arg_number_new;
9817 if (CustomValue[x][y] != last_ce_value)
9819 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9820 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9822 if (CustomValue[x][y] == 0)
9824 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9825 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9832 case CA_SET_CE_SCORE:
9834 int last_ce_score = ei->collect_score;
9836 ei->collect_score = action_arg_number_new;
9838 if (ei->collect_score != last_ce_score)
9840 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9841 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9843 if (ei->collect_score == 0)
9847 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9848 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9851 This is a very special case that seems to be a mixture between
9852 CheckElementChange() and CheckTriggeredElementChange(): while
9853 the first one only affects single elements that are triggered
9854 directly, the second one affects multiple elements in the playfield
9855 that are triggered indirectly by another element. This is a third
9856 case: Changing the CE score always affects multiple identical CEs,
9857 so every affected CE must be checked, not only the single CE for
9858 which the CE score was changed in the first place (as every instance
9859 of that CE shares the same CE score, and therefore also can change)!
9861 SCAN_PLAYFIELD(xx, yy)
9863 if (Feld[xx][yy] == element)
9864 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9865 CE_SCORE_GETS_ZERO);
9873 case CA_SET_CE_ARTWORK:
9875 int artwork_element = action_arg_element;
9876 boolean reset_frame = FALSE;
9879 if (action_arg == CA_ARG_ELEMENT_RESET)
9880 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9883 if (ei->gfx_element != artwork_element)
9886 ei->gfx_element = artwork_element;
9888 SCAN_PLAYFIELD(xx, yy)
9890 if (Feld[xx][yy] == element)
9894 ResetGfxAnimation(xx, yy);
9895 ResetRandomAnimationValue(xx, yy);
9898 TEST_DrawLevelField(xx, yy);
9905 /* ---------- engine actions ------------------------------------------ */
9907 case CA_SET_ENGINE_SCAN_MODE:
9909 InitPlayfieldScanMode(action_arg);
9919 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9921 int old_element = Feld[x][y];
9922 int new_element = GetElementFromGroupElement(element);
9923 int previous_move_direction = MovDir[x][y];
9924 int last_ce_value = CustomValue[x][y];
9925 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9926 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9927 boolean add_player_onto_element = (new_element_is_player &&
9928 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9929 IS_WALKABLE(old_element));
9931 if (!add_player_onto_element)
9933 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9934 RemoveMovingField(x, y);
9938 Feld[x][y] = new_element;
9940 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9941 MovDir[x][y] = previous_move_direction;
9943 if (element_info[new_element].use_last_ce_value)
9944 CustomValue[x][y] = last_ce_value;
9946 InitField_WithBug1(x, y, FALSE);
9948 new_element = Feld[x][y]; /* element may have changed */
9950 ResetGfxAnimation(x, y);
9951 ResetRandomAnimationValue(x, y);
9953 TEST_DrawLevelField(x, y);
9955 if (GFX_CRUMBLED(new_element))
9956 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9959 /* check if element under the player changes from accessible to unaccessible
9960 (needed for special case of dropping element which then changes) */
9961 /* (must be checked after creating new element for walkable group elements) */
9962 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9963 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9970 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9971 if (new_element_is_player)
9972 RelocatePlayer(x, y, new_element);
9975 ChangeCount[x][y]++; /* count number of changes in the same frame */
9977 TestIfBadThingTouchesPlayer(x, y);
9978 TestIfPlayerTouchesCustomElement(x, y);
9979 TestIfElementTouchesCustomElement(x, y);
9982 static void CreateField(int x, int y, int element)
9984 CreateFieldExt(x, y, element, FALSE);
9987 static void CreateElementFromChange(int x, int y, int element)
9989 element = GET_VALID_RUNTIME_ELEMENT(element);
9991 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9993 int old_element = Feld[x][y];
9995 /* prevent changed element from moving in same engine frame
9996 unless both old and new element can either fall or move */
9997 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9998 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10002 CreateFieldExt(x, y, element, TRUE);
10005 static boolean ChangeElement(int x, int y, int element, int page)
10007 struct ElementInfo *ei = &element_info[element];
10008 struct ElementChangeInfo *change = &ei->change_page[page];
10009 int ce_value = CustomValue[x][y];
10010 int ce_score = ei->collect_score;
10011 int target_element;
10012 int old_element = Feld[x][y];
10014 /* always use default change event to prevent running into a loop */
10015 if (ChangeEvent[x][y] == -1)
10016 ChangeEvent[x][y] = CE_DELAY;
10018 if (ChangeEvent[x][y] == CE_DELAY)
10020 /* reset actual trigger element, trigger player and action element */
10021 change->actual_trigger_element = EL_EMPTY;
10022 change->actual_trigger_player = EL_EMPTY;
10023 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10024 change->actual_trigger_side = CH_SIDE_NONE;
10025 change->actual_trigger_ce_value = 0;
10026 change->actual_trigger_ce_score = 0;
10029 /* do not change elements more than a specified maximum number of changes */
10030 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10033 ChangeCount[x][y]++; /* count number of changes in the same frame */
10035 if (change->explode)
10042 if (change->use_target_content)
10044 boolean complete_replace = TRUE;
10045 boolean can_replace[3][3];
10048 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10051 boolean is_walkable;
10052 boolean is_diggable;
10053 boolean is_collectible;
10054 boolean is_removable;
10055 boolean is_destructible;
10056 int ex = x + xx - 1;
10057 int ey = y + yy - 1;
10058 int content_element = change->target_content.e[xx][yy];
10061 can_replace[xx][yy] = TRUE;
10063 if (ex == x && ey == y) /* do not check changing element itself */
10066 if (content_element == EL_EMPTY_SPACE)
10068 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10073 if (!IN_LEV_FIELD(ex, ey))
10075 can_replace[xx][yy] = FALSE;
10076 complete_replace = FALSE;
10083 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10084 e = MovingOrBlocked2Element(ex, ey);
10086 is_empty = (IS_FREE(ex, ey) ||
10087 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10089 is_walkable = (is_empty || IS_WALKABLE(e));
10090 is_diggable = (is_empty || IS_DIGGABLE(e));
10091 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10092 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10093 is_removable = (is_diggable || is_collectible);
10095 can_replace[xx][yy] =
10096 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10097 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10098 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10099 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10100 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10101 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10102 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10104 if (!can_replace[xx][yy])
10105 complete_replace = FALSE;
10108 if (!change->only_if_complete || complete_replace)
10110 boolean something_has_changed = FALSE;
10112 if (change->only_if_complete && change->use_random_replace &&
10113 RND(100) < change->random_percentage)
10116 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10118 int ex = x + xx - 1;
10119 int ey = y + yy - 1;
10120 int content_element;
10122 if (can_replace[xx][yy] && (!change->use_random_replace ||
10123 RND(100) < change->random_percentage))
10125 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10126 RemoveMovingField(ex, ey);
10128 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10130 content_element = change->target_content.e[xx][yy];
10131 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10132 ce_value, ce_score);
10134 CreateElementFromChange(ex, ey, target_element);
10136 something_has_changed = TRUE;
10138 /* for symmetry reasons, freeze newly created border elements */
10139 if (ex != x || ey != y)
10140 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10144 if (something_has_changed)
10146 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10147 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10153 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10154 ce_value, ce_score);
10156 if (element == EL_DIAGONAL_GROWING ||
10157 element == EL_DIAGONAL_SHRINKING)
10159 target_element = Store[x][y];
10161 Store[x][y] = EL_EMPTY;
10164 CreateElementFromChange(x, y, target_element);
10166 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10167 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10170 /* this uses direct change before indirect change */
10171 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10176 static void HandleElementChange(int x, int y, int page)
10178 int element = MovingOrBlocked2Element(x, y);
10179 struct ElementInfo *ei = &element_info[element];
10180 struct ElementChangeInfo *change = &ei->change_page[page];
10181 boolean handle_action_before_change = FALSE;
10184 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10185 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10188 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10189 x, y, element, element_info[element].token_name);
10190 printf("HandleElementChange(): This should never happen!\n");
10195 /* this can happen with classic bombs on walkable, changing elements */
10196 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10201 if (ChangeDelay[x][y] == 0) /* initialize element change */
10203 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10205 if (change->can_change)
10207 /* !!! not clear why graphic animation should be reset at all here !!! */
10208 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10209 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10212 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10214 When using an animation frame delay of 1 (this only happens with
10215 "sp_zonk.moving.left/right" in the classic graphics), the default
10216 (non-moving) animation shows wrong animation frames (while the
10217 moving animation, like "sp_zonk.moving.left/right", is correct,
10218 so this graphical bug never shows up with the classic graphics).
10219 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10220 be drawn instead of the correct frames 0,1,2,3. This is caused by
10221 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10222 an element change: First when the change delay ("ChangeDelay[][]")
10223 counter has reached zero after decrementing, then a second time in
10224 the next frame (after "GfxFrame[][]" was already incremented) when
10225 "ChangeDelay[][]" is reset to the initial delay value again.
10227 This causes frame 0 to be drawn twice, while the last frame won't
10228 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10230 As some animations may already be cleverly designed around this bug
10231 (at least the "Snake Bite" snake tail animation does this), it cannot
10232 simply be fixed here without breaking such existing animations.
10233 Unfortunately, it cannot easily be detected if a graphics set was
10234 designed "before" or "after" the bug was fixed. As a workaround,
10235 a new graphics set option "game.graphics_engine_version" was added
10236 to be able to specify the game's major release version for which the
10237 graphics set was designed, which can then be used to decide if the
10238 bugfix should be used (version 4 and above) or not (version 3 or
10239 below, or if no version was specified at all, as with old sets).
10241 (The wrong/fixed animation frames can be tested with the test level set
10242 "test_gfxframe" and level "000", which contains a specially prepared
10243 custom element at level position (x/y) == (11/9) which uses the zonk
10244 animation mentioned above. Using "game.graphics_engine_version: 4"
10245 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10246 This can also be seen from the debug output for this test element.)
10249 /* when a custom element is about to change (for example by change delay),
10250 do not reset graphic animation when the custom element is moving */
10251 if (game.graphics_engine_version < 4 &&
10254 ResetGfxAnimation(x, y);
10255 ResetRandomAnimationValue(x, y);
10258 if (change->pre_change_function)
10259 change->pre_change_function(x, y);
10263 ChangeDelay[x][y]--;
10265 if (ChangeDelay[x][y] != 0) /* continue element change */
10267 if (change->can_change)
10269 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10271 if (IS_ANIMATED(graphic))
10272 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10274 if (change->change_function)
10275 change->change_function(x, y);
10278 else /* finish element change */
10280 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10282 page = ChangePage[x][y];
10283 ChangePage[x][y] = -1;
10285 change = &ei->change_page[page];
10288 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10290 ChangeDelay[x][y] = 1; /* try change after next move step */
10291 ChangePage[x][y] = page; /* remember page to use for change */
10296 /* special case: set new level random seed before changing element */
10297 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10298 handle_action_before_change = TRUE;
10300 if (change->has_action && handle_action_before_change)
10301 ExecuteCustomElementAction(x, y, element, page);
10303 if (change->can_change)
10305 if (ChangeElement(x, y, element, page))
10307 if (change->post_change_function)
10308 change->post_change_function(x, y);
10312 if (change->has_action && !handle_action_before_change)
10313 ExecuteCustomElementAction(x, y, element, page);
10317 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10318 int trigger_element,
10320 int trigger_player,
10324 boolean change_done_any = FALSE;
10325 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10328 if (!(trigger_events[trigger_element][trigger_event]))
10331 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10333 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10335 int element = EL_CUSTOM_START + i;
10336 boolean change_done = FALSE;
10339 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10340 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10343 for (p = 0; p < element_info[element].num_change_pages; p++)
10345 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10347 if (change->can_change_or_has_action &&
10348 change->has_event[trigger_event] &&
10349 change->trigger_side & trigger_side &&
10350 change->trigger_player & trigger_player &&
10351 change->trigger_page & trigger_page_bits &&
10352 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10354 change->actual_trigger_element = trigger_element;
10355 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10356 change->actual_trigger_player_bits = trigger_player;
10357 change->actual_trigger_side = trigger_side;
10358 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10359 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10361 if ((change->can_change && !change_done) || change->has_action)
10365 SCAN_PLAYFIELD(x, y)
10367 if (Feld[x][y] == element)
10369 if (change->can_change && !change_done)
10371 /* if element already changed in this frame, not only prevent
10372 another element change (checked in ChangeElement()), but
10373 also prevent additional element actions for this element */
10375 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10376 !level.use_action_after_change_bug)
10379 ChangeDelay[x][y] = 1;
10380 ChangeEvent[x][y] = trigger_event;
10382 HandleElementChange(x, y, p);
10384 else if (change->has_action)
10386 /* if element already changed in this frame, not only prevent
10387 another element change (checked in ChangeElement()), but
10388 also prevent additional element actions for this element */
10390 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10391 !level.use_action_after_change_bug)
10394 ExecuteCustomElementAction(x, y, element, p);
10395 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10400 if (change->can_change)
10402 change_done = TRUE;
10403 change_done_any = TRUE;
10410 RECURSION_LOOP_DETECTION_END();
10412 return change_done_any;
10415 static boolean CheckElementChangeExt(int x, int y,
10417 int trigger_element,
10419 int trigger_player,
10422 boolean change_done = FALSE;
10425 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10426 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10429 if (Feld[x][y] == EL_BLOCKED)
10431 Blocked2Moving(x, y, &x, &y);
10432 element = Feld[x][y];
10435 /* check if element has already changed or is about to change after moving */
10436 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10437 Feld[x][y] != element) ||
10439 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10440 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10441 ChangePage[x][y] != -1)))
10444 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10446 for (p = 0; p < element_info[element].num_change_pages; p++)
10448 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10450 /* check trigger element for all events where the element that is checked
10451 for changing interacts with a directly adjacent element -- this is
10452 different to element changes that affect other elements to change on the
10453 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10454 boolean check_trigger_element =
10455 (trigger_event == CE_TOUCHING_X ||
10456 trigger_event == CE_HITTING_X ||
10457 trigger_event == CE_HIT_BY_X ||
10458 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10460 if (change->can_change_or_has_action &&
10461 change->has_event[trigger_event] &&
10462 change->trigger_side & trigger_side &&
10463 change->trigger_player & trigger_player &&
10464 (!check_trigger_element ||
10465 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10467 change->actual_trigger_element = trigger_element;
10468 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10469 change->actual_trigger_player_bits = trigger_player;
10470 change->actual_trigger_side = trigger_side;
10471 change->actual_trigger_ce_value = CustomValue[x][y];
10472 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10474 /* special case: trigger element not at (x,y) position for some events */
10475 if (check_trigger_element)
10487 { 0, 0 }, { 0, 0 }, { 0, 0 },
10491 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10492 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10494 change->actual_trigger_ce_value = CustomValue[xx][yy];
10495 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10498 if (change->can_change && !change_done)
10500 ChangeDelay[x][y] = 1;
10501 ChangeEvent[x][y] = trigger_event;
10503 HandleElementChange(x, y, p);
10505 change_done = TRUE;
10507 else if (change->has_action)
10509 ExecuteCustomElementAction(x, y, element, p);
10510 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10515 RECURSION_LOOP_DETECTION_END();
10517 return change_done;
10520 static void PlayPlayerSound(struct PlayerInfo *player)
10522 int jx = player->jx, jy = player->jy;
10523 int sound_element = player->artwork_element;
10524 int last_action = player->last_action_waiting;
10525 int action = player->action_waiting;
10527 if (player->is_waiting)
10529 if (action != last_action)
10530 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10532 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10536 if (action != last_action)
10537 StopSound(element_info[sound_element].sound[last_action]);
10539 if (last_action == ACTION_SLEEPING)
10540 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10544 static void PlayAllPlayersSound()
10548 for (i = 0; i < MAX_PLAYERS; i++)
10549 if (stored_player[i].active)
10550 PlayPlayerSound(&stored_player[i]);
10553 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10555 boolean last_waiting = player->is_waiting;
10556 int move_dir = player->MovDir;
10558 player->dir_waiting = move_dir;
10559 player->last_action_waiting = player->action_waiting;
10563 if (!last_waiting) /* not waiting -> waiting */
10565 player->is_waiting = TRUE;
10567 player->frame_counter_bored =
10569 game.player_boring_delay_fixed +
10570 GetSimpleRandom(game.player_boring_delay_random);
10571 player->frame_counter_sleeping =
10573 game.player_sleeping_delay_fixed +
10574 GetSimpleRandom(game.player_sleeping_delay_random);
10576 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10579 if (game.player_sleeping_delay_fixed +
10580 game.player_sleeping_delay_random > 0 &&
10581 player->anim_delay_counter == 0 &&
10582 player->post_delay_counter == 0 &&
10583 FrameCounter >= player->frame_counter_sleeping)
10584 player->is_sleeping = TRUE;
10585 else if (game.player_boring_delay_fixed +
10586 game.player_boring_delay_random > 0 &&
10587 FrameCounter >= player->frame_counter_bored)
10588 player->is_bored = TRUE;
10590 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10591 player->is_bored ? ACTION_BORING :
10594 if (player->is_sleeping && player->use_murphy)
10596 /* special case for sleeping Murphy when leaning against non-free tile */
10598 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10599 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10600 !IS_MOVING(player->jx - 1, player->jy)))
10601 move_dir = MV_LEFT;
10602 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10603 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10604 !IS_MOVING(player->jx + 1, player->jy)))
10605 move_dir = MV_RIGHT;
10607 player->is_sleeping = FALSE;
10609 player->dir_waiting = move_dir;
10612 if (player->is_sleeping)
10614 if (player->num_special_action_sleeping > 0)
10616 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10618 int last_special_action = player->special_action_sleeping;
10619 int num_special_action = player->num_special_action_sleeping;
10620 int special_action =
10621 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10622 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10623 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10624 last_special_action + 1 : ACTION_SLEEPING);
10625 int special_graphic =
10626 el_act_dir2img(player->artwork_element, special_action, move_dir);
10628 player->anim_delay_counter =
10629 graphic_info[special_graphic].anim_delay_fixed +
10630 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10631 player->post_delay_counter =
10632 graphic_info[special_graphic].post_delay_fixed +
10633 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10635 player->special_action_sleeping = special_action;
10638 if (player->anim_delay_counter > 0)
10640 player->action_waiting = player->special_action_sleeping;
10641 player->anim_delay_counter--;
10643 else if (player->post_delay_counter > 0)
10645 player->post_delay_counter--;
10649 else if (player->is_bored)
10651 if (player->num_special_action_bored > 0)
10653 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10655 int special_action =
10656 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10657 int special_graphic =
10658 el_act_dir2img(player->artwork_element, special_action, move_dir);
10660 player->anim_delay_counter =
10661 graphic_info[special_graphic].anim_delay_fixed +
10662 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10663 player->post_delay_counter =
10664 graphic_info[special_graphic].post_delay_fixed +
10665 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10667 player->special_action_bored = special_action;
10670 if (player->anim_delay_counter > 0)
10672 player->action_waiting = player->special_action_bored;
10673 player->anim_delay_counter--;
10675 else if (player->post_delay_counter > 0)
10677 player->post_delay_counter--;
10682 else if (last_waiting) /* waiting -> not waiting */
10684 player->is_waiting = FALSE;
10685 player->is_bored = FALSE;
10686 player->is_sleeping = FALSE;
10688 player->frame_counter_bored = -1;
10689 player->frame_counter_sleeping = -1;
10691 player->anim_delay_counter = 0;
10692 player->post_delay_counter = 0;
10694 player->dir_waiting = player->MovDir;
10695 player->action_waiting = ACTION_DEFAULT;
10697 player->special_action_bored = ACTION_DEFAULT;
10698 player->special_action_sleeping = ACTION_DEFAULT;
10702 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10704 if ((!player->is_moving && player->was_moving) ||
10705 (player->MovPos == 0 && player->was_moving) ||
10706 (player->is_snapping && !player->was_snapping) ||
10707 (player->is_dropping && !player->was_dropping))
10709 if (!CheckSaveEngineSnapshotToList())
10712 player->was_moving = FALSE;
10713 player->was_snapping = TRUE;
10714 player->was_dropping = TRUE;
10718 if (player->is_moving)
10719 player->was_moving = TRUE;
10721 if (!player->is_snapping)
10722 player->was_snapping = FALSE;
10724 if (!player->is_dropping)
10725 player->was_dropping = FALSE;
10729 static void CheckSingleStepMode(struct PlayerInfo *player)
10731 if (tape.single_step && tape.recording && !tape.pausing)
10733 /* as it is called "single step mode", just return to pause mode when the
10734 player stopped moving after one tile (or never starts moving at all) */
10735 if (!player->is_moving && !player->is_pushing)
10737 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10738 SnapField(player, 0, 0); /* stop snapping */
10742 CheckSaveEngineSnapshot(player);
10745 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10747 int left = player_action & JOY_LEFT;
10748 int right = player_action & JOY_RIGHT;
10749 int up = player_action & JOY_UP;
10750 int down = player_action & JOY_DOWN;
10751 int button1 = player_action & JOY_BUTTON_1;
10752 int button2 = player_action & JOY_BUTTON_2;
10753 int dx = (left ? -1 : right ? 1 : 0);
10754 int dy = (up ? -1 : down ? 1 : 0);
10756 if (!player->active || tape.pausing)
10762 SnapField(player, dx, dy);
10766 DropElement(player);
10768 MovePlayer(player, dx, dy);
10771 CheckSingleStepMode(player);
10773 SetPlayerWaiting(player, FALSE);
10775 return player_action;
10779 /* no actions for this player (no input at player's configured device) */
10781 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10782 SnapField(player, 0, 0);
10783 CheckGravityMovementWhenNotMoving(player);
10785 if (player->MovPos == 0)
10786 SetPlayerWaiting(player, TRUE);
10788 if (player->MovPos == 0) /* needed for tape.playing */
10789 player->is_moving = FALSE;
10791 player->is_dropping = FALSE;
10792 player->is_dropping_pressed = FALSE;
10793 player->drop_pressed_delay = 0;
10795 CheckSingleStepMode(player);
10801 static void CheckLevelTime()
10805 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10806 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10808 if (level.native_em_level->lev->home == 0) /* all players at home */
10810 PlayerWins(local_player);
10812 AllPlayersGone = TRUE;
10814 level.native_em_level->lev->home = -1;
10817 if (level.native_em_level->ply[0]->alive == 0 &&
10818 level.native_em_level->ply[1]->alive == 0 &&
10819 level.native_em_level->ply[2]->alive == 0 &&
10820 level.native_em_level->ply[3]->alive == 0) /* all dead */
10821 AllPlayersGone = TRUE;
10823 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10825 if (game_sp.LevelSolved &&
10826 !game_sp.GameOver) /* game won */
10828 PlayerWins(local_player);
10830 game_sp.GameOver = TRUE;
10832 AllPlayersGone = TRUE;
10835 if (game_sp.GameOver) /* game lost */
10836 AllPlayersGone = TRUE;
10839 if (TimeFrames >= FRAMES_PER_SECOND)
10844 for (i = 0; i < MAX_PLAYERS; i++)
10846 struct PlayerInfo *player = &stored_player[i];
10848 if (SHIELD_ON(player))
10850 player->shield_normal_time_left--;
10852 if (player->shield_deadly_time_left > 0)
10853 player->shield_deadly_time_left--;
10857 if (!local_player->LevelSolved && !level.use_step_counter)
10865 if (TimeLeft <= 10 && setup.time_limit)
10866 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10868 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10869 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10871 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10873 if (!TimeLeft && setup.time_limit)
10875 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10876 level.native_em_level->lev->killed_out_of_time = TRUE;
10878 for (i = 0; i < MAX_PLAYERS; i++)
10879 KillPlayer(&stored_player[i]);
10882 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10884 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10887 level.native_em_level->lev->time =
10888 (game.no_time_limit ? TimePlayed : TimeLeft);
10891 if (tape.recording || tape.playing)
10892 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10895 if (tape.recording || tape.playing)
10896 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10898 UpdateAndDisplayGameControlValues();
10901 void AdvanceFrameAndPlayerCounters(int player_nr)
10905 /* advance frame counters (global frame counter and time frame counter) */
10909 /* advance player counters (counters for move delay, move animation etc.) */
10910 for (i = 0; i < MAX_PLAYERS; i++)
10912 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10913 int move_delay_value = stored_player[i].move_delay_value;
10914 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10916 if (!advance_player_counters) /* not all players may be affected */
10919 if (move_frames == 0) /* less than one move per game frame */
10921 int stepsize = TILEX / move_delay_value;
10922 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10923 int count = (stored_player[i].is_moving ?
10924 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10926 if (count % delay == 0)
10930 stored_player[i].Frame += move_frames;
10932 if (stored_player[i].MovPos != 0)
10933 stored_player[i].StepFrame += move_frames;
10935 if (stored_player[i].move_delay > 0)
10936 stored_player[i].move_delay--;
10938 /* due to bugs in previous versions, counter must count up, not down */
10939 if (stored_player[i].push_delay != -1)
10940 stored_player[i].push_delay++;
10942 if (stored_player[i].drop_delay > 0)
10943 stored_player[i].drop_delay--;
10945 if (stored_player[i].is_dropping_pressed)
10946 stored_player[i].drop_pressed_delay++;
10950 void StartGameActions(boolean init_network_game, boolean record_tape,
10953 unsigned int new_random_seed = InitRND(random_seed);
10956 TapeStartRecording(new_random_seed);
10958 #if defined(NETWORK_AVALIABLE)
10959 if (init_network_game)
10961 SendToServer_StartPlaying();
10970 void GameActionsExt()
10973 static unsigned int game_frame_delay = 0;
10975 unsigned int game_frame_delay_value;
10976 byte *recorded_player_action;
10977 byte summarized_player_action = 0;
10978 byte tape_action[MAX_PLAYERS];
10981 /* detect endless loops, caused by custom element programming */
10982 if (recursion_loop_detected && recursion_loop_depth == 0)
10984 char *message = getStringCat3("Internal Error! Element ",
10985 EL_NAME(recursion_loop_element),
10986 " caused endless loop! Quit the game?");
10988 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10989 EL_NAME(recursion_loop_element));
10991 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10993 recursion_loop_detected = FALSE; /* if game should be continued */
11000 if (game.restart_level)
11001 StartGameActions(options.network, setup.autorecord, level.random_seed);
11003 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11004 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11006 if (level.native_em_level->lev->home == 0) /* all players at home */
11008 PlayerWins(local_player);
11010 AllPlayersGone = TRUE;
11012 level.native_em_level->lev->home = -1;
11015 if (level.native_em_level->ply[0]->alive == 0 &&
11016 level.native_em_level->ply[1]->alive == 0 &&
11017 level.native_em_level->ply[2]->alive == 0 &&
11018 level.native_em_level->ply[3]->alive == 0) /* all dead */
11019 AllPlayersGone = TRUE;
11021 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11023 if (game_sp.LevelSolved &&
11024 !game_sp.GameOver) /* game won */
11026 PlayerWins(local_player);
11028 game_sp.GameOver = TRUE;
11030 AllPlayersGone = TRUE;
11033 if (game_sp.GameOver) /* game lost */
11034 AllPlayersGone = TRUE;
11037 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11040 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11043 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11046 game_frame_delay_value =
11047 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11049 if (tape.playing && tape.warp_forward && !tape.pausing)
11050 game_frame_delay_value = 0;
11052 SetVideoFrameDelay(game_frame_delay_value);
11056 /* ---------- main game synchronization point ---------- */
11058 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11060 printf("::: skip == %d\n", skip);
11063 /* ---------- main game synchronization point ---------- */
11065 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11069 if (network_playing && !network_player_action_received)
11071 /* try to get network player actions in time */
11073 #if defined(NETWORK_AVALIABLE)
11074 /* last chance to get network player actions without main loop delay */
11075 HandleNetworking();
11078 /* game was quit by network peer */
11079 if (game_status != GAME_MODE_PLAYING)
11082 if (!network_player_action_received)
11083 return; /* failed to get network player actions in time */
11085 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11091 /* at this point we know that we really continue executing the game */
11093 network_player_action_received = FALSE;
11095 /* when playing tape, read previously recorded player input from tape data */
11096 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11098 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11102 if (tape.set_centered_player)
11104 game.centered_player_nr_next = tape.centered_player_nr_next;
11105 game.set_centered_player = TRUE;
11108 for (i = 0; i < MAX_PLAYERS; i++)
11110 summarized_player_action |= stored_player[i].action;
11112 if (!network_playing && (game.team_mode || tape.playing))
11113 stored_player[i].effective_action = stored_player[i].action;
11116 #if defined(NETWORK_AVALIABLE)
11117 if (network_playing)
11118 SendToServer_MovePlayer(summarized_player_action);
11121 // summarize all actions at local players mapped input device position
11122 // (this allows using different input devices in single player mode)
11123 if (!options.network && !game.team_mode)
11124 stored_player[map_player_action[local_player->index_nr]].effective_action =
11125 summarized_player_action;
11127 if (tape.recording &&
11129 setup.input_on_focus &&
11130 game.centered_player_nr != -1)
11132 for (i = 0; i < MAX_PLAYERS; i++)
11133 stored_player[i].effective_action =
11134 (i == game.centered_player_nr ? summarized_player_action : 0);
11137 if (recorded_player_action != NULL)
11138 for (i = 0; i < MAX_PLAYERS; i++)
11139 stored_player[i].effective_action = recorded_player_action[i];
11141 for (i = 0; i < MAX_PLAYERS; i++)
11143 tape_action[i] = stored_player[i].effective_action;
11145 /* (this may happen in the RND game engine if a player was not present on
11146 the playfield on level start, but appeared later from a custom element */
11147 if (setup.team_mode &&
11150 !tape.player_participates[i])
11151 tape.player_participates[i] = TRUE;
11154 /* only record actions from input devices, but not programmed actions */
11155 if (tape.recording)
11156 TapeRecordAction(tape_action);
11158 #if USE_NEW_PLAYER_ASSIGNMENTS
11159 // !!! also map player actions in single player mode !!!
11160 // if (game.team_mode)
11163 byte mapped_action[MAX_PLAYERS];
11165 #if DEBUG_PLAYER_ACTIONS
11167 for (i = 0; i < MAX_PLAYERS; i++)
11168 printf(" %d, ", stored_player[i].effective_action);
11171 for (i = 0; i < MAX_PLAYERS; i++)
11172 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11174 for (i = 0; i < MAX_PLAYERS; i++)
11175 stored_player[i].effective_action = mapped_action[i];
11177 #if DEBUG_PLAYER_ACTIONS
11179 for (i = 0; i < MAX_PLAYERS; i++)
11180 printf(" %d, ", stored_player[i].effective_action);
11184 #if DEBUG_PLAYER_ACTIONS
11188 for (i = 0; i < MAX_PLAYERS; i++)
11189 printf(" %d, ", stored_player[i].effective_action);
11195 for (i = 0; i < MAX_PLAYERS; i++)
11197 // allow engine snapshot in case of changed movement attempt
11198 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11199 (stored_player[i].effective_action & KEY_MOTION))
11200 game.snapshot.changed_action = TRUE;
11202 // allow engine snapshot in case of snapping/dropping attempt
11203 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11204 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11205 game.snapshot.changed_action = TRUE;
11207 game.snapshot.last_action[i] = stored_player[i].effective_action;
11210 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11212 GameActions_EM_Main();
11214 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11216 GameActions_SP_Main();
11220 GameActions_RND_Main();
11223 BlitScreenToBitmap(backbuffer);
11227 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11229 if (options.debug) /* calculate frames per second */
11231 static unsigned int fps_counter = 0;
11232 static int fps_frames = 0;
11233 unsigned int fps_delay_ms = Counter() - fps_counter;
11237 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11239 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11242 fps_counter = Counter();
11245 redraw_mask |= REDRAW_FPS;
11249 static void GameActions_CheckSaveEngineSnapshot()
11251 if (!game.snapshot.save_snapshot)
11254 // clear flag for saving snapshot _before_ saving snapshot
11255 game.snapshot.save_snapshot = FALSE;
11257 SaveEngineSnapshotToList();
11264 GameActions_CheckSaveEngineSnapshot();
11267 void GameActions_EM_Main()
11269 byte effective_action[MAX_PLAYERS];
11270 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11273 for (i = 0; i < MAX_PLAYERS; i++)
11274 effective_action[i] = stored_player[i].effective_action;
11276 GameActions_EM(effective_action, warp_mode);
11279 void GameActions_SP_Main()
11281 byte effective_action[MAX_PLAYERS];
11282 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11285 for (i = 0; i < MAX_PLAYERS; i++)
11286 effective_action[i] = stored_player[i].effective_action;
11288 GameActions_SP(effective_action, warp_mode);
11291 void GameActions_RND_Main()
11296 void GameActions_RND()
11298 int magic_wall_x = 0, magic_wall_y = 0;
11299 int i, x, y, element, graphic;
11301 InitPlayfieldScanModeVars();
11303 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11305 SCAN_PLAYFIELD(x, y)
11307 ChangeCount[x][y] = 0;
11308 ChangeEvent[x][y] = -1;
11312 if (game.set_centered_player)
11314 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11316 /* switching to "all players" only possible if all players fit to screen */
11317 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11319 game.centered_player_nr_next = game.centered_player_nr;
11320 game.set_centered_player = FALSE;
11323 /* do not switch focus to non-existing (or non-active) player */
11324 if (game.centered_player_nr_next >= 0 &&
11325 !stored_player[game.centered_player_nr_next].active)
11327 game.centered_player_nr_next = game.centered_player_nr;
11328 game.set_centered_player = FALSE;
11332 if (game.set_centered_player &&
11333 ScreenMovPos == 0) /* screen currently aligned at tile position */
11337 if (game.centered_player_nr_next == -1)
11339 setScreenCenteredToAllPlayers(&sx, &sy);
11343 sx = stored_player[game.centered_player_nr_next].jx;
11344 sy = stored_player[game.centered_player_nr_next].jy;
11347 game.centered_player_nr = game.centered_player_nr_next;
11348 game.set_centered_player = FALSE;
11350 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11351 DrawGameDoorValues();
11354 for (i = 0; i < MAX_PLAYERS; i++)
11356 int actual_player_action = stored_player[i].effective_action;
11359 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11360 - rnd_equinox_tetrachloride 048
11361 - rnd_equinox_tetrachloride_ii 096
11362 - rnd_emanuel_schmieg 002
11363 - doctor_sloan_ww 001, 020
11365 if (stored_player[i].MovPos == 0)
11366 CheckGravityMovement(&stored_player[i]);
11369 /* overwrite programmed action with tape action */
11370 if (stored_player[i].programmed_action)
11371 actual_player_action = stored_player[i].programmed_action;
11373 PlayerActions(&stored_player[i], actual_player_action);
11375 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11378 ScrollScreen(NULL, SCROLL_GO_ON);
11380 /* for backwards compatibility, the following code emulates a fixed bug that
11381 occured when pushing elements (causing elements that just made their last
11382 pushing step to already (if possible) make their first falling step in the
11383 same game frame, which is bad); this code is also needed to use the famous
11384 "spring push bug" which is used in older levels and might be wanted to be
11385 used also in newer levels, but in this case the buggy pushing code is only
11386 affecting the "spring" element and no other elements */
11388 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11390 for (i = 0; i < MAX_PLAYERS; i++)
11392 struct PlayerInfo *player = &stored_player[i];
11393 int x = player->jx;
11394 int y = player->jy;
11396 if (player->active && player->is_pushing && player->is_moving &&
11398 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11399 Feld[x][y] == EL_SPRING))
11401 ContinueMoving(x, y);
11403 /* continue moving after pushing (this is actually a bug) */
11404 if (!IS_MOVING(x, y))
11405 Stop[x][y] = FALSE;
11410 SCAN_PLAYFIELD(x, y)
11412 ChangeCount[x][y] = 0;
11413 ChangeEvent[x][y] = -1;
11415 /* this must be handled before main playfield loop */
11416 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11419 if (MovDelay[x][y] <= 0)
11423 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11426 if (MovDelay[x][y] <= 0)
11429 TEST_DrawLevelField(x, y);
11431 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11436 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11438 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11439 printf("GameActions(): This should never happen!\n");
11441 ChangePage[x][y] = -1;
11445 Stop[x][y] = FALSE;
11446 if (WasJustMoving[x][y] > 0)
11447 WasJustMoving[x][y]--;
11448 if (WasJustFalling[x][y] > 0)
11449 WasJustFalling[x][y]--;
11450 if (CheckCollision[x][y] > 0)
11451 CheckCollision[x][y]--;
11452 if (CheckImpact[x][y] > 0)
11453 CheckImpact[x][y]--;
11457 /* reset finished pushing action (not done in ContinueMoving() to allow
11458 continuous pushing animation for elements with zero push delay) */
11459 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11461 ResetGfxAnimation(x, y);
11462 TEST_DrawLevelField(x, y);
11466 if (IS_BLOCKED(x, y))
11470 Blocked2Moving(x, y, &oldx, &oldy);
11471 if (!IS_MOVING(oldx, oldy))
11473 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11474 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11475 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11476 printf("GameActions(): This should never happen!\n");
11482 SCAN_PLAYFIELD(x, y)
11484 element = Feld[x][y];
11485 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11487 ResetGfxFrame(x, y, TRUE);
11489 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11490 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11491 ResetRandomAnimationValue(x, y);
11493 SetRandomAnimationValue(x, y);
11495 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11497 if (IS_INACTIVE(element))
11499 if (IS_ANIMATED(graphic))
11500 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11505 /* this may take place after moving, so 'element' may have changed */
11506 if (IS_CHANGING(x, y) &&
11507 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11509 int page = element_info[element].event_page_nr[CE_DELAY];
11511 HandleElementChange(x, y, page);
11513 element = Feld[x][y];
11514 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11517 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11521 element = Feld[x][y];
11522 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11524 if (IS_ANIMATED(graphic) &&
11525 !IS_MOVING(x, y) &&
11527 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11529 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11530 TEST_DrawTwinkleOnField(x, y);
11532 else if ((element == EL_ACID ||
11533 element == EL_EXIT_OPEN ||
11534 element == EL_EM_EXIT_OPEN ||
11535 element == EL_SP_EXIT_OPEN ||
11536 element == EL_STEEL_EXIT_OPEN ||
11537 element == EL_EM_STEEL_EXIT_OPEN ||
11538 element == EL_SP_TERMINAL ||
11539 element == EL_SP_TERMINAL_ACTIVE ||
11540 element == EL_EXTRA_TIME ||
11541 element == EL_SHIELD_NORMAL ||
11542 element == EL_SHIELD_DEADLY) &&
11543 IS_ANIMATED(graphic))
11544 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11545 else if (IS_MOVING(x, y))
11546 ContinueMoving(x, y);
11547 else if (IS_ACTIVE_BOMB(element))
11548 CheckDynamite(x, y);
11549 else if (element == EL_AMOEBA_GROWING)
11550 AmoebeWaechst(x, y);
11551 else if (element == EL_AMOEBA_SHRINKING)
11552 AmoebaDisappearing(x, y);
11554 #if !USE_NEW_AMOEBA_CODE
11555 else if (IS_AMOEBALIVE(element))
11556 AmoebeAbleger(x, y);
11559 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11561 else if (element == EL_EXIT_CLOSED)
11563 else if (element == EL_EM_EXIT_CLOSED)
11565 else if (element == EL_STEEL_EXIT_CLOSED)
11566 CheckExitSteel(x, y);
11567 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11568 CheckExitSteelEM(x, y);
11569 else if (element == EL_SP_EXIT_CLOSED)
11571 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11572 element == EL_EXPANDABLE_STEELWALL_GROWING)
11573 MauerWaechst(x, y);
11574 else if (element == EL_EXPANDABLE_WALL ||
11575 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11576 element == EL_EXPANDABLE_WALL_VERTICAL ||
11577 element == EL_EXPANDABLE_WALL_ANY ||
11578 element == EL_BD_EXPANDABLE_WALL)
11579 MauerAbleger(x, y);
11580 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11581 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11582 element == EL_EXPANDABLE_STEELWALL_ANY)
11583 MauerAblegerStahl(x, y);
11584 else if (element == EL_FLAMES)
11585 CheckForDragon(x, y);
11586 else if (element == EL_EXPLOSION)
11587 ; /* drawing of correct explosion animation is handled separately */
11588 else if (element == EL_ELEMENT_SNAPPING ||
11589 element == EL_DIAGONAL_SHRINKING ||
11590 element == EL_DIAGONAL_GROWING)
11592 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11594 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11596 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11597 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11599 if (IS_BELT_ACTIVE(element))
11600 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11602 if (game.magic_wall_active)
11604 int jx = local_player->jx, jy = local_player->jy;
11606 /* play the element sound at the position nearest to the player */
11607 if ((element == EL_MAGIC_WALL_FULL ||
11608 element == EL_MAGIC_WALL_ACTIVE ||
11609 element == EL_MAGIC_WALL_EMPTYING ||
11610 element == EL_BD_MAGIC_WALL_FULL ||
11611 element == EL_BD_MAGIC_WALL_ACTIVE ||
11612 element == EL_BD_MAGIC_WALL_EMPTYING ||
11613 element == EL_DC_MAGIC_WALL_FULL ||
11614 element == EL_DC_MAGIC_WALL_ACTIVE ||
11615 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11616 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11624 #if USE_NEW_AMOEBA_CODE
11625 /* new experimental amoeba growth stuff */
11626 if (!(FrameCounter % 8))
11628 static unsigned int random = 1684108901;
11630 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11632 x = RND(lev_fieldx);
11633 y = RND(lev_fieldy);
11634 element = Feld[x][y];
11636 if (!IS_PLAYER(x,y) &&
11637 (element == EL_EMPTY ||
11638 CAN_GROW_INTO(element) ||
11639 element == EL_QUICKSAND_EMPTY ||
11640 element == EL_QUICKSAND_FAST_EMPTY ||
11641 element == EL_ACID_SPLASH_LEFT ||
11642 element == EL_ACID_SPLASH_RIGHT))
11644 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11645 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11646 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11647 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11648 Feld[x][y] = EL_AMOEBA_DROP;
11651 random = random * 129 + 1;
11656 game.explosions_delayed = FALSE;
11658 SCAN_PLAYFIELD(x, y)
11660 element = Feld[x][y];
11662 if (ExplodeField[x][y])
11663 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11664 else if (element == EL_EXPLOSION)
11665 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11667 ExplodeField[x][y] = EX_TYPE_NONE;
11670 game.explosions_delayed = TRUE;
11672 if (game.magic_wall_active)
11674 if (!(game.magic_wall_time_left % 4))
11676 int element = Feld[magic_wall_x][magic_wall_y];
11678 if (element == EL_BD_MAGIC_WALL_FULL ||
11679 element == EL_BD_MAGIC_WALL_ACTIVE ||
11680 element == EL_BD_MAGIC_WALL_EMPTYING)
11681 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11682 else if (element == EL_DC_MAGIC_WALL_FULL ||
11683 element == EL_DC_MAGIC_WALL_ACTIVE ||
11684 element == EL_DC_MAGIC_WALL_EMPTYING)
11685 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11687 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11690 if (game.magic_wall_time_left > 0)
11692 game.magic_wall_time_left--;
11694 if (!game.magic_wall_time_left)
11696 SCAN_PLAYFIELD(x, y)
11698 element = Feld[x][y];
11700 if (element == EL_MAGIC_WALL_ACTIVE ||
11701 element == EL_MAGIC_WALL_FULL)
11703 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11704 TEST_DrawLevelField(x, y);
11706 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11707 element == EL_BD_MAGIC_WALL_FULL)
11709 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11710 TEST_DrawLevelField(x, y);
11712 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11713 element == EL_DC_MAGIC_WALL_FULL)
11715 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11716 TEST_DrawLevelField(x, y);
11720 game.magic_wall_active = FALSE;
11725 if (game.light_time_left > 0)
11727 game.light_time_left--;
11729 if (game.light_time_left == 0)
11730 RedrawAllLightSwitchesAndInvisibleElements();
11733 if (game.timegate_time_left > 0)
11735 game.timegate_time_left--;
11737 if (game.timegate_time_left == 0)
11738 CloseAllOpenTimegates();
11741 if (game.lenses_time_left > 0)
11743 game.lenses_time_left--;
11745 if (game.lenses_time_left == 0)
11746 RedrawAllInvisibleElementsForLenses();
11749 if (game.magnify_time_left > 0)
11751 game.magnify_time_left--;
11753 if (game.magnify_time_left == 0)
11754 RedrawAllInvisibleElementsForMagnifier();
11757 for (i = 0; i < MAX_PLAYERS; i++)
11759 struct PlayerInfo *player = &stored_player[i];
11761 if (SHIELD_ON(player))
11763 if (player->shield_deadly_time_left)
11764 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11765 else if (player->shield_normal_time_left)
11766 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11770 #if USE_DELAYED_GFX_REDRAW
11771 SCAN_PLAYFIELD(x, y)
11773 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11775 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11776 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11778 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11779 DrawLevelField(x, y);
11781 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11782 DrawLevelFieldCrumbled(x, y);
11784 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11785 DrawLevelFieldCrumbledNeighbours(x, y);
11787 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11788 DrawTwinkleOnField(x, y);
11791 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11796 PlayAllPlayersSound();
11798 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11800 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11802 local_player->show_envelope = 0;
11805 /* use random number generator in every frame to make it less predictable */
11806 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11810 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11812 int min_x = x, min_y = y, max_x = x, max_y = y;
11815 for (i = 0; i < MAX_PLAYERS; i++)
11817 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11819 if (!stored_player[i].active || &stored_player[i] == player)
11822 min_x = MIN(min_x, jx);
11823 min_y = MIN(min_y, jy);
11824 max_x = MAX(max_x, jx);
11825 max_y = MAX(max_y, jy);
11828 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11831 static boolean AllPlayersInVisibleScreen()
11835 for (i = 0; i < MAX_PLAYERS; i++)
11837 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11839 if (!stored_player[i].active)
11842 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11849 void ScrollLevel(int dx, int dy)
11851 int scroll_offset = 2 * TILEX_VAR;
11854 BlitBitmap(drawto_field, drawto_field,
11855 FX + TILEX_VAR * (dx == -1) - scroll_offset,
11856 FY + TILEY_VAR * (dy == -1) - scroll_offset,
11857 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11858 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11859 FX + TILEX_VAR * (dx == 1) - scroll_offset,
11860 FY + TILEY_VAR * (dy == 1) - scroll_offset);
11864 x = (dx == 1 ? BX1 : BX2);
11865 for (y = BY1; y <= BY2; y++)
11866 DrawScreenField(x, y);
11871 y = (dy == 1 ? BY1 : BY2);
11872 for (x = BX1; x <= BX2; x++)
11873 DrawScreenField(x, y);
11876 redraw_mask |= REDRAW_FIELD;
11879 static boolean canFallDown(struct PlayerInfo *player)
11881 int jx = player->jx, jy = player->jy;
11883 return (IN_LEV_FIELD(jx, jy + 1) &&
11884 (IS_FREE(jx, jy + 1) ||
11885 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11886 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11887 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11890 static boolean canPassField(int x, int y, int move_dir)
11892 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11893 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11894 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11895 int nextx = x + dx;
11896 int nexty = y + dy;
11897 int element = Feld[x][y];
11899 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11900 !CAN_MOVE(element) &&
11901 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11902 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11903 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11906 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11908 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11909 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11910 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11914 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11915 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11916 (IS_DIGGABLE(Feld[newx][newy]) ||
11917 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11918 canPassField(newx, newy, move_dir)));
11921 static void CheckGravityMovement(struct PlayerInfo *player)
11923 if (player->gravity && !player->programmed_action)
11925 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11926 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11927 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11928 int jx = player->jx, jy = player->jy;
11929 boolean player_is_moving_to_valid_field =
11930 (!player_is_snapping &&
11931 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11932 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11933 boolean player_can_fall_down = canFallDown(player);
11935 if (player_can_fall_down &&
11936 !player_is_moving_to_valid_field)
11937 player->programmed_action = MV_DOWN;
11941 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11943 return CheckGravityMovement(player);
11945 if (player->gravity && !player->programmed_action)
11947 int jx = player->jx, jy = player->jy;
11948 boolean field_under_player_is_free =
11949 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11950 boolean player_is_standing_on_valid_field =
11951 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11952 (IS_WALKABLE(Feld[jx][jy]) &&
11953 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11955 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11956 player->programmed_action = MV_DOWN;
11961 MovePlayerOneStep()
11962 -----------------------------------------------------------------------------
11963 dx, dy: direction (non-diagonal) to try to move the player to
11964 real_dx, real_dy: direction as read from input device (can be diagonal)
11967 boolean MovePlayerOneStep(struct PlayerInfo *player,
11968 int dx, int dy, int real_dx, int real_dy)
11970 int jx = player->jx, jy = player->jy;
11971 int new_jx = jx + dx, new_jy = jy + dy;
11973 boolean player_can_move = !player->cannot_move;
11975 if (!player->active || (!dx && !dy))
11976 return MP_NO_ACTION;
11978 player->MovDir = (dx < 0 ? MV_LEFT :
11979 dx > 0 ? MV_RIGHT :
11981 dy > 0 ? MV_DOWN : MV_NONE);
11983 if (!IN_LEV_FIELD(new_jx, new_jy))
11984 return MP_NO_ACTION;
11986 if (!player_can_move)
11988 if (player->MovPos == 0)
11990 player->is_moving = FALSE;
11991 player->is_digging = FALSE;
11992 player->is_collecting = FALSE;
11993 player->is_snapping = FALSE;
11994 player->is_pushing = FALSE;
11998 if (!options.network && game.centered_player_nr == -1 &&
11999 !AllPlayersInSight(player, new_jx, new_jy))
12000 return MP_NO_ACTION;
12002 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12003 if (can_move != MP_MOVING)
12006 /* check if DigField() has caused relocation of the player */
12007 if (player->jx != jx || player->jy != jy)
12008 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12010 StorePlayer[jx][jy] = 0;
12011 player->last_jx = jx;
12012 player->last_jy = jy;
12013 player->jx = new_jx;
12014 player->jy = new_jy;
12015 StorePlayer[new_jx][new_jy] = player->element_nr;
12017 if (player->move_delay_value_next != -1)
12019 player->move_delay_value = player->move_delay_value_next;
12020 player->move_delay_value_next = -1;
12024 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12026 player->step_counter++;
12028 PlayerVisit[jx][jy] = FrameCounter;
12030 player->is_moving = TRUE;
12033 /* should better be called in MovePlayer(), but this breaks some tapes */
12034 ScrollPlayer(player, SCROLL_INIT);
12040 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12042 int jx = player->jx, jy = player->jy;
12043 int old_jx = jx, old_jy = jy;
12044 int moved = MP_NO_ACTION;
12046 if (!player->active)
12051 if (player->MovPos == 0)
12053 player->is_moving = FALSE;
12054 player->is_digging = FALSE;
12055 player->is_collecting = FALSE;
12056 player->is_snapping = FALSE;
12057 player->is_pushing = FALSE;
12063 if (player->move_delay > 0)
12066 player->move_delay = -1; /* set to "uninitialized" value */
12068 /* store if player is automatically moved to next field */
12069 player->is_auto_moving = (player->programmed_action != MV_NONE);
12071 /* remove the last programmed player action */
12072 player->programmed_action = 0;
12074 if (player->MovPos)
12076 /* should only happen if pre-1.2 tape recordings are played */
12077 /* this is only for backward compatibility */
12079 int original_move_delay_value = player->move_delay_value;
12082 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12086 /* scroll remaining steps with finest movement resolution */
12087 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12089 while (player->MovPos)
12091 ScrollPlayer(player, SCROLL_GO_ON);
12092 ScrollScreen(NULL, SCROLL_GO_ON);
12094 AdvanceFrameAndPlayerCounters(player->index_nr);
12097 BackToFront_WithFrameDelay(0);
12100 player->move_delay_value = original_move_delay_value;
12103 player->is_active = FALSE;
12105 if (player->last_move_dir & MV_HORIZONTAL)
12107 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12108 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12112 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12113 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12116 if (!moved && !player->is_active)
12118 player->is_moving = FALSE;
12119 player->is_digging = FALSE;
12120 player->is_collecting = FALSE;
12121 player->is_snapping = FALSE;
12122 player->is_pushing = FALSE;
12128 if (moved & MP_MOVING && !ScreenMovPos &&
12129 (player->index_nr == game.centered_player_nr ||
12130 game.centered_player_nr == -1))
12132 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12133 int offset = game.scroll_delay_value;
12135 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12137 /* actual player has left the screen -- scroll in that direction */
12138 if (jx != old_jx) /* player has moved horizontally */
12139 scroll_x += (jx - old_jx);
12140 else /* player has moved vertically */
12141 scroll_y += (jy - old_jy);
12145 if (jx != old_jx) /* player has moved horizontally */
12147 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12148 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12149 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12151 /* don't scroll over playfield boundaries */
12152 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12153 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12155 /* don't scroll more than one field at a time */
12156 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12158 /* don't scroll against the player's moving direction */
12159 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12160 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12161 scroll_x = old_scroll_x;
12163 else /* player has moved vertically */
12165 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12166 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12167 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12169 /* don't scroll over playfield boundaries */
12170 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12171 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12173 /* don't scroll more than one field at a time */
12174 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12176 /* don't scroll against the player's moving direction */
12177 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12178 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12179 scroll_y = old_scroll_y;
12183 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12185 if (!options.network && game.centered_player_nr == -1 &&
12186 !AllPlayersInVisibleScreen())
12188 scroll_x = old_scroll_x;
12189 scroll_y = old_scroll_y;
12193 ScrollScreen(player, SCROLL_INIT);
12194 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12199 player->StepFrame = 0;
12201 if (moved & MP_MOVING)
12203 if (old_jx != jx && old_jy == jy)
12204 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12205 else if (old_jx == jx && old_jy != jy)
12206 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12208 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12210 player->last_move_dir = player->MovDir;
12211 player->is_moving = TRUE;
12212 player->is_snapping = FALSE;
12213 player->is_switching = FALSE;
12214 player->is_dropping = FALSE;
12215 player->is_dropping_pressed = FALSE;
12216 player->drop_pressed_delay = 0;
12219 /* should better be called here than above, but this breaks some tapes */
12220 ScrollPlayer(player, SCROLL_INIT);
12225 CheckGravityMovementWhenNotMoving(player);
12227 player->is_moving = FALSE;
12229 /* at this point, the player is allowed to move, but cannot move right now
12230 (e.g. because of something blocking the way) -- ensure that the player
12231 is also allowed to move in the next frame (in old versions before 3.1.1,
12232 the player was forced to wait again for eight frames before next try) */
12234 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12235 player->move_delay = 0; /* allow direct movement in the next frame */
12238 if (player->move_delay == -1) /* not yet initialized by DigField() */
12239 player->move_delay = player->move_delay_value;
12241 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12243 TestIfPlayerTouchesBadThing(jx, jy);
12244 TestIfPlayerTouchesCustomElement(jx, jy);
12247 if (!player->active)
12248 RemovePlayer(player);
12253 void ScrollPlayer(struct PlayerInfo *player, int mode)
12255 int jx = player->jx, jy = player->jy;
12256 int last_jx = player->last_jx, last_jy = player->last_jy;
12257 int move_stepsize = TILEX / player->move_delay_value;
12259 if (!player->active)
12262 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12265 if (mode == SCROLL_INIT)
12267 player->actual_frame_counter = FrameCounter;
12268 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12270 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12271 Feld[last_jx][last_jy] == EL_EMPTY)
12273 int last_field_block_delay = 0; /* start with no blocking at all */
12274 int block_delay_adjustment = player->block_delay_adjustment;
12276 /* if player blocks last field, add delay for exactly one move */
12277 if (player->block_last_field)
12279 last_field_block_delay += player->move_delay_value;
12281 /* when blocking enabled, prevent moving up despite gravity */
12282 if (player->gravity && player->MovDir == MV_UP)
12283 block_delay_adjustment = -1;
12286 /* add block delay adjustment (also possible when not blocking) */
12287 last_field_block_delay += block_delay_adjustment;
12289 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12290 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12293 if (player->MovPos != 0) /* player has not yet reached destination */
12296 else if (!FrameReached(&player->actual_frame_counter, 1))
12299 if (player->MovPos != 0)
12301 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12302 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12304 /* before DrawPlayer() to draw correct player graphic for this case */
12305 if (player->MovPos == 0)
12306 CheckGravityMovement(player);
12309 if (player->MovPos == 0) /* player reached destination field */
12311 if (player->move_delay_reset_counter > 0)
12313 player->move_delay_reset_counter--;
12315 if (player->move_delay_reset_counter == 0)
12317 /* continue with normal speed after quickly moving through gate */
12318 HALVE_PLAYER_SPEED(player);
12320 /* be able to make the next move without delay */
12321 player->move_delay = 0;
12325 player->last_jx = jx;
12326 player->last_jy = jy;
12328 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12329 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12330 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12331 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12332 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12333 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12334 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12335 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12337 DrawPlayer(player); /* needed here only to cleanup last field */
12338 RemovePlayer(player);
12340 if (local_player->friends_still_needed == 0 ||
12341 IS_SP_ELEMENT(Feld[jx][jy]))
12342 PlayerWins(player);
12345 /* this breaks one level: "machine", level 000 */
12347 int move_direction = player->MovDir;
12348 int enter_side = MV_DIR_OPPOSITE(move_direction);
12349 int leave_side = move_direction;
12350 int old_jx = last_jx;
12351 int old_jy = last_jy;
12352 int old_element = Feld[old_jx][old_jy];
12353 int new_element = Feld[jx][jy];
12355 if (IS_CUSTOM_ELEMENT(old_element))
12356 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12358 player->index_bit, leave_side);
12360 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12361 CE_PLAYER_LEAVES_X,
12362 player->index_bit, leave_side);
12364 if (IS_CUSTOM_ELEMENT(new_element))
12365 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12366 player->index_bit, enter_side);
12368 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12369 CE_PLAYER_ENTERS_X,
12370 player->index_bit, enter_side);
12372 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12373 CE_MOVE_OF_X, move_direction);
12376 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12378 TestIfPlayerTouchesBadThing(jx, jy);
12379 TestIfPlayerTouchesCustomElement(jx, jy);
12381 /* needed because pushed element has not yet reached its destination,
12382 so it would trigger a change event at its previous field location */
12383 if (!player->is_pushing)
12384 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12386 if (!player->active)
12387 RemovePlayer(player);
12390 if (!local_player->LevelSolved && level.use_step_counter)
12400 if (TimeLeft <= 10 && setup.time_limit)
12401 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12403 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12405 DisplayGameControlValues();
12407 if (!TimeLeft && setup.time_limit)
12408 for (i = 0; i < MAX_PLAYERS; i++)
12409 KillPlayer(&stored_player[i]);
12411 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12413 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12415 DisplayGameControlValues();
12419 if (tape.single_step && tape.recording && !tape.pausing &&
12420 !player->programmed_action)
12421 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12423 if (!player->programmed_action)
12424 CheckSaveEngineSnapshot(player);
12428 void ScrollScreen(struct PlayerInfo *player, int mode)
12430 static unsigned int screen_frame_counter = 0;
12432 if (mode == SCROLL_INIT)
12434 /* set scrolling step size according to actual player's moving speed */
12435 ScrollStepSize = TILEX / player->move_delay_value;
12437 screen_frame_counter = FrameCounter;
12438 ScreenMovDir = player->MovDir;
12439 ScreenMovPos = player->MovPos;
12440 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12443 else if (!FrameReached(&screen_frame_counter, 1))
12448 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12449 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12450 redraw_mask |= REDRAW_FIELD;
12453 ScreenMovDir = MV_NONE;
12456 void TestIfPlayerTouchesCustomElement(int x, int y)
12458 static int xy[4][2] =
12465 static int trigger_sides[4][2] =
12467 /* center side border side */
12468 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12469 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12470 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12471 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12473 static int touch_dir[4] =
12475 MV_LEFT | MV_RIGHT,
12480 int center_element = Feld[x][y]; /* should always be non-moving! */
12483 for (i = 0; i < NUM_DIRECTIONS; i++)
12485 int xx = x + xy[i][0];
12486 int yy = y + xy[i][1];
12487 int center_side = trigger_sides[i][0];
12488 int border_side = trigger_sides[i][1];
12489 int border_element;
12491 if (!IN_LEV_FIELD(xx, yy))
12494 if (IS_PLAYER(x, y)) /* player found at center element */
12496 struct PlayerInfo *player = PLAYERINFO(x, y);
12498 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12499 border_element = Feld[xx][yy]; /* may be moving! */
12500 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12501 border_element = Feld[xx][yy];
12502 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12503 border_element = MovingOrBlocked2Element(xx, yy);
12505 continue; /* center and border element do not touch */
12507 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12508 player->index_bit, border_side);
12509 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12510 CE_PLAYER_TOUCHES_X,
12511 player->index_bit, border_side);
12514 /* use player element that is initially defined in the level playfield,
12515 not the player element that corresponds to the runtime player number
12516 (example: a level that contains EL_PLAYER_3 as the only player would
12517 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12518 int player_element = PLAYERINFO(x, y)->initial_element;
12520 CheckElementChangeBySide(xx, yy, border_element, player_element,
12521 CE_TOUCHING_X, border_side);
12524 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12526 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12528 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12530 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12531 continue; /* center and border element do not touch */
12534 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12535 player->index_bit, center_side);
12536 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12537 CE_PLAYER_TOUCHES_X,
12538 player->index_bit, center_side);
12541 /* use player element that is initially defined in the level playfield,
12542 not the player element that corresponds to the runtime player number
12543 (example: a level that contains EL_PLAYER_3 as the only player would
12544 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12545 int player_element = PLAYERINFO(xx, yy)->initial_element;
12547 CheckElementChangeBySide(x, y, center_element, player_element,
12548 CE_TOUCHING_X, center_side);
12556 void TestIfElementTouchesCustomElement(int x, int y)
12558 static int xy[4][2] =
12565 static int trigger_sides[4][2] =
12567 /* center side border side */
12568 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12569 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12570 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12571 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12573 static int touch_dir[4] =
12575 MV_LEFT | MV_RIGHT,
12580 boolean change_center_element = FALSE;
12581 int center_element = Feld[x][y]; /* should always be non-moving! */
12582 int border_element_old[NUM_DIRECTIONS];
12585 for (i = 0; i < NUM_DIRECTIONS; i++)
12587 int xx = x + xy[i][0];
12588 int yy = y + xy[i][1];
12589 int border_element;
12591 border_element_old[i] = -1;
12593 if (!IN_LEV_FIELD(xx, yy))
12596 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12597 border_element = Feld[xx][yy]; /* may be moving! */
12598 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12599 border_element = Feld[xx][yy];
12600 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12601 border_element = MovingOrBlocked2Element(xx, yy);
12603 continue; /* center and border element do not touch */
12605 border_element_old[i] = border_element;
12608 for (i = 0; i < NUM_DIRECTIONS; i++)
12610 int xx = x + xy[i][0];
12611 int yy = y + xy[i][1];
12612 int center_side = trigger_sides[i][0];
12613 int border_element = border_element_old[i];
12615 if (border_element == -1)
12618 /* check for change of border element */
12619 CheckElementChangeBySide(xx, yy, border_element, center_element,
12620 CE_TOUCHING_X, center_side);
12622 /* (center element cannot be player, so we dont have to check this here) */
12625 for (i = 0; i < NUM_DIRECTIONS; i++)
12627 int xx = x + xy[i][0];
12628 int yy = y + xy[i][1];
12629 int border_side = trigger_sides[i][1];
12630 int border_element = border_element_old[i];
12632 if (border_element == -1)
12635 /* check for change of center element (but change it only once) */
12636 if (!change_center_element)
12637 change_center_element =
12638 CheckElementChangeBySide(x, y, center_element, border_element,
12639 CE_TOUCHING_X, border_side);
12641 if (IS_PLAYER(xx, yy))
12643 /* use player element that is initially defined in the level playfield,
12644 not the player element that corresponds to the runtime player number
12645 (example: a level that contains EL_PLAYER_3 as the only player would
12646 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12647 int player_element = PLAYERINFO(xx, yy)->initial_element;
12649 CheckElementChangeBySide(x, y, center_element, player_element,
12650 CE_TOUCHING_X, border_side);
12655 void TestIfElementHitsCustomElement(int x, int y, int direction)
12657 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12658 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12659 int hitx = x + dx, hity = y + dy;
12660 int hitting_element = Feld[x][y];
12661 int touched_element;
12663 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12666 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12667 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12669 if (IN_LEV_FIELD(hitx, hity))
12671 int opposite_direction = MV_DIR_OPPOSITE(direction);
12672 int hitting_side = direction;
12673 int touched_side = opposite_direction;
12674 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12675 MovDir[hitx][hity] != direction ||
12676 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12682 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12683 CE_HITTING_X, touched_side);
12685 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12686 CE_HIT_BY_X, hitting_side);
12688 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12689 CE_HIT_BY_SOMETHING, opposite_direction);
12691 if (IS_PLAYER(hitx, hity))
12693 /* use player element that is initially defined in the level playfield,
12694 not the player element that corresponds to the runtime player number
12695 (example: a level that contains EL_PLAYER_3 as the only player would
12696 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12697 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12699 CheckElementChangeBySide(x, y, hitting_element, player_element,
12700 CE_HITTING_X, touched_side);
12705 /* "hitting something" is also true when hitting the playfield border */
12706 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12707 CE_HITTING_SOMETHING, direction);
12710 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12712 int i, kill_x = -1, kill_y = -1;
12714 int bad_element = -1;
12715 static int test_xy[4][2] =
12722 static int test_dir[4] =
12730 for (i = 0; i < NUM_DIRECTIONS; i++)
12732 int test_x, test_y, test_move_dir, test_element;
12734 test_x = good_x + test_xy[i][0];
12735 test_y = good_y + test_xy[i][1];
12737 if (!IN_LEV_FIELD(test_x, test_y))
12741 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12743 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12745 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12746 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12748 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12749 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12753 bad_element = test_element;
12759 if (kill_x != -1 || kill_y != -1)
12761 if (IS_PLAYER(good_x, good_y))
12763 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12765 if (player->shield_deadly_time_left > 0 &&
12766 !IS_INDESTRUCTIBLE(bad_element))
12767 Bang(kill_x, kill_y);
12768 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12769 KillPlayer(player);
12772 Bang(good_x, good_y);
12776 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12778 int i, kill_x = -1, kill_y = -1;
12779 int bad_element = Feld[bad_x][bad_y];
12780 static int test_xy[4][2] =
12787 static int touch_dir[4] =
12789 MV_LEFT | MV_RIGHT,
12794 static int test_dir[4] =
12802 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12805 for (i = 0; i < NUM_DIRECTIONS; i++)
12807 int test_x, test_y, test_move_dir, test_element;
12809 test_x = bad_x + test_xy[i][0];
12810 test_y = bad_y + test_xy[i][1];
12812 if (!IN_LEV_FIELD(test_x, test_y))
12816 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12818 test_element = Feld[test_x][test_y];
12820 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12821 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12823 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12824 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12826 /* good thing is player or penguin that does not move away */
12827 if (IS_PLAYER(test_x, test_y))
12829 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12831 if (bad_element == EL_ROBOT && player->is_moving)
12832 continue; /* robot does not kill player if he is moving */
12834 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12836 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12837 continue; /* center and border element do not touch */
12845 else if (test_element == EL_PENGUIN)
12855 if (kill_x != -1 || kill_y != -1)
12857 if (IS_PLAYER(kill_x, kill_y))
12859 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12861 if (player->shield_deadly_time_left > 0 &&
12862 !IS_INDESTRUCTIBLE(bad_element))
12863 Bang(bad_x, bad_y);
12864 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12865 KillPlayer(player);
12868 Bang(kill_x, kill_y);
12872 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12874 int bad_element = Feld[bad_x][bad_y];
12875 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12876 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12877 int test_x = bad_x + dx, test_y = bad_y + dy;
12878 int test_move_dir, test_element;
12879 int kill_x = -1, kill_y = -1;
12881 if (!IN_LEV_FIELD(test_x, test_y))
12885 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12887 test_element = Feld[test_x][test_y];
12889 if (test_move_dir != bad_move_dir)
12891 /* good thing can be player or penguin that does not move away */
12892 if (IS_PLAYER(test_x, test_y))
12894 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12896 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12897 player as being hit when he is moving towards the bad thing, because
12898 the "get hit by" condition would be lost after the player stops) */
12899 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12900 return; /* player moves away from bad thing */
12905 else if (test_element == EL_PENGUIN)
12912 if (kill_x != -1 || kill_y != -1)
12914 if (IS_PLAYER(kill_x, kill_y))
12916 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12918 if (player->shield_deadly_time_left > 0 &&
12919 !IS_INDESTRUCTIBLE(bad_element))
12920 Bang(bad_x, bad_y);
12921 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12922 KillPlayer(player);
12925 Bang(kill_x, kill_y);
12929 void TestIfPlayerTouchesBadThing(int x, int y)
12931 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12934 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12936 TestIfGoodThingHitsBadThing(x, y, move_dir);
12939 void TestIfBadThingTouchesPlayer(int x, int y)
12941 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12944 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12946 TestIfBadThingHitsGoodThing(x, y, move_dir);
12949 void TestIfFriendTouchesBadThing(int x, int y)
12951 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12954 void TestIfBadThingTouchesFriend(int x, int y)
12956 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12959 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12961 int i, kill_x = bad_x, kill_y = bad_y;
12962 static int xy[4][2] =
12970 for (i = 0; i < NUM_DIRECTIONS; i++)
12974 x = bad_x + xy[i][0];
12975 y = bad_y + xy[i][1];
12976 if (!IN_LEV_FIELD(x, y))
12979 element = Feld[x][y];
12980 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12981 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12989 if (kill_x != bad_x || kill_y != bad_y)
12990 Bang(bad_x, bad_y);
12993 void KillPlayer(struct PlayerInfo *player)
12995 int jx = player->jx, jy = player->jy;
12997 if (!player->active)
13001 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13002 player->killed, player->active, player->reanimated);
13005 /* the following code was introduced to prevent an infinite loop when calling
13007 -> CheckTriggeredElementChangeExt()
13008 -> ExecuteCustomElementAction()
13010 -> (infinitely repeating the above sequence of function calls)
13011 which occurs when killing the player while having a CE with the setting
13012 "kill player X when explosion of <player X>"; the solution using a new
13013 field "player->killed" was chosen for backwards compatibility, although
13014 clever use of the fields "player->active" etc. would probably also work */
13016 if (player->killed)
13020 player->killed = TRUE;
13022 /* remove accessible field at the player's position */
13023 Feld[jx][jy] = EL_EMPTY;
13025 /* deactivate shield (else Bang()/Explode() would not work right) */
13026 player->shield_normal_time_left = 0;
13027 player->shield_deadly_time_left = 0;
13030 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13031 player->killed, player->active, player->reanimated);
13037 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13038 player->killed, player->active, player->reanimated);
13041 if (player->reanimated) /* killed player may have been reanimated */
13042 player->killed = player->reanimated = FALSE;
13044 BuryPlayer(player);
13047 static void KillPlayerUnlessEnemyProtected(int x, int y)
13049 if (!PLAYER_ENEMY_PROTECTED(x, y))
13050 KillPlayer(PLAYERINFO(x, y));
13053 static void KillPlayerUnlessExplosionProtected(int x, int y)
13055 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13056 KillPlayer(PLAYERINFO(x, y));
13059 void BuryPlayer(struct PlayerInfo *player)
13061 int jx = player->jx, jy = player->jy;
13063 if (!player->active)
13066 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13067 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13069 player->GameOver = TRUE;
13070 RemovePlayer(player);
13073 void RemovePlayer(struct PlayerInfo *player)
13075 int jx = player->jx, jy = player->jy;
13076 int i, found = FALSE;
13078 player->present = FALSE;
13079 player->active = FALSE;
13081 if (!ExplodeField[jx][jy])
13082 StorePlayer[jx][jy] = 0;
13084 if (player->is_moving)
13085 TEST_DrawLevelField(player->last_jx, player->last_jy);
13087 for (i = 0; i < MAX_PLAYERS; i++)
13088 if (stored_player[i].active)
13092 AllPlayersGone = TRUE;
13098 static void setFieldForSnapping(int x, int y, int element, int direction)
13100 struct ElementInfo *ei = &element_info[element];
13101 int direction_bit = MV_DIR_TO_BIT(direction);
13102 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13103 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13104 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13106 Feld[x][y] = EL_ELEMENT_SNAPPING;
13107 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13109 ResetGfxAnimation(x, y);
13111 GfxElement[x][y] = element;
13112 GfxAction[x][y] = action;
13113 GfxDir[x][y] = direction;
13114 GfxFrame[x][y] = -1;
13118 =============================================================================
13119 checkDiagonalPushing()
13120 -----------------------------------------------------------------------------
13121 check if diagonal input device direction results in pushing of object
13122 (by checking if the alternative direction is walkable, diggable, ...)
13123 =============================================================================
13126 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13127 int x, int y, int real_dx, int real_dy)
13129 int jx, jy, dx, dy, xx, yy;
13131 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13134 /* diagonal direction: check alternative direction */
13139 xx = jx + (dx == 0 ? real_dx : 0);
13140 yy = jy + (dy == 0 ? real_dy : 0);
13142 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13146 =============================================================================
13148 -----------------------------------------------------------------------------
13149 x, y: field next to player (non-diagonal) to try to dig to
13150 real_dx, real_dy: direction as read from input device (can be diagonal)
13151 =============================================================================
13154 static int DigField(struct PlayerInfo *player,
13155 int oldx, int oldy, int x, int y,
13156 int real_dx, int real_dy, int mode)
13158 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13159 boolean player_was_pushing = player->is_pushing;
13160 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13161 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13162 int jx = oldx, jy = oldy;
13163 int dx = x - jx, dy = y - jy;
13164 int nextx = x + dx, nexty = y + dy;
13165 int move_direction = (dx == -1 ? MV_LEFT :
13166 dx == +1 ? MV_RIGHT :
13168 dy == +1 ? MV_DOWN : MV_NONE);
13169 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13170 int dig_side = MV_DIR_OPPOSITE(move_direction);
13171 int old_element = Feld[jx][jy];
13172 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13175 if (is_player) /* function can also be called by EL_PENGUIN */
13177 if (player->MovPos == 0)
13179 player->is_digging = FALSE;
13180 player->is_collecting = FALSE;
13183 if (player->MovPos == 0) /* last pushing move finished */
13184 player->is_pushing = FALSE;
13186 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13188 player->is_switching = FALSE;
13189 player->push_delay = -1;
13191 return MP_NO_ACTION;
13195 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13196 old_element = Back[jx][jy];
13198 /* in case of element dropped at player position, check background */
13199 else if (Back[jx][jy] != EL_EMPTY &&
13200 game.engine_version >= VERSION_IDENT(2,2,0,0))
13201 old_element = Back[jx][jy];
13203 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13204 return MP_NO_ACTION; /* field has no opening in this direction */
13206 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13207 return MP_NO_ACTION; /* field has no opening in this direction */
13209 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13213 Feld[jx][jy] = player->artwork_element;
13214 InitMovingField(jx, jy, MV_DOWN);
13215 Store[jx][jy] = EL_ACID;
13216 ContinueMoving(jx, jy);
13217 BuryPlayer(player);
13219 return MP_DONT_RUN_INTO;
13222 if (player_can_move && DONT_RUN_INTO(element))
13224 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13226 return MP_DONT_RUN_INTO;
13229 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13230 return MP_NO_ACTION;
13232 collect_count = element_info[element].collect_count_initial;
13234 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13235 return MP_NO_ACTION;
13237 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13238 player_can_move = player_can_move_or_snap;
13240 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13241 game.engine_version >= VERSION_IDENT(2,2,0,0))
13243 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13244 player->index_bit, dig_side);
13245 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13246 player->index_bit, dig_side);
13248 if (element == EL_DC_LANDMINE)
13251 if (Feld[x][y] != element) /* field changed by snapping */
13254 return MP_NO_ACTION;
13257 if (player->gravity && is_player && !player->is_auto_moving &&
13258 canFallDown(player) && move_direction != MV_DOWN &&
13259 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13260 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13262 if (player_can_move &&
13263 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13265 int sound_element = SND_ELEMENT(element);
13266 int sound_action = ACTION_WALKING;
13268 if (IS_RND_GATE(element))
13270 if (!player->key[RND_GATE_NR(element)])
13271 return MP_NO_ACTION;
13273 else if (IS_RND_GATE_GRAY(element))
13275 if (!player->key[RND_GATE_GRAY_NR(element)])
13276 return MP_NO_ACTION;
13278 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13280 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13281 return MP_NO_ACTION;
13283 else if (element == EL_EXIT_OPEN ||
13284 element == EL_EM_EXIT_OPEN ||
13285 element == EL_EM_EXIT_OPENING ||
13286 element == EL_STEEL_EXIT_OPEN ||
13287 element == EL_EM_STEEL_EXIT_OPEN ||
13288 element == EL_EM_STEEL_EXIT_OPENING ||
13289 element == EL_SP_EXIT_OPEN ||
13290 element == EL_SP_EXIT_OPENING)
13292 sound_action = ACTION_PASSING; /* player is passing exit */
13294 else if (element == EL_EMPTY)
13296 sound_action = ACTION_MOVING; /* nothing to walk on */
13299 /* play sound from background or player, whatever is available */
13300 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13301 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13303 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13305 else if (player_can_move &&
13306 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13308 if (!ACCESS_FROM(element, opposite_direction))
13309 return MP_NO_ACTION; /* field not accessible from this direction */
13311 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13312 return MP_NO_ACTION;
13314 if (IS_EM_GATE(element))
13316 if (!player->key[EM_GATE_NR(element)])
13317 return MP_NO_ACTION;
13319 else if (IS_EM_GATE_GRAY(element))
13321 if (!player->key[EM_GATE_GRAY_NR(element)])
13322 return MP_NO_ACTION;
13324 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13326 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13327 return MP_NO_ACTION;
13329 else if (IS_EMC_GATE(element))
13331 if (!player->key[EMC_GATE_NR(element)])
13332 return MP_NO_ACTION;
13334 else if (IS_EMC_GATE_GRAY(element))
13336 if (!player->key[EMC_GATE_GRAY_NR(element)])
13337 return MP_NO_ACTION;
13339 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13341 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13342 return MP_NO_ACTION;
13344 else if (element == EL_DC_GATE_WHITE ||
13345 element == EL_DC_GATE_WHITE_GRAY ||
13346 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13348 if (player->num_white_keys == 0)
13349 return MP_NO_ACTION;
13351 player->num_white_keys--;
13353 else if (IS_SP_PORT(element))
13355 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13356 element == EL_SP_GRAVITY_PORT_RIGHT ||
13357 element == EL_SP_GRAVITY_PORT_UP ||
13358 element == EL_SP_GRAVITY_PORT_DOWN)
13359 player->gravity = !player->gravity;
13360 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13361 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13362 element == EL_SP_GRAVITY_ON_PORT_UP ||
13363 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13364 player->gravity = TRUE;
13365 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13366 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13367 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13368 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13369 player->gravity = FALSE;
13372 /* automatically move to the next field with double speed */
13373 player->programmed_action = move_direction;
13375 if (player->move_delay_reset_counter == 0)
13377 player->move_delay_reset_counter = 2; /* two double speed steps */
13379 DOUBLE_PLAYER_SPEED(player);
13382 PlayLevelSoundAction(x, y, ACTION_PASSING);
13384 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13388 if (mode != DF_SNAP)
13390 GfxElement[x][y] = GFX_ELEMENT(element);
13391 player->is_digging = TRUE;
13394 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13396 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13397 player->index_bit, dig_side);
13399 if (mode == DF_SNAP)
13401 if (level.block_snap_field)
13402 setFieldForSnapping(x, y, element, move_direction);
13404 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13406 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13407 player->index_bit, dig_side);
13410 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13414 if (is_player && mode != DF_SNAP)
13416 GfxElement[x][y] = element;
13417 player->is_collecting = TRUE;
13420 if (element == EL_SPEED_PILL)
13422 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13424 else if (element == EL_EXTRA_TIME && level.time > 0)
13426 TimeLeft += level.extra_time;
13428 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13430 DisplayGameControlValues();
13432 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13434 player->shield_normal_time_left += level.shield_normal_time;
13435 if (element == EL_SHIELD_DEADLY)
13436 player->shield_deadly_time_left += level.shield_deadly_time;
13438 else if (element == EL_DYNAMITE ||
13439 element == EL_EM_DYNAMITE ||
13440 element == EL_SP_DISK_RED)
13442 if (player->inventory_size < MAX_INVENTORY_SIZE)
13443 player->inventory_element[player->inventory_size++] = element;
13445 DrawGameDoorValues();
13447 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13449 player->dynabomb_count++;
13450 player->dynabombs_left++;
13452 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13454 player->dynabomb_size++;
13456 else if (element == EL_DYNABOMB_INCREASE_POWER)
13458 player->dynabomb_xl = TRUE;
13460 else if (IS_KEY(element))
13462 player->key[KEY_NR(element)] = TRUE;
13464 DrawGameDoorValues();
13466 else if (element == EL_DC_KEY_WHITE)
13468 player->num_white_keys++;
13470 /* display white keys? */
13471 /* DrawGameDoorValues(); */
13473 else if (IS_ENVELOPE(element))
13475 player->show_envelope = element;
13477 else if (element == EL_EMC_LENSES)
13479 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13481 RedrawAllInvisibleElementsForLenses();
13483 else if (element == EL_EMC_MAGNIFIER)
13485 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13487 RedrawAllInvisibleElementsForMagnifier();
13489 else if (IS_DROPPABLE(element) ||
13490 IS_THROWABLE(element)) /* can be collected and dropped */
13494 if (collect_count == 0)
13495 player->inventory_infinite_element = element;
13497 for (i = 0; i < collect_count; i++)
13498 if (player->inventory_size < MAX_INVENTORY_SIZE)
13499 player->inventory_element[player->inventory_size++] = element;
13501 DrawGameDoorValues();
13503 else if (collect_count > 0)
13505 local_player->gems_still_needed -= collect_count;
13506 if (local_player->gems_still_needed < 0)
13507 local_player->gems_still_needed = 0;
13509 game.snapshot.collected_item = TRUE;
13511 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13513 DisplayGameControlValues();
13516 RaiseScoreElement(element);
13517 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13520 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13521 player->index_bit, dig_side);
13523 if (mode == DF_SNAP)
13525 if (level.block_snap_field)
13526 setFieldForSnapping(x, y, element, move_direction);
13528 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13530 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13531 player->index_bit, dig_side);
13534 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13536 if (mode == DF_SNAP && element != EL_BD_ROCK)
13537 return MP_NO_ACTION;
13539 if (CAN_FALL(element) && dy)
13540 return MP_NO_ACTION;
13542 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13543 !(element == EL_SPRING && level.use_spring_bug))
13544 return MP_NO_ACTION;
13546 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13547 ((move_direction & MV_VERTICAL &&
13548 ((element_info[element].move_pattern & MV_LEFT &&
13549 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13550 (element_info[element].move_pattern & MV_RIGHT &&
13551 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13552 (move_direction & MV_HORIZONTAL &&
13553 ((element_info[element].move_pattern & MV_UP &&
13554 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13555 (element_info[element].move_pattern & MV_DOWN &&
13556 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13557 return MP_NO_ACTION;
13559 /* do not push elements already moving away faster than player */
13560 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13561 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13562 return MP_NO_ACTION;
13564 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13566 if (player->push_delay_value == -1 || !player_was_pushing)
13567 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13569 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13571 if (player->push_delay_value == -1)
13572 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13574 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13576 if (!player->is_pushing)
13577 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13580 player->is_pushing = TRUE;
13581 player->is_active = TRUE;
13583 if (!(IN_LEV_FIELD(nextx, nexty) &&
13584 (IS_FREE(nextx, nexty) ||
13585 (IS_SB_ELEMENT(element) &&
13586 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13587 (IS_CUSTOM_ELEMENT(element) &&
13588 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13589 return MP_NO_ACTION;
13591 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13592 return MP_NO_ACTION;
13594 if (player->push_delay == -1) /* new pushing; restart delay */
13595 player->push_delay = 0;
13597 if (player->push_delay < player->push_delay_value &&
13598 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13599 element != EL_SPRING && element != EL_BALLOON)
13601 /* make sure that there is no move delay before next try to push */
13602 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13603 player->move_delay = 0;
13605 return MP_NO_ACTION;
13608 if (IS_CUSTOM_ELEMENT(element) &&
13609 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13611 if (!DigFieldByCE(nextx, nexty, element))
13612 return MP_NO_ACTION;
13615 if (IS_SB_ELEMENT(element))
13617 if (element == EL_SOKOBAN_FIELD_FULL)
13619 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13620 local_player->sokobanfields_still_needed++;
13623 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13625 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13626 local_player->sokobanfields_still_needed--;
13629 Feld[x][y] = EL_SOKOBAN_OBJECT;
13631 if (Back[x][y] == Back[nextx][nexty])
13632 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13633 else if (Back[x][y] != 0)
13634 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13637 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13640 if (local_player->sokobanfields_still_needed == 0 &&
13641 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13643 PlayerWins(player);
13645 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13649 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13651 InitMovingField(x, y, move_direction);
13652 GfxAction[x][y] = ACTION_PUSHING;
13654 if (mode == DF_SNAP)
13655 ContinueMoving(x, y);
13657 MovPos[x][y] = (dx != 0 ? dx : dy);
13659 Pushed[x][y] = TRUE;
13660 Pushed[nextx][nexty] = TRUE;
13662 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13663 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13665 player->push_delay_value = -1; /* get new value later */
13667 /* check for element change _after_ element has been pushed */
13668 if (game.use_change_when_pushing_bug)
13670 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13671 player->index_bit, dig_side);
13672 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13673 player->index_bit, dig_side);
13676 else if (IS_SWITCHABLE(element))
13678 if (PLAYER_SWITCHING(player, x, y))
13680 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13681 player->index_bit, dig_side);
13686 player->is_switching = TRUE;
13687 player->switch_x = x;
13688 player->switch_y = y;
13690 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13692 if (element == EL_ROBOT_WHEEL)
13694 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13698 game.robot_wheel_active = TRUE;
13700 TEST_DrawLevelField(x, y);
13702 else if (element == EL_SP_TERMINAL)
13706 SCAN_PLAYFIELD(xx, yy)
13708 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13712 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13714 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13716 ResetGfxAnimation(xx, yy);
13717 TEST_DrawLevelField(xx, yy);
13721 else if (IS_BELT_SWITCH(element))
13723 ToggleBeltSwitch(x, y);
13725 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13726 element == EL_SWITCHGATE_SWITCH_DOWN ||
13727 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13728 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13730 ToggleSwitchgateSwitch(x, y);
13732 else if (element == EL_LIGHT_SWITCH ||
13733 element == EL_LIGHT_SWITCH_ACTIVE)
13735 ToggleLightSwitch(x, y);
13737 else if (element == EL_TIMEGATE_SWITCH ||
13738 element == EL_DC_TIMEGATE_SWITCH)
13740 ActivateTimegateSwitch(x, y);
13742 else if (element == EL_BALLOON_SWITCH_LEFT ||
13743 element == EL_BALLOON_SWITCH_RIGHT ||
13744 element == EL_BALLOON_SWITCH_UP ||
13745 element == EL_BALLOON_SWITCH_DOWN ||
13746 element == EL_BALLOON_SWITCH_NONE ||
13747 element == EL_BALLOON_SWITCH_ANY)
13749 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13750 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13751 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13752 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13753 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13756 else if (element == EL_LAMP)
13758 Feld[x][y] = EL_LAMP_ACTIVE;
13759 local_player->lights_still_needed--;
13761 ResetGfxAnimation(x, y);
13762 TEST_DrawLevelField(x, y);
13764 else if (element == EL_TIME_ORB_FULL)
13766 Feld[x][y] = EL_TIME_ORB_EMPTY;
13768 if (level.time > 0 || level.use_time_orb_bug)
13770 TimeLeft += level.time_orb_time;
13771 game.no_time_limit = FALSE;
13773 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13775 DisplayGameControlValues();
13778 ResetGfxAnimation(x, y);
13779 TEST_DrawLevelField(x, y);
13781 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13782 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13786 game.ball_state = !game.ball_state;
13788 SCAN_PLAYFIELD(xx, yy)
13790 int e = Feld[xx][yy];
13792 if (game.ball_state)
13794 if (e == EL_EMC_MAGIC_BALL)
13795 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13796 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13797 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13801 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13802 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13803 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13804 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13809 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13810 player->index_bit, dig_side);
13812 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13813 player->index_bit, dig_side);
13815 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13816 player->index_bit, dig_side);
13822 if (!PLAYER_SWITCHING(player, x, y))
13824 player->is_switching = TRUE;
13825 player->switch_x = x;
13826 player->switch_y = y;
13828 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13829 player->index_bit, dig_side);
13830 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13831 player->index_bit, dig_side);
13833 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13834 player->index_bit, dig_side);
13835 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13836 player->index_bit, dig_side);
13839 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13840 player->index_bit, dig_side);
13841 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13842 player->index_bit, dig_side);
13844 return MP_NO_ACTION;
13847 player->push_delay = -1;
13849 if (is_player) /* function can also be called by EL_PENGUIN */
13851 if (Feld[x][y] != element) /* really digged/collected something */
13853 player->is_collecting = !player->is_digging;
13854 player->is_active = TRUE;
13861 static boolean DigFieldByCE(int x, int y, int digging_element)
13863 int element = Feld[x][y];
13865 if (!IS_FREE(x, y))
13867 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13868 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13871 /* no element can dig solid indestructible elements */
13872 if (IS_INDESTRUCTIBLE(element) &&
13873 !IS_DIGGABLE(element) &&
13874 !IS_COLLECTIBLE(element))
13877 if (AmoebaNr[x][y] &&
13878 (element == EL_AMOEBA_FULL ||
13879 element == EL_BD_AMOEBA ||
13880 element == EL_AMOEBA_GROWING))
13882 AmoebaCnt[AmoebaNr[x][y]]--;
13883 AmoebaCnt2[AmoebaNr[x][y]]--;
13886 if (IS_MOVING(x, y))
13887 RemoveMovingField(x, y);
13891 TEST_DrawLevelField(x, y);
13894 /* if digged element was about to explode, prevent the explosion */
13895 ExplodeField[x][y] = EX_TYPE_NONE;
13897 PlayLevelSoundAction(x, y, action);
13900 Store[x][y] = EL_EMPTY;
13902 /* this makes it possible to leave the removed element again */
13903 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13904 Store[x][y] = element;
13909 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13911 int jx = player->jx, jy = player->jy;
13912 int x = jx + dx, y = jy + dy;
13913 int snap_direction = (dx == -1 ? MV_LEFT :
13914 dx == +1 ? MV_RIGHT :
13916 dy == +1 ? MV_DOWN : MV_NONE);
13917 boolean can_continue_snapping = (level.continuous_snapping &&
13918 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13920 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13923 if (!player->active || !IN_LEV_FIELD(x, y))
13931 if (player->MovPos == 0)
13932 player->is_pushing = FALSE;
13934 player->is_snapping = FALSE;
13936 if (player->MovPos == 0)
13938 player->is_moving = FALSE;
13939 player->is_digging = FALSE;
13940 player->is_collecting = FALSE;
13946 /* prevent snapping with already pressed snap key when not allowed */
13947 if (player->is_snapping && !can_continue_snapping)
13950 player->MovDir = snap_direction;
13952 if (player->MovPos == 0)
13954 player->is_moving = FALSE;
13955 player->is_digging = FALSE;
13956 player->is_collecting = FALSE;
13959 player->is_dropping = FALSE;
13960 player->is_dropping_pressed = FALSE;
13961 player->drop_pressed_delay = 0;
13963 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13966 player->is_snapping = TRUE;
13967 player->is_active = TRUE;
13969 if (player->MovPos == 0)
13971 player->is_moving = FALSE;
13972 player->is_digging = FALSE;
13973 player->is_collecting = FALSE;
13976 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13977 TEST_DrawLevelField(player->last_jx, player->last_jy);
13979 TEST_DrawLevelField(x, y);
13984 static boolean DropElement(struct PlayerInfo *player)
13986 int old_element, new_element;
13987 int dropx = player->jx, dropy = player->jy;
13988 int drop_direction = player->MovDir;
13989 int drop_side = drop_direction;
13990 int drop_element = get_next_dropped_element(player);
13992 player->is_dropping_pressed = TRUE;
13994 /* do not drop an element on top of another element; when holding drop key
13995 pressed without moving, dropped element must move away before the next
13996 element can be dropped (this is especially important if the next element
13997 is dynamite, which can be placed on background for historical reasons) */
13998 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14001 if (IS_THROWABLE(drop_element))
14003 dropx += GET_DX_FROM_DIR(drop_direction);
14004 dropy += GET_DY_FROM_DIR(drop_direction);
14006 if (!IN_LEV_FIELD(dropx, dropy))
14010 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14011 new_element = drop_element; /* default: no change when dropping */
14013 /* check if player is active, not moving and ready to drop */
14014 if (!player->active || player->MovPos || player->drop_delay > 0)
14017 /* check if player has anything that can be dropped */
14018 if (new_element == EL_UNDEFINED)
14021 /* check if drop key was pressed long enough for EM style dynamite */
14022 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14025 /* check if anything can be dropped at the current position */
14026 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14029 /* collected custom elements can only be dropped on empty fields */
14030 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14033 if (old_element != EL_EMPTY)
14034 Back[dropx][dropy] = old_element; /* store old element on this field */
14036 ResetGfxAnimation(dropx, dropy);
14037 ResetRandomAnimationValue(dropx, dropy);
14039 if (player->inventory_size > 0 ||
14040 player->inventory_infinite_element != EL_UNDEFINED)
14042 if (player->inventory_size > 0)
14044 player->inventory_size--;
14046 DrawGameDoorValues();
14048 if (new_element == EL_DYNAMITE)
14049 new_element = EL_DYNAMITE_ACTIVE;
14050 else if (new_element == EL_EM_DYNAMITE)
14051 new_element = EL_EM_DYNAMITE_ACTIVE;
14052 else if (new_element == EL_SP_DISK_RED)
14053 new_element = EL_SP_DISK_RED_ACTIVE;
14056 Feld[dropx][dropy] = new_element;
14058 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14059 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14060 el2img(Feld[dropx][dropy]), 0);
14062 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14064 /* needed if previous element just changed to "empty" in the last frame */
14065 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14067 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14068 player->index_bit, drop_side);
14069 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14071 player->index_bit, drop_side);
14073 TestIfElementTouchesCustomElement(dropx, dropy);
14075 else /* player is dropping a dyna bomb */
14077 player->dynabombs_left--;
14079 Feld[dropx][dropy] = new_element;
14081 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14082 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14083 el2img(Feld[dropx][dropy]), 0);
14085 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14088 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14089 InitField_WithBug1(dropx, dropy, FALSE);
14091 new_element = Feld[dropx][dropy]; /* element might have changed */
14093 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14094 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14096 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14097 MovDir[dropx][dropy] = drop_direction;
14099 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14101 /* do not cause impact style collision by dropping elements that can fall */
14102 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14105 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14106 player->is_dropping = TRUE;
14108 player->drop_pressed_delay = 0;
14109 player->is_dropping_pressed = FALSE;
14111 player->drop_x = dropx;
14112 player->drop_y = dropy;
14117 /* ------------------------------------------------------------------------- */
14118 /* game sound playing functions */
14119 /* ------------------------------------------------------------------------- */
14121 static int *loop_sound_frame = NULL;
14122 static int *loop_sound_volume = NULL;
14124 void InitPlayLevelSound()
14126 int num_sounds = getSoundListSize();
14128 checked_free(loop_sound_frame);
14129 checked_free(loop_sound_volume);
14131 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14132 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14135 static void PlayLevelSound(int x, int y, int nr)
14137 int sx = SCREENX(x), sy = SCREENY(y);
14138 int volume, stereo_position;
14139 int max_distance = 8;
14140 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14142 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14143 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14146 if (!IN_LEV_FIELD(x, y) ||
14147 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14148 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14151 volume = SOUND_MAX_VOLUME;
14153 if (!IN_SCR_FIELD(sx, sy))
14155 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14156 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14158 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14161 stereo_position = (SOUND_MAX_LEFT +
14162 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14163 (SCR_FIELDX + 2 * max_distance));
14165 if (IS_LOOP_SOUND(nr))
14167 /* This assures that quieter loop sounds do not overwrite louder ones,
14168 while restarting sound volume comparison with each new game frame. */
14170 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14173 loop_sound_volume[nr] = volume;
14174 loop_sound_frame[nr] = FrameCounter;
14177 PlaySoundExt(nr, volume, stereo_position, type);
14180 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14182 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14183 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14184 y < LEVELY(BY1) ? LEVELY(BY1) :
14185 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14189 static void PlayLevelSoundAction(int x, int y, int action)
14191 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14194 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14196 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14198 if (sound_effect != SND_UNDEFINED)
14199 PlayLevelSound(x, y, sound_effect);
14202 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14205 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14207 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14208 PlayLevelSound(x, y, sound_effect);
14211 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14213 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14215 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14216 PlayLevelSound(x, y, sound_effect);
14219 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14221 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14223 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14224 StopSound(sound_effect);
14227 static void PlayLevelMusic()
14229 if (levelset.music[level_nr] != MUS_UNDEFINED)
14230 PlayMusic(levelset.music[level_nr]); /* from config file */
14232 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14235 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14237 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14238 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14239 int x = xx - 1 - offset;
14240 int y = yy - 1 - offset;
14245 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14249 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14253 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14257 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14261 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14265 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14269 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14272 case SAMPLE_android_clone:
14273 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14276 case SAMPLE_android_move:
14277 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14280 case SAMPLE_spring:
14281 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14285 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14289 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14292 case SAMPLE_eater_eat:
14293 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14297 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14300 case SAMPLE_collect:
14301 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14304 case SAMPLE_diamond:
14305 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14308 case SAMPLE_squash:
14309 /* !!! CHECK THIS !!! */
14311 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14313 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14317 case SAMPLE_wonderfall:
14318 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14322 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14326 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14330 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14334 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14338 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14342 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14345 case SAMPLE_wonder:
14346 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14350 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14353 case SAMPLE_exit_open:
14354 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14357 case SAMPLE_exit_leave:
14358 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14361 case SAMPLE_dynamite:
14362 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14366 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14370 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14374 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14378 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14382 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14386 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14390 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14395 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14397 int element = map_element_SP_to_RND(element_sp);
14398 int action = map_action_SP_to_RND(action_sp);
14399 int offset = (setup.sp_show_border_elements ? 0 : 1);
14400 int x = xx - offset;
14401 int y = yy - offset;
14403 PlayLevelSoundElementAction(x, y, element, action);
14406 void RaiseScore(int value)
14408 local_player->score += value;
14410 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14412 DisplayGameControlValues();
14415 void RaiseScoreElement(int element)
14420 case EL_BD_DIAMOND:
14421 case EL_EMERALD_YELLOW:
14422 case EL_EMERALD_RED:
14423 case EL_EMERALD_PURPLE:
14424 case EL_SP_INFOTRON:
14425 RaiseScore(level.score[SC_EMERALD]);
14428 RaiseScore(level.score[SC_DIAMOND]);
14431 RaiseScore(level.score[SC_CRYSTAL]);
14434 RaiseScore(level.score[SC_PEARL]);
14437 case EL_BD_BUTTERFLY:
14438 case EL_SP_ELECTRON:
14439 RaiseScore(level.score[SC_BUG]);
14442 case EL_BD_FIREFLY:
14443 case EL_SP_SNIKSNAK:
14444 RaiseScore(level.score[SC_SPACESHIP]);
14447 case EL_DARK_YAMYAM:
14448 RaiseScore(level.score[SC_YAMYAM]);
14451 RaiseScore(level.score[SC_ROBOT]);
14454 RaiseScore(level.score[SC_PACMAN]);
14457 RaiseScore(level.score[SC_NUT]);
14460 case EL_EM_DYNAMITE:
14461 case EL_SP_DISK_RED:
14462 case EL_DYNABOMB_INCREASE_NUMBER:
14463 case EL_DYNABOMB_INCREASE_SIZE:
14464 case EL_DYNABOMB_INCREASE_POWER:
14465 RaiseScore(level.score[SC_DYNAMITE]);
14467 case EL_SHIELD_NORMAL:
14468 case EL_SHIELD_DEADLY:
14469 RaiseScore(level.score[SC_SHIELD]);
14471 case EL_EXTRA_TIME:
14472 RaiseScore(level.extra_time_score);
14486 case EL_DC_KEY_WHITE:
14487 RaiseScore(level.score[SC_KEY]);
14490 RaiseScore(element_info[element].collect_score);
14495 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14497 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14499 /* closing door required in case of envelope style request dialogs */
14501 CloseDoor(DOOR_CLOSE_1);
14503 #if defined(NETWORK_AVALIABLE)
14504 if (options.network)
14505 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14510 FadeSkipNextFadeIn();
14512 SetGameStatus(GAME_MODE_MAIN);
14517 else /* continue playing the game */
14519 if (tape.playing && tape.deactivate_display)
14520 TapeDeactivateDisplayOff(TRUE);
14522 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14524 if (tape.playing && tape.deactivate_display)
14525 TapeDeactivateDisplayOn();
14529 void RequestQuitGame(boolean ask_if_really_quit)
14531 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14532 boolean skip_request = AllPlayersGone || quick_quit;
14534 RequestQuitGameExt(skip_request, quick_quit,
14535 "Do you really want to quit the game?");
14539 /* ------------------------------------------------------------------------- */
14540 /* random generator functions */
14541 /* ------------------------------------------------------------------------- */
14543 unsigned int InitEngineRandom_RND(int seed)
14545 game.num_random_calls = 0;
14547 return InitEngineRandom(seed);
14550 unsigned int RND(int max)
14554 game.num_random_calls++;
14556 return GetEngineRandom(max);
14563 /* ------------------------------------------------------------------------- */
14564 /* game engine snapshot handling functions */
14565 /* ------------------------------------------------------------------------- */
14567 struct EngineSnapshotInfo
14569 /* runtime values for custom element collect score */
14570 int collect_score[NUM_CUSTOM_ELEMENTS];
14572 /* runtime values for group element choice position */
14573 int choice_pos[NUM_GROUP_ELEMENTS];
14575 /* runtime values for belt position animations */
14576 int belt_graphic[4][NUM_BELT_PARTS];
14577 int belt_anim_mode[4][NUM_BELT_PARTS];
14580 static struct EngineSnapshotInfo engine_snapshot_rnd;
14581 static char *snapshot_level_identifier = NULL;
14582 static int snapshot_level_nr = -1;
14584 static void SaveEngineSnapshotValues_RND()
14586 static int belt_base_active_element[4] =
14588 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14589 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14590 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14591 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14595 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14597 int element = EL_CUSTOM_START + i;
14599 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14602 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14604 int element = EL_GROUP_START + i;
14606 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14609 for (i = 0; i < 4; i++)
14611 for (j = 0; j < NUM_BELT_PARTS; j++)
14613 int element = belt_base_active_element[i] + j;
14614 int graphic = el2img(element);
14615 int anim_mode = graphic_info[graphic].anim_mode;
14617 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14618 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14623 static void LoadEngineSnapshotValues_RND()
14625 unsigned int num_random_calls = game.num_random_calls;
14628 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14630 int element = EL_CUSTOM_START + i;
14632 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14635 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14637 int element = EL_GROUP_START + i;
14639 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14642 for (i = 0; i < 4; i++)
14644 for (j = 0; j < NUM_BELT_PARTS; j++)
14646 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14647 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14649 graphic_info[graphic].anim_mode = anim_mode;
14653 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14655 InitRND(tape.random_seed);
14656 for (i = 0; i < num_random_calls; i++)
14660 if (game.num_random_calls != num_random_calls)
14662 Error(ERR_INFO, "number of random calls out of sync");
14663 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14664 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14665 Error(ERR_EXIT, "this should not happen -- please debug");
14669 void FreeEngineSnapshotSingle()
14671 FreeSnapshotSingle();
14673 setString(&snapshot_level_identifier, NULL);
14674 snapshot_level_nr = -1;
14677 void FreeEngineSnapshotList()
14679 FreeSnapshotList();
14682 ListNode *SaveEngineSnapshotBuffers()
14684 ListNode *buffers = NULL;
14686 /* copy some special values to a structure better suited for the snapshot */
14688 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14689 SaveEngineSnapshotValues_RND();
14690 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14691 SaveEngineSnapshotValues_EM();
14692 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14693 SaveEngineSnapshotValues_SP(&buffers);
14695 /* save values stored in special snapshot structure */
14697 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14698 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14699 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14700 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14701 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14702 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14704 /* save further RND engine values */
14706 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14707 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14708 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14710 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14711 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14712 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14713 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14715 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14716 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14717 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14718 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14719 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14721 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14722 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14723 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14725 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14727 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14729 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14730 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14732 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14733 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14734 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14735 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14736 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14737 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14738 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14739 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14740 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14741 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14742 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14743 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14744 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14745 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14746 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14747 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14748 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14749 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14751 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14752 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14754 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14755 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14756 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14758 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14759 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14761 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14762 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14763 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14764 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14765 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14767 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14768 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14771 ListNode *node = engine_snapshot_list_rnd;
14774 while (node != NULL)
14776 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14781 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14787 void SaveEngineSnapshotSingle()
14789 ListNode *buffers = SaveEngineSnapshotBuffers();
14791 /* finally save all snapshot buffers to single snapshot */
14792 SaveSnapshotSingle(buffers);
14794 /* save level identification information */
14795 setString(&snapshot_level_identifier, leveldir_current->identifier);
14796 snapshot_level_nr = level_nr;
14799 boolean CheckSaveEngineSnapshotToList()
14801 boolean save_snapshot =
14802 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14803 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14804 game.snapshot.changed_action) ||
14805 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14806 game.snapshot.collected_item));
14808 game.snapshot.changed_action = FALSE;
14809 game.snapshot.collected_item = FALSE;
14810 game.snapshot.save_snapshot = save_snapshot;
14812 return save_snapshot;
14815 void SaveEngineSnapshotToList()
14817 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14821 ListNode *buffers = SaveEngineSnapshotBuffers();
14823 /* finally save all snapshot buffers to snapshot list */
14824 SaveSnapshotToList(buffers);
14827 void SaveEngineSnapshotToListInitial()
14829 FreeEngineSnapshotList();
14831 SaveEngineSnapshotToList();
14834 void LoadEngineSnapshotValues()
14836 /* restore special values from snapshot structure */
14838 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14839 LoadEngineSnapshotValues_RND();
14840 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14841 LoadEngineSnapshotValues_EM();
14842 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14843 LoadEngineSnapshotValues_SP();
14846 void LoadEngineSnapshotSingle()
14848 LoadSnapshotSingle();
14850 LoadEngineSnapshotValues();
14853 void LoadEngineSnapshot_Undo(int steps)
14855 LoadSnapshotFromList_Older(steps);
14857 LoadEngineSnapshotValues();
14860 void LoadEngineSnapshot_Redo(int steps)
14862 LoadSnapshotFromList_Newer(steps);
14864 LoadEngineSnapshotValues();
14867 boolean CheckEngineSnapshotSingle()
14869 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14870 snapshot_level_nr == level_nr);
14873 boolean CheckEngineSnapshotList()
14875 return CheckSnapshotList();
14879 /* ---------- new game button stuff ---------------------------------------- */
14887 } gamebutton_info[NUM_GAME_BUTTONS] =
14890 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
14891 GAME_CTRL_ID_STOP, "stop game"
14894 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
14895 GAME_CTRL_ID_PAUSE, "pause game"
14898 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
14899 GAME_CTRL_ID_PLAY, "play game"
14902 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
14903 GAME_CTRL_ID_UNDO, "undo step"
14906 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
14907 GAME_CTRL_ID_REDO, "redo step"
14910 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
14911 GAME_CTRL_ID_SAVE, "save game"
14914 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
14915 GAME_CTRL_ID_PAUSE2, "pause game"
14918 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
14919 GAME_CTRL_ID_LOAD, "load game"
14922 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
14923 SOUND_CTRL_ID_MUSIC, "background music on/off"
14926 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
14927 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14930 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
14931 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14935 void CreateGameButtons()
14939 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14941 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14942 struct XY *pos = gamebutton_info[i].pos;
14943 struct GadgetInfo *gi;
14946 unsigned int event_mask;
14947 int base_x = (tape.show_game_buttons ? VX : DX);
14948 int base_y = (tape.show_game_buttons ? VY : DY);
14949 int gd_x = gfx->src_x;
14950 int gd_y = gfx->src_y;
14951 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14952 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14953 int gd_xa = gfx->src_x + gfx->active_xoffset;
14954 int gd_ya = gfx->src_y + gfx->active_yoffset;
14955 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14956 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14959 if (gfx->bitmap == NULL)
14961 game_gadget[id] = NULL;
14966 if (id == GAME_CTRL_ID_STOP ||
14967 id == GAME_CTRL_ID_PLAY ||
14968 id == GAME_CTRL_ID_SAVE ||
14969 id == GAME_CTRL_ID_LOAD)
14971 button_type = GD_TYPE_NORMAL_BUTTON;
14973 event_mask = GD_EVENT_RELEASED;
14975 else if (id == GAME_CTRL_ID_UNDO ||
14976 id == GAME_CTRL_ID_REDO)
14978 button_type = GD_TYPE_NORMAL_BUTTON;
14980 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14984 button_type = GD_TYPE_CHECK_BUTTON;
14986 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14987 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14988 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14989 event_mask = GD_EVENT_PRESSED;
14992 gi = CreateGadget(GDI_CUSTOM_ID, id,
14993 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14994 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14995 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14996 GDI_WIDTH, gfx->width,
14997 GDI_HEIGHT, gfx->height,
14998 GDI_TYPE, button_type,
14999 GDI_STATE, GD_BUTTON_UNPRESSED,
15000 GDI_CHECKED, checked,
15001 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15002 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15003 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15004 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15005 GDI_DIRECT_DRAW, FALSE,
15006 GDI_EVENT_MASK, event_mask,
15007 GDI_CALLBACK_ACTION, HandleGameButtons,
15011 Error(ERR_EXIT, "cannot create gadget");
15013 game_gadget[id] = gi;
15017 void FreeGameButtons()
15021 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15022 FreeGadget(game_gadget[i]);
15025 static void UnmapGameButtonsAtSamePosition(int id)
15029 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15031 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15032 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15033 UnmapGadget(game_gadget[i]);
15036 static void UnmapGameButtonsAtSamePosition_All()
15038 if (setup.show_snapshot_buttons)
15040 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15041 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15042 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15046 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15047 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15048 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15052 static void MapGameButtonsAtSamePosition(int id)
15056 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15058 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15059 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15060 MapGadget(game_gadget[i]);
15062 UnmapGameButtonsAtSamePosition_All();
15065 void MapUndoRedoButtons()
15067 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15068 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15070 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15071 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15073 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15076 void UnmapUndoRedoButtons()
15078 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15079 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15081 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15082 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15084 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15087 void MapGameButtons()
15091 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15092 if (i != GAME_CTRL_ID_UNDO &&
15093 i != GAME_CTRL_ID_REDO)
15094 MapGadget(game_gadget[i]);
15096 UnmapGameButtonsAtSamePosition_All();
15098 RedrawGameButtons();
15101 void UnmapGameButtons()
15105 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15106 UnmapGadget(game_gadget[i]);
15109 void RedrawGameButtons()
15113 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15114 RedrawGadget(game_gadget[i]);
15116 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15117 redraw_mask &= ~REDRAW_ALL;
15120 void GameUndoRedoExt()
15122 ClearPlayerAction();
15124 tape.pausing = TRUE;
15127 UpdateAndDisplayGameControlValues();
15129 DrawCompleteVideoDisplay();
15130 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15131 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15132 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15137 void GameUndo(int steps)
15139 if (!CheckEngineSnapshotList())
15142 LoadEngineSnapshot_Undo(steps);
15147 void GameRedo(int steps)
15149 if (!CheckEngineSnapshotList())
15152 LoadEngineSnapshot_Redo(steps);
15157 static void HandleGameButtonsExt(int id, int button)
15159 static boolean game_undo_executed = FALSE;
15160 int steps = BUTTON_STEPSIZE(button);
15161 boolean handle_game_buttons =
15162 (game_status == GAME_MODE_PLAYING ||
15163 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15165 if (!handle_game_buttons)
15170 case GAME_CTRL_ID_STOP:
15171 if (game_status == GAME_MODE_MAIN)
15177 RequestQuitGame(TRUE);
15181 case GAME_CTRL_ID_PAUSE:
15182 case GAME_CTRL_ID_PAUSE2:
15183 if (options.network && game_status == GAME_MODE_PLAYING)
15185 #if defined(NETWORK_AVALIABLE)
15187 SendToServer_ContinuePlaying();
15189 SendToServer_PausePlaying();
15193 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15195 game_undo_executed = FALSE;
15199 case GAME_CTRL_ID_PLAY:
15200 if (game_status == GAME_MODE_MAIN)
15202 StartGameActions(options.network, setup.autorecord, level.random_seed);
15204 else if (tape.pausing)
15206 #if defined(NETWORK_AVALIABLE)
15207 if (options.network)
15208 SendToServer_ContinuePlaying();
15211 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15215 case GAME_CTRL_ID_UNDO:
15216 // Important: When using "save snapshot when collecting an item" mode,
15217 // load last (current) snapshot for first "undo" after pressing "pause"
15218 // (else the last-but-one snapshot would be loaded, because the snapshot
15219 // pointer already points to the last snapshot when pressing "pause",
15220 // which is fine for "every step/move" mode, but not for "every collect")
15221 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15222 !game_undo_executed)
15225 game_undo_executed = TRUE;
15230 case GAME_CTRL_ID_REDO:
15234 case GAME_CTRL_ID_SAVE:
15238 case GAME_CTRL_ID_LOAD:
15242 case SOUND_CTRL_ID_MUSIC:
15243 if (setup.sound_music)
15245 setup.sound_music = FALSE;
15249 else if (audio.music_available)
15251 setup.sound = setup.sound_music = TRUE;
15253 SetAudioMode(setup.sound);
15259 case SOUND_CTRL_ID_LOOPS:
15260 if (setup.sound_loops)
15261 setup.sound_loops = FALSE;
15262 else if (audio.loops_available)
15264 setup.sound = setup.sound_loops = TRUE;
15266 SetAudioMode(setup.sound);
15270 case SOUND_CTRL_ID_SIMPLE:
15271 if (setup.sound_simple)
15272 setup.sound_simple = FALSE;
15273 else if (audio.sound_available)
15275 setup.sound = setup.sound_simple = TRUE;
15277 SetAudioMode(setup.sound);
15286 static void HandleGameButtons(struct GadgetInfo *gi)
15288 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15291 void HandleSoundButtonKeys(Key key)
15294 if (key == setup.shortcut.sound_simple)
15295 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15296 else if (key == setup.shortcut.sound_loops)
15297 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15298 else if (key == setup.shortcut.sound_music)
15299 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);