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 void InitGameControlValues()
2057 for (i = 0; game_panel_controls[i].nr != -1; i++)
2059 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2060 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2061 struct TextPosInfo *pos = gpc->pos;
2063 int type = gpc->type;
2067 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2068 Error(ERR_EXIT, "this should not happen -- please debug");
2071 /* force update of game controls after initialization */
2072 gpc->value = gpc->last_value = -1;
2073 gpc->frame = gpc->last_frame = -1;
2074 gpc->gfx_frame = -1;
2076 /* determine panel value width for later calculation of alignment */
2077 if (type == TYPE_INTEGER || type == TYPE_STRING)
2079 pos->width = pos->size * getFontWidth(pos->font);
2080 pos->height = getFontHeight(pos->font);
2082 else if (type == TYPE_ELEMENT)
2084 pos->width = pos->size;
2085 pos->height = pos->size;
2088 /* fill structure for game panel draw order */
2090 gpo->sort_priority = pos->sort_priority;
2093 /* sort game panel controls according to sort_priority and control number */
2094 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2095 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2098 void UpdatePlayfieldElementCount()
2100 boolean use_element_count = FALSE;
2103 /* first check if it is needed at all to calculate playfield element count */
2104 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2105 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2106 use_element_count = TRUE;
2108 if (!use_element_count)
2111 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2112 element_info[i].element_count = 0;
2114 SCAN_PLAYFIELD(x, y)
2116 element_info[Feld[x][y]].element_count++;
2119 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2120 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2121 if (IS_IN_GROUP(j, i))
2122 element_info[EL_GROUP_START + i].element_count +=
2123 element_info[j].element_count;
2126 void UpdateGameControlValues()
2129 int time = (local_player->LevelSolved ?
2130 local_player->LevelSolved_CountingTime :
2131 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2132 level.native_em_level->lev->time :
2133 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2134 level.native_sp_level->game_sp->time_played :
2135 game.no_time_limit ? TimePlayed : TimeLeft);
2136 int score = (local_player->LevelSolved ?
2137 local_player->LevelSolved_CountingScore :
2138 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139 level.native_em_level->lev->score :
2140 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2141 level.native_sp_level->game_sp->score :
2142 local_player->score);
2143 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2144 level.native_em_level->lev->required :
2145 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2146 level.native_sp_level->game_sp->infotrons_still_needed :
2147 local_player->gems_still_needed);
2148 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149 level.native_em_level->lev->required > 0 :
2150 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2152 local_player->gems_still_needed > 0 ||
2153 local_player->sokobanfields_still_needed > 0 ||
2154 local_player->lights_still_needed > 0);
2156 UpdatePlayfieldElementCount();
2158 /* update game panel control values */
2160 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2161 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2163 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2164 for (i = 0; i < MAX_NUM_KEYS; i++)
2165 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2166 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2167 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2169 if (game.centered_player_nr == -1)
2171 for (i = 0; i < MAX_PLAYERS; i++)
2173 /* only one player in Supaplex game engine */
2174 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2177 for (k = 0; k < MAX_NUM_KEYS; k++)
2179 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2181 if (level.native_em_level->ply[i]->keys & (1 << k))
2182 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2183 get_key_element_from_nr(k);
2185 else if (stored_player[i].key[k])
2186 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2187 get_key_element_from_nr(k);
2190 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2191 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2192 level.native_em_level->ply[i]->dynamite;
2193 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2194 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2195 level.native_sp_level->game_sp->red_disk_count;
2197 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2198 stored_player[i].inventory_size;
2200 if (stored_player[i].num_white_keys > 0)
2201 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2204 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2205 stored_player[i].num_white_keys;
2210 int player_nr = game.centered_player_nr;
2212 for (k = 0; k < MAX_NUM_KEYS; k++)
2214 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2216 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2217 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2218 get_key_element_from_nr(k);
2220 else if (stored_player[player_nr].key[k])
2221 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2222 get_key_element_from_nr(k);
2225 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2227 level.native_em_level->ply[player_nr]->dynamite;
2228 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2229 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2230 level.native_sp_level->game_sp->red_disk_count;
2232 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2233 stored_player[player_nr].inventory_size;
2235 if (stored_player[player_nr].num_white_keys > 0)
2236 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2238 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2239 stored_player[player_nr].num_white_keys;
2242 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2244 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2245 get_inventory_element_from_pos(local_player, i);
2246 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2247 get_inventory_element_from_pos(local_player, -i - 1);
2250 game_panel_controls[GAME_PANEL_SCORE].value = score;
2251 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2253 game_panel_controls[GAME_PANEL_TIME].value = time;
2255 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2256 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2257 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2259 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2261 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2262 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2264 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2265 local_player->shield_normal_time_left;
2266 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2267 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2269 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2270 local_player->shield_deadly_time_left;
2272 game_panel_controls[GAME_PANEL_EXIT].value =
2273 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2275 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2276 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2277 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2278 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2279 EL_EMC_MAGIC_BALL_SWITCH);
2281 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2282 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2283 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2284 game.light_time_left;
2286 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2287 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2288 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2289 game.timegate_time_left;
2291 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2292 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2294 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2295 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2296 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2297 game.lenses_time_left;
2299 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2300 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2301 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2302 game.magnify_time_left;
2304 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2305 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2306 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2307 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2308 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2309 EL_BALLOON_SWITCH_NONE);
2311 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2312 local_player->dynabomb_count;
2313 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2314 local_player->dynabomb_size;
2315 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2316 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2318 game_panel_controls[GAME_PANEL_PENGUINS].value =
2319 local_player->friends_still_needed;
2321 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2322 local_player->sokobanfields_still_needed;
2323 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2324 local_player->sokobanfields_still_needed;
2326 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2327 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2329 for (i = 0; i < NUM_BELTS; i++)
2331 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2332 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2333 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2334 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2335 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2338 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2339 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2340 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2341 game.magic_wall_time_left;
2343 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2344 local_player->gravity;
2346 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2347 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2349 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2350 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2351 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2352 game.panel.element[i].id : EL_UNDEFINED);
2354 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2355 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2356 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2357 element_info[game.panel.element_count[i].id].element_count : 0);
2359 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2360 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2361 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2362 element_info[game.panel.ce_score[i].id].collect_score : 0);
2364 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2365 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2366 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2367 element_info[game.panel.ce_score_element[i].id].collect_score :
2370 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2371 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2372 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2374 /* update game panel control frames */
2376 for (i = 0; game_panel_controls[i].nr != -1; i++)
2378 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2380 if (gpc->type == TYPE_ELEMENT)
2382 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2384 int last_anim_random_frame = gfx.anim_random_frame;
2385 int element = gpc->value;
2386 int graphic = el2panelimg(element);
2388 if (gpc->value != gpc->last_value)
2391 gpc->gfx_random = INIT_GFX_RANDOM();
2397 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2398 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2399 gpc->gfx_random = INIT_GFX_RANDOM();
2402 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2403 gfx.anim_random_frame = gpc->gfx_random;
2405 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2406 gpc->gfx_frame = element_info[element].collect_score;
2408 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2411 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2412 gfx.anim_random_frame = last_anim_random_frame;
2418 void DisplayGameControlValues()
2420 boolean redraw_panel = FALSE;
2423 for (i = 0; game_panel_controls[i].nr != -1; i++)
2425 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2427 if (PANEL_DEACTIVATED(gpc->pos))
2430 if (gpc->value == gpc->last_value &&
2431 gpc->frame == gpc->last_frame)
2434 redraw_panel = TRUE;
2440 /* copy default game door content to main double buffer */
2442 /* !!! CHECK AGAIN !!! */
2443 SetPanelBackground();
2444 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2445 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2447 /* redraw game control buttons */
2448 RedrawGameButtons();
2450 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2452 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2454 int nr = game_panel_order[i].nr;
2455 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2456 struct TextPosInfo *pos = gpc->pos;
2457 int type = gpc->type;
2458 int value = gpc->value;
2459 int frame = gpc->frame;
2460 int size = pos->size;
2461 int font = pos->font;
2462 boolean draw_masked = pos->draw_masked;
2463 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2465 if (PANEL_DEACTIVATED(pos))
2468 gpc->last_value = value;
2469 gpc->last_frame = frame;
2471 if (type == TYPE_INTEGER)
2473 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2474 nr == GAME_PANEL_TIME)
2476 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2478 if (use_dynamic_size) /* use dynamic number of digits */
2480 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2481 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2482 int size2 = size1 + 1;
2483 int font1 = pos->font;
2484 int font2 = pos->font_alt;
2486 size = (value < value_change ? size1 : size2);
2487 font = (value < value_change ? font1 : font2);
2491 /* correct text size if "digits" is zero or less */
2493 size = strlen(int2str(value, size));
2495 /* dynamically correct text alignment */
2496 pos->width = size * getFontWidth(font);
2498 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2499 int2str(value, size), font, mask_mode);
2501 else if (type == TYPE_ELEMENT)
2503 int element, graphic;
2507 int dst_x = PANEL_XPOS(pos);
2508 int dst_y = PANEL_YPOS(pos);
2510 if (value != EL_UNDEFINED && value != EL_EMPTY)
2513 graphic = el2panelimg(value);
2515 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2517 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2520 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2523 width = graphic_info[graphic].width * size / TILESIZE;
2524 height = graphic_info[graphic].height * size / TILESIZE;
2527 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2530 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2534 else if (type == TYPE_STRING)
2536 boolean active = (value != 0);
2537 char *state_normal = "off";
2538 char *state_active = "on";
2539 char *state = (active ? state_active : state_normal);
2540 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2541 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2542 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2543 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2545 if (nr == GAME_PANEL_GRAVITY_STATE)
2547 int font1 = pos->font; /* (used for normal state) */
2548 int font2 = pos->font_alt; /* (used for active state) */
2550 font = (active ? font2 : font1);
2559 /* don't truncate output if "chars" is zero or less */
2562 /* dynamically correct text alignment */
2563 pos->width = size * getFontWidth(font);
2566 s_cut = getStringCopyN(s, size);
2568 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2569 s_cut, font, mask_mode);
2575 redraw_mask |= REDRAW_DOOR_1;
2578 SetGameStatus(GAME_MODE_PLAYING);
2581 void UpdateAndDisplayGameControlValues()
2583 if (tape.deactivate_display)
2586 UpdateGameControlValues();
2587 DisplayGameControlValues();
2590 void UpdateGameDoorValues()
2592 UpdateGameControlValues();
2595 void DrawGameDoorValues()
2597 DisplayGameControlValues();
2602 =============================================================================
2604 -----------------------------------------------------------------------------
2605 initialize game engine due to level / tape version number
2606 =============================================================================
2609 static void InitGameEngine()
2611 int i, j, k, l, x, y;
2613 /* set game engine from tape file when re-playing, else from level file */
2614 game.engine_version = (tape.playing ? tape.engine_version :
2615 level.game_version);
2617 /* set single or multi-player game mode (needed for re-playing tapes) */
2618 game.team_mode = setup.team_mode;
2622 int num_players = 0;
2624 for (i = 0; i < MAX_PLAYERS; i++)
2625 if (tape.player_participates[i])
2628 /* multi-player tapes contain input data for more than one player */
2629 game.team_mode = (num_players > 1);
2632 /* ---------------------------------------------------------------------- */
2633 /* set flags for bugs and changes according to active game engine version */
2634 /* ---------------------------------------------------------------------- */
2637 Summary of bugfix/change:
2638 Fixed handling for custom elements that change when pushed by the player.
2640 Fixed/changed in version:
2644 Before 3.1.0, custom elements that "change when pushing" changed directly
2645 after the player started pushing them (until then handled in "DigField()").
2646 Since 3.1.0, these custom elements are not changed until the "pushing"
2647 move of the element is finished (now handled in "ContinueMoving()").
2649 Affected levels/tapes:
2650 The first condition is generally needed for all levels/tapes before version
2651 3.1.0, which might use the old behaviour before it was changed; known tapes
2652 that are affected are some tapes from the level set "Walpurgis Gardens" by
2654 The second condition is an exception from the above case and is needed for
2655 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2656 above (including some development versions of 3.1.0), but before it was
2657 known that this change would break tapes like the above and was fixed in
2658 3.1.1, so that the changed behaviour was active although the engine version
2659 while recording maybe was before 3.1.0. There is at least one tape that is
2660 affected by this exception, which is the tape for the one-level set "Bug
2661 Machine" by Juergen Bonhagen.
2664 game.use_change_when_pushing_bug =
2665 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2667 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2668 tape.game_version < VERSION_IDENT(3,1,1,0)));
2671 Summary of bugfix/change:
2672 Fixed handling for blocking the field the player leaves when moving.
2674 Fixed/changed in version:
2678 Before 3.1.1, when "block last field when moving" was enabled, the field
2679 the player is leaving when moving was blocked for the time of the move,
2680 and was directly unblocked afterwards. This resulted in the last field
2681 being blocked for exactly one less than the number of frames of one player
2682 move. Additionally, even when blocking was disabled, the last field was
2683 blocked for exactly one frame.
2684 Since 3.1.1, due to changes in player movement handling, the last field
2685 is not blocked at all when blocking is disabled. When blocking is enabled,
2686 the last field is blocked for exactly the number of frames of one player
2687 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2688 last field is blocked for exactly one more than the number of frames of
2691 Affected levels/tapes:
2692 (!!! yet to be determined -- probably many !!!)
2695 game.use_block_last_field_bug =
2696 (game.engine_version < VERSION_IDENT(3,1,1,0));
2698 /* ---------------------------------------------------------------------- */
2700 /* set maximal allowed number of custom element changes per game frame */
2701 game.max_num_changes_per_frame = 1;
2703 /* default scan direction: scan playfield from top/left to bottom/right */
2704 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2706 /* dynamically adjust element properties according to game engine version */
2707 InitElementPropertiesEngine(game.engine_version);
2710 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2711 printf(" tape version == %06d [%s] [file: %06d]\n",
2712 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2714 printf(" => game.engine_version == %06d\n", game.engine_version);
2717 /* ---------- initialize player's initial move delay --------------------- */
2719 /* dynamically adjust player properties according to level information */
2720 for (i = 0; i < MAX_PLAYERS; i++)
2721 game.initial_move_delay_value[i] =
2722 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2724 /* dynamically adjust player properties according to game engine version */
2725 for (i = 0; i < MAX_PLAYERS; i++)
2726 game.initial_move_delay[i] =
2727 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2728 game.initial_move_delay_value[i] : 0);
2730 /* ---------- initialize player's initial push delay --------------------- */
2732 /* dynamically adjust player properties according to game engine version */
2733 game.initial_push_delay_value =
2734 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2736 /* ---------- initialize changing elements ------------------------------- */
2738 /* initialize changing elements information */
2739 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2741 struct ElementInfo *ei = &element_info[i];
2743 /* this pointer might have been changed in the level editor */
2744 ei->change = &ei->change_page[0];
2746 if (!IS_CUSTOM_ELEMENT(i))
2748 ei->change->target_element = EL_EMPTY_SPACE;
2749 ei->change->delay_fixed = 0;
2750 ei->change->delay_random = 0;
2751 ei->change->delay_frames = 1;
2754 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2756 ei->has_change_event[j] = FALSE;
2758 ei->event_page_nr[j] = 0;
2759 ei->event_page[j] = &ei->change_page[0];
2763 /* add changing elements from pre-defined list */
2764 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2766 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2767 struct ElementInfo *ei = &element_info[ch_delay->element];
2769 ei->change->target_element = ch_delay->target_element;
2770 ei->change->delay_fixed = ch_delay->change_delay;
2772 ei->change->pre_change_function = ch_delay->pre_change_function;
2773 ei->change->change_function = ch_delay->change_function;
2774 ei->change->post_change_function = ch_delay->post_change_function;
2776 ei->change->can_change = TRUE;
2777 ei->change->can_change_or_has_action = TRUE;
2779 ei->has_change_event[CE_DELAY] = TRUE;
2781 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2782 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2785 /* ---------- initialize internal run-time variables --------------------- */
2787 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2789 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2791 for (j = 0; j < ei->num_change_pages; j++)
2793 ei->change_page[j].can_change_or_has_action =
2794 (ei->change_page[j].can_change |
2795 ei->change_page[j].has_action);
2799 /* add change events from custom element configuration */
2800 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2802 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2804 for (j = 0; j < ei->num_change_pages; j++)
2806 if (!ei->change_page[j].can_change_or_has_action)
2809 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2811 /* only add event page for the first page found with this event */
2812 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2814 ei->has_change_event[k] = TRUE;
2816 ei->event_page_nr[k] = j;
2817 ei->event_page[k] = &ei->change_page[j];
2823 /* ---------- initialize reference elements in change conditions --------- */
2825 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2827 int element = EL_CUSTOM_START + i;
2828 struct ElementInfo *ei = &element_info[element];
2830 for (j = 0; j < ei->num_change_pages; j++)
2832 int trigger_element = ei->change_page[j].initial_trigger_element;
2834 if (trigger_element >= EL_PREV_CE_8 &&
2835 trigger_element <= EL_NEXT_CE_8)
2836 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2838 ei->change_page[j].trigger_element = trigger_element;
2842 /* ---------- initialize run-time trigger player and element ------------- */
2844 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2846 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2848 for (j = 0; j < ei->num_change_pages; j++)
2850 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2851 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2852 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2853 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2854 ei->change_page[j].actual_trigger_ce_value = 0;
2855 ei->change_page[j].actual_trigger_ce_score = 0;
2859 /* ---------- initialize trigger events ---------------------------------- */
2861 /* initialize trigger events information */
2862 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2863 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2864 trigger_events[i][j] = FALSE;
2866 /* add trigger events from element change event properties */
2867 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2869 struct ElementInfo *ei = &element_info[i];
2871 for (j = 0; j < ei->num_change_pages; j++)
2873 if (!ei->change_page[j].can_change_or_has_action)
2876 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2878 int trigger_element = ei->change_page[j].trigger_element;
2880 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2882 if (ei->change_page[j].has_event[k])
2884 if (IS_GROUP_ELEMENT(trigger_element))
2886 struct ElementGroupInfo *group =
2887 element_info[trigger_element].group;
2889 for (l = 0; l < group->num_elements_resolved; l++)
2890 trigger_events[group->element_resolved[l]][k] = TRUE;
2892 else if (trigger_element == EL_ANY_ELEMENT)
2893 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2894 trigger_events[l][k] = TRUE;
2896 trigger_events[trigger_element][k] = TRUE;
2903 /* ---------- initialize push delay -------------------------------------- */
2905 /* initialize push delay values to default */
2906 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2908 if (!IS_CUSTOM_ELEMENT(i))
2910 /* set default push delay values (corrected since version 3.0.7-1) */
2911 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2913 element_info[i].push_delay_fixed = 2;
2914 element_info[i].push_delay_random = 8;
2918 element_info[i].push_delay_fixed = 8;
2919 element_info[i].push_delay_random = 8;
2924 /* set push delay value for certain elements from pre-defined list */
2925 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2927 int e = push_delay_list[i].element;
2929 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2930 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2933 /* set push delay value for Supaplex elements for newer engine versions */
2934 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2936 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2938 if (IS_SP_ELEMENT(i))
2940 /* set SP push delay to just enough to push under a falling zonk */
2941 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2943 element_info[i].push_delay_fixed = delay;
2944 element_info[i].push_delay_random = 0;
2949 /* ---------- initialize move stepsize ----------------------------------- */
2951 /* initialize move stepsize values to default */
2952 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2953 if (!IS_CUSTOM_ELEMENT(i))
2954 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2956 /* set move stepsize value for certain elements from pre-defined list */
2957 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2959 int e = move_stepsize_list[i].element;
2961 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2964 /* ---------- initialize collect score ----------------------------------- */
2966 /* initialize collect score values for custom elements from initial value */
2967 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2968 if (IS_CUSTOM_ELEMENT(i))
2969 element_info[i].collect_score = element_info[i].collect_score_initial;
2971 /* ---------- initialize collect count ----------------------------------- */
2973 /* initialize collect count values for non-custom elements */
2974 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2975 if (!IS_CUSTOM_ELEMENT(i))
2976 element_info[i].collect_count_initial = 0;
2978 /* add collect count values for all elements from pre-defined list */
2979 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2980 element_info[collect_count_list[i].element].collect_count_initial =
2981 collect_count_list[i].count;
2983 /* ---------- initialize access direction -------------------------------- */
2985 /* initialize access direction values to default (access from every side) */
2986 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2987 if (!IS_CUSTOM_ELEMENT(i))
2988 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2990 /* set access direction value for certain elements from pre-defined list */
2991 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2992 element_info[access_direction_list[i].element].access_direction =
2993 access_direction_list[i].direction;
2995 /* ---------- initialize explosion content ------------------------------- */
2996 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2998 if (IS_CUSTOM_ELEMENT(i))
3001 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3003 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3005 element_info[i].content.e[x][y] =
3006 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3007 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3008 i == EL_PLAYER_3 ? EL_EMERALD :
3009 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3010 i == EL_MOLE ? EL_EMERALD_RED :
3011 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3012 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3013 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3014 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3015 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3016 i == EL_WALL_EMERALD ? EL_EMERALD :
3017 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3018 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3019 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3020 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3021 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3022 i == EL_WALL_PEARL ? EL_PEARL :
3023 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3028 /* ---------- initialize recursion detection ------------------------------ */
3029 recursion_loop_depth = 0;
3030 recursion_loop_detected = FALSE;
3031 recursion_loop_element = EL_UNDEFINED;
3033 /* ---------- initialize graphics engine ---------------------------------- */
3034 game.scroll_delay_value =
3035 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3036 setup.scroll_delay ? setup.scroll_delay_value : 0);
3037 game.scroll_delay_value =
3038 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3040 /* ---------- initialize game engine snapshots ---------------------------- */
3041 for (i = 0; i < MAX_PLAYERS; i++)
3042 game.snapshot.last_action[i] = 0;
3043 game.snapshot.changed_action = FALSE;
3044 game.snapshot.collected_item = FALSE;
3045 game.snapshot.mode =
3046 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3047 SNAPSHOT_MODE_EVERY_STEP :
3048 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3049 SNAPSHOT_MODE_EVERY_MOVE :
3050 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3051 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3052 game.snapshot.save_snapshot = FALSE;
3055 int get_num_special_action(int element, int action_first, int action_last)
3057 int num_special_action = 0;
3060 for (i = action_first; i <= action_last; i++)
3062 boolean found = FALSE;
3064 for (j = 0; j < NUM_DIRECTIONS; j++)
3065 if (el_act_dir2img(element, i, j) !=
3066 el_act_dir2img(element, ACTION_DEFAULT, j))
3070 num_special_action++;
3075 return num_special_action;
3080 =============================================================================
3082 -----------------------------------------------------------------------------
3083 initialize and start new game
3084 =============================================================================
3089 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3090 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3091 int fade_mask = REDRAW_FIELD;
3093 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3094 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3095 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3096 int initial_move_dir = MV_DOWN;
3099 // required here to update video display before fading (FIX THIS)
3100 DrawMaskedBorder(REDRAW_DOOR_2);
3102 if (!game.restart_level)
3103 CloseDoor(DOOR_CLOSE_1);
3105 SetGameStatus(GAME_MODE_PLAYING);
3107 if (level_editor_test_game)
3108 FadeSkipNextFadeIn();
3110 FadeSetEnterScreen();
3112 if (CheckIfGlobalBorderHasChanged())
3113 fade_mask = REDRAW_ALL;
3115 FadeSoundsAndMusic();
3117 ExpireSoundLoops(TRUE);
3121 /* needed if different viewport properties defined for playing */
3122 ChangeViewportPropertiesIfNeeded();
3126 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3128 DrawCompleteVideoDisplay();
3131 InitGameControlValues();
3133 /* don't play tapes over network */
3134 network_playing = (options.network && !tape.playing);
3136 for (i = 0; i < MAX_PLAYERS; i++)
3138 struct PlayerInfo *player = &stored_player[i];
3140 player->index_nr = i;
3141 player->index_bit = (1 << i);
3142 player->element_nr = EL_PLAYER_1 + i;
3144 player->present = FALSE;
3145 player->active = FALSE;
3146 player->mapped = FALSE;
3148 player->killed = FALSE;
3149 player->reanimated = FALSE;
3152 player->effective_action = 0;
3153 player->programmed_action = 0;
3156 player->score_final = 0;
3158 player->gems_still_needed = level.gems_needed;
3159 player->sokobanfields_still_needed = 0;
3160 player->lights_still_needed = 0;
3161 player->friends_still_needed = 0;
3163 for (j = 0; j < MAX_NUM_KEYS; j++)
3164 player->key[j] = FALSE;
3166 player->num_white_keys = 0;
3168 player->dynabomb_count = 0;
3169 player->dynabomb_size = 1;
3170 player->dynabombs_left = 0;
3171 player->dynabomb_xl = FALSE;
3173 player->MovDir = initial_move_dir;
3176 player->GfxDir = initial_move_dir;
3177 player->GfxAction = ACTION_DEFAULT;
3179 player->StepFrame = 0;
3181 player->initial_element = player->element_nr;
3182 player->artwork_element =
3183 (level.use_artwork_element[i] ? level.artwork_element[i] :
3184 player->element_nr);
3185 player->use_murphy = FALSE;
3187 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3188 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3190 player->gravity = level.initial_player_gravity[i];
3192 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3194 player->actual_frame_counter = 0;
3196 player->step_counter = 0;
3198 player->last_move_dir = initial_move_dir;
3200 player->is_active = FALSE;
3202 player->is_waiting = FALSE;
3203 player->is_moving = FALSE;
3204 player->is_auto_moving = FALSE;
3205 player->is_digging = FALSE;
3206 player->is_snapping = FALSE;
3207 player->is_collecting = FALSE;
3208 player->is_pushing = FALSE;
3209 player->is_switching = FALSE;
3210 player->is_dropping = FALSE;
3211 player->is_dropping_pressed = FALSE;
3213 player->is_bored = FALSE;
3214 player->is_sleeping = FALSE;
3216 player->was_waiting = TRUE;
3217 player->was_moving = FALSE;
3218 player->was_snapping = FALSE;
3219 player->was_dropping = FALSE;
3221 player->frame_counter_bored = -1;
3222 player->frame_counter_sleeping = -1;
3224 player->anim_delay_counter = 0;
3225 player->post_delay_counter = 0;
3227 player->dir_waiting = initial_move_dir;
3228 player->action_waiting = ACTION_DEFAULT;
3229 player->last_action_waiting = ACTION_DEFAULT;
3230 player->special_action_bored = ACTION_DEFAULT;
3231 player->special_action_sleeping = ACTION_DEFAULT;
3233 player->switch_x = -1;
3234 player->switch_y = -1;
3236 player->drop_x = -1;
3237 player->drop_y = -1;
3239 player->show_envelope = 0;
3241 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3243 player->push_delay = -1; /* initialized when pushing starts */
3244 player->push_delay_value = game.initial_push_delay_value;
3246 player->drop_delay = 0;
3247 player->drop_pressed_delay = 0;
3249 player->last_jx = -1;
3250 player->last_jy = -1;
3254 player->shield_normal_time_left = 0;
3255 player->shield_deadly_time_left = 0;
3257 player->inventory_infinite_element = EL_UNDEFINED;
3258 player->inventory_size = 0;
3260 if (level.use_initial_inventory[i])
3262 for (j = 0; j < level.initial_inventory_size[i]; j++)
3264 int element = level.initial_inventory_content[i][j];
3265 int collect_count = element_info[element].collect_count_initial;
3268 if (!IS_CUSTOM_ELEMENT(element))
3271 if (collect_count == 0)
3272 player->inventory_infinite_element = element;
3274 for (k = 0; k < collect_count; k++)
3275 if (player->inventory_size < MAX_INVENTORY_SIZE)
3276 player->inventory_element[player->inventory_size++] = element;
3280 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3281 SnapField(player, 0, 0);
3283 player->LevelSolved = FALSE;
3284 player->GameOver = FALSE;
3286 player->LevelSolved_GameWon = FALSE;
3287 player->LevelSolved_GameEnd = FALSE;
3288 player->LevelSolved_PanelOff = FALSE;
3289 player->LevelSolved_SaveTape = FALSE;
3290 player->LevelSolved_SaveScore = FALSE;
3291 player->LevelSolved_CountingTime = 0;
3292 player->LevelSolved_CountingScore = 0;
3294 map_player_action[i] = i;
3297 network_player_action_received = FALSE;
3299 #if defined(NETWORK_AVALIABLE)
3300 /* initial null action */
3301 if (network_playing)
3302 SendToServer_MovePlayer(MV_NONE);
3311 TimeLeft = level.time;
3314 ScreenMovDir = MV_NONE;
3318 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3320 AllPlayersGone = FALSE;
3322 game.no_time_limit = (level.time == 0);
3324 game.yamyam_content_nr = 0;
3325 game.robot_wheel_active = FALSE;
3326 game.magic_wall_active = FALSE;
3327 game.magic_wall_time_left = 0;
3328 game.light_time_left = 0;
3329 game.timegate_time_left = 0;
3330 game.switchgate_pos = 0;
3331 game.wind_direction = level.wind_direction_initial;
3333 game.lenses_time_left = 0;
3334 game.magnify_time_left = 0;
3336 game.ball_state = level.ball_state_initial;
3337 game.ball_content_nr = 0;
3339 game.envelope_active = FALSE;
3341 /* set focus to local player for network games, else to all players */
3342 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3343 game.centered_player_nr_next = game.centered_player_nr;
3344 game.set_centered_player = FALSE;
3346 if (network_playing && tape.recording)
3348 /* store client dependent player focus when recording network games */
3349 tape.centered_player_nr_next = game.centered_player_nr_next;
3350 tape.set_centered_player = TRUE;
3353 for (i = 0; i < NUM_BELTS; i++)
3355 game.belt_dir[i] = MV_NONE;
3356 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3359 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3360 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3362 #if DEBUG_INIT_PLAYER
3365 printf("Player status at level initialization:\n");
3369 SCAN_PLAYFIELD(x, y)
3371 Feld[x][y] = level.field[x][y];
3372 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3373 ChangeDelay[x][y] = 0;
3374 ChangePage[x][y] = -1;
3375 CustomValue[x][y] = 0; /* initialized in InitField() */
3376 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3378 WasJustMoving[x][y] = 0;
3379 WasJustFalling[x][y] = 0;
3380 CheckCollision[x][y] = 0;
3381 CheckImpact[x][y] = 0;
3383 Pushed[x][y] = FALSE;
3385 ChangeCount[x][y] = 0;
3386 ChangeEvent[x][y] = -1;
3388 ExplodePhase[x][y] = 0;
3389 ExplodeDelay[x][y] = 0;
3390 ExplodeField[x][y] = EX_TYPE_NONE;
3392 RunnerVisit[x][y] = 0;
3393 PlayerVisit[x][y] = 0;
3396 GfxRandom[x][y] = INIT_GFX_RANDOM();
3397 GfxElement[x][y] = EL_UNDEFINED;
3398 GfxAction[x][y] = ACTION_DEFAULT;
3399 GfxDir[x][y] = MV_NONE;
3400 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3403 SCAN_PLAYFIELD(x, y)
3405 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3407 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3409 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3412 InitField(x, y, TRUE);
3414 ResetGfxAnimation(x, y);
3419 for (i = 0; i < MAX_PLAYERS; i++)
3421 struct PlayerInfo *player = &stored_player[i];
3423 /* set number of special actions for bored and sleeping animation */
3424 player->num_special_action_bored =
3425 get_num_special_action(player->artwork_element,
3426 ACTION_BORING_1, ACTION_BORING_LAST);
3427 player->num_special_action_sleeping =
3428 get_num_special_action(player->artwork_element,
3429 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3432 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3433 emulate_sb ? EMU_SOKOBAN :
3434 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3436 /* initialize type of slippery elements */
3437 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3439 if (!IS_CUSTOM_ELEMENT(i))
3441 /* default: elements slip down either to the left or right randomly */
3442 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3444 /* SP style elements prefer to slip down on the left side */
3445 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3446 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3448 /* BD style elements prefer to slip down on the left side */
3449 if (game.emulation == EMU_BOULDERDASH)
3450 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3454 /* initialize explosion and ignition delay */
3455 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3457 if (!IS_CUSTOM_ELEMENT(i))
3460 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3461 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3462 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3463 int last_phase = (num_phase + 1) * delay;
3464 int half_phase = (num_phase / 2) * delay;
3466 element_info[i].explosion_delay = last_phase - 1;
3467 element_info[i].ignition_delay = half_phase;
3469 if (i == EL_BLACK_ORB)
3470 element_info[i].ignition_delay = 1;
3474 /* correct non-moving belts to start moving left */
3475 for (i = 0; i < NUM_BELTS; i++)
3476 if (game.belt_dir[i] == MV_NONE)
3477 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3479 #if USE_NEW_PLAYER_ASSIGNMENTS
3480 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3481 /* choose default local player */
3482 local_player = &stored_player[0];
3484 for (i = 0; i < MAX_PLAYERS; i++)
3485 stored_player[i].connected = FALSE;
3487 local_player->connected = TRUE;
3488 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3492 for (i = 0; i < MAX_PLAYERS; i++)
3493 stored_player[i].connected = tape.player_participates[i];
3495 else if (game.team_mode && !options.network)
3497 /* try to guess locally connected team mode players (needed for correct
3498 assignment of player figures from level to locally playing players) */
3500 for (i = 0; i < MAX_PLAYERS; i++)
3501 if (setup.input[i].use_joystick ||
3502 setup.input[i].key.left != KSYM_UNDEFINED)
3503 stored_player[i].connected = TRUE;
3506 #if DEBUG_INIT_PLAYER
3509 printf("Player status after level initialization:\n");
3511 for (i = 0; i < MAX_PLAYERS; i++)
3513 struct PlayerInfo *player = &stored_player[i];
3515 printf("- player %d: present == %d, connected == %d, active == %d",
3521 if (local_player == player)
3522 printf(" (local player)");
3529 #if DEBUG_INIT_PLAYER
3531 printf("Reassigning players ...\n");
3534 /* check if any connected player was not found in playfield */
3535 for (i = 0; i < MAX_PLAYERS; i++)
3537 struct PlayerInfo *player = &stored_player[i];
3539 if (player->connected && !player->present)
3541 struct PlayerInfo *field_player = NULL;
3543 #if DEBUG_INIT_PLAYER
3545 printf("- looking for field player for player %d ...\n", i + 1);
3548 /* assign first free player found that is present in the playfield */
3550 /* first try: look for unmapped playfield player that is not connected */
3551 for (j = 0; j < MAX_PLAYERS; j++)
3552 if (field_player == NULL &&
3553 stored_player[j].present &&
3554 !stored_player[j].mapped &&
3555 !stored_player[j].connected)
3556 field_player = &stored_player[j];
3558 /* second try: look for *any* unmapped playfield player */
3559 for (j = 0; j < MAX_PLAYERS; j++)
3560 if (field_player == NULL &&
3561 stored_player[j].present &&
3562 !stored_player[j].mapped)
3563 field_player = &stored_player[j];
3565 if (field_player != NULL)
3567 int jx = field_player->jx, jy = field_player->jy;
3569 #if DEBUG_INIT_PLAYER
3571 printf("- found player %d\n", field_player->index_nr + 1);
3574 player->present = FALSE;
3575 player->active = FALSE;
3577 field_player->present = TRUE;
3578 field_player->active = TRUE;
3581 player->initial_element = field_player->initial_element;
3582 player->artwork_element = field_player->artwork_element;
3584 player->block_last_field = field_player->block_last_field;
3585 player->block_delay_adjustment = field_player->block_delay_adjustment;
3588 StorePlayer[jx][jy] = field_player->element_nr;
3590 field_player->jx = field_player->last_jx = jx;
3591 field_player->jy = field_player->last_jy = jy;
3593 if (local_player == player)
3594 local_player = field_player;
3596 map_player_action[field_player->index_nr] = i;
3598 field_player->mapped = TRUE;
3600 #if DEBUG_INIT_PLAYER
3602 printf("- map_player_action[%d] == %d\n",
3603 field_player->index_nr + 1, i + 1);
3608 if (player->connected && player->present)
3609 player->mapped = TRUE;
3612 #if DEBUG_INIT_PLAYER
3615 printf("Player status after player assignment (first stage):\n");
3617 for (i = 0; i < MAX_PLAYERS; i++)
3619 struct PlayerInfo *player = &stored_player[i];
3621 printf("- player %d: present == %d, connected == %d, active == %d",
3627 if (local_player == player)
3628 printf(" (local player)");
3637 /* check if any connected player was not found in playfield */
3638 for (i = 0; i < MAX_PLAYERS; i++)
3640 struct PlayerInfo *player = &stored_player[i];
3642 if (player->connected && !player->present)
3644 for (j = 0; j < MAX_PLAYERS; j++)
3646 struct PlayerInfo *field_player = &stored_player[j];
3647 int jx = field_player->jx, jy = field_player->jy;
3649 /* assign first free player found that is present in the playfield */
3650 if (field_player->present && !field_player->connected)
3652 player->present = TRUE;
3653 player->active = TRUE;
3655 field_player->present = FALSE;
3656 field_player->active = FALSE;
3658 player->initial_element = field_player->initial_element;
3659 player->artwork_element = field_player->artwork_element;
3661 player->block_last_field = field_player->block_last_field;
3662 player->block_delay_adjustment = field_player->block_delay_adjustment;
3664 StorePlayer[jx][jy] = player->element_nr;
3666 player->jx = player->last_jx = jx;
3667 player->jy = player->last_jy = jy;
3677 printf("::: local_player->present == %d\n", local_player->present);
3682 /* when playing a tape, eliminate all players who do not participate */
3684 #if USE_NEW_PLAYER_ASSIGNMENTS
3686 if (!game.team_mode)
3688 for (i = 0; i < MAX_PLAYERS; i++)
3690 if (stored_player[i].active &&
3691 !tape.player_participates[map_player_action[i]])
3693 struct PlayerInfo *player = &stored_player[i];
3694 int jx = player->jx, jy = player->jy;
3696 #if DEBUG_INIT_PLAYER
3698 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3701 player->active = FALSE;
3702 StorePlayer[jx][jy] = 0;
3703 Feld[jx][jy] = EL_EMPTY;
3710 for (i = 0; i < MAX_PLAYERS; i++)
3712 if (stored_player[i].active &&
3713 !tape.player_participates[i])
3715 struct PlayerInfo *player = &stored_player[i];
3716 int jx = player->jx, jy = player->jy;
3718 player->active = FALSE;
3719 StorePlayer[jx][jy] = 0;
3720 Feld[jx][jy] = EL_EMPTY;
3725 else if (!options.network && !game.team_mode) /* && !tape.playing */
3727 /* when in single player mode, eliminate all but the first active player */
3729 for (i = 0; i < MAX_PLAYERS; i++)
3731 if (stored_player[i].active)
3733 for (j = i + 1; j < MAX_PLAYERS; j++)
3735 if (stored_player[j].active)
3737 struct PlayerInfo *player = &stored_player[j];
3738 int jx = player->jx, jy = player->jy;
3740 player->active = FALSE;
3741 player->present = FALSE;
3743 StorePlayer[jx][jy] = 0;
3744 Feld[jx][jy] = EL_EMPTY;
3751 /* when recording the game, store which players take part in the game */
3754 #if USE_NEW_PLAYER_ASSIGNMENTS
3755 for (i = 0; i < MAX_PLAYERS; i++)
3756 if (stored_player[i].connected)
3757 tape.player_participates[i] = TRUE;
3759 for (i = 0; i < MAX_PLAYERS; i++)
3760 if (stored_player[i].active)
3761 tape.player_participates[i] = TRUE;
3765 #if DEBUG_INIT_PLAYER
3768 printf("Player status after player assignment (final stage):\n");
3770 for (i = 0; i < MAX_PLAYERS; i++)
3772 struct PlayerInfo *player = &stored_player[i];
3774 printf("- player %d: present == %d, connected == %d, active == %d",
3780 if (local_player == player)
3781 printf(" (local player)");
3788 if (BorderElement == EL_EMPTY)
3791 SBX_Right = lev_fieldx - SCR_FIELDX;
3793 SBY_Lower = lev_fieldy - SCR_FIELDY;
3798 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3800 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3803 if (full_lev_fieldx <= SCR_FIELDX)
3804 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3805 if (full_lev_fieldy <= SCR_FIELDY)
3806 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3808 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3810 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3813 /* if local player not found, look for custom element that might create
3814 the player (make some assumptions about the right custom element) */
3815 if (!local_player->present)
3817 int start_x = 0, start_y = 0;
3818 int found_rating = 0;
3819 int found_element = EL_UNDEFINED;
3820 int player_nr = local_player->index_nr;
3822 SCAN_PLAYFIELD(x, y)
3824 int element = Feld[x][y];
3829 if (level.use_start_element[player_nr] &&
3830 level.start_element[player_nr] == element &&
3837 found_element = element;
3840 if (!IS_CUSTOM_ELEMENT(element))
3843 if (CAN_CHANGE(element))
3845 for (i = 0; i < element_info[element].num_change_pages; i++)
3847 /* check for player created from custom element as single target */
3848 content = element_info[element].change_page[i].target_element;
3849 is_player = ELEM_IS_PLAYER(content);
3851 if (is_player && (found_rating < 3 ||
3852 (found_rating == 3 && element < found_element)))
3858 found_element = element;
3863 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3865 /* check for player created from custom element as explosion content */
3866 content = element_info[element].content.e[xx][yy];
3867 is_player = ELEM_IS_PLAYER(content);
3869 if (is_player && (found_rating < 2 ||
3870 (found_rating == 2 && element < found_element)))
3872 start_x = x + xx - 1;
3873 start_y = y + yy - 1;
3876 found_element = element;
3879 if (!CAN_CHANGE(element))
3882 for (i = 0; i < element_info[element].num_change_pages; i++)
3884 /* check for player created from custom element as extended target */
3886 element_info[element].change_page[i].target_content.e[xx][yy];
3888 is_player = ELEM_IS_PLAYER(content);
3890 if (is_player && (found_rating < 1 ||
3891 (found_rating == 1 && element < found_element)))
3893 start_x = x + xx - 1;
3894 start_y = y + yy - 1;
3897 found_element = element;
3903 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3904 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3907 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3908 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3913 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3914 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3915 local_player->jx - MIDPOSX);
3917 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3918 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3919 local_player->jy - MIDPOSY);
3922 /* !!! FIX THIS (START) !!! */
3923 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3925 InitGameEngine_EM();
3927 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3929 InitGameEngine_SP();
3933 DrawLevel(REDRAW_FIELD);
3936 /* after drawing the level, correct some elements */
3937 if (game.timegate_time_left == 0)
3938 CloseAllOpenTimegates();
3941 /* blit playfield from scroll buffer to normal back buffer for fading in */
3942 BlitScreenToBitmap(backbuffer);
3943 /* !!! FIX THIS (END) !!! */
3945 DrawMaskedBorder(fade_mask);
3950 // full screen redraw is required at this point in the following cases:
3951 // - special editor door undrawn when game was started from level editor
3952 // - drawing area (playfield) was changed and has to be removed completely
3953 redraw_mask = REDRAW_ALL;
3957 if (!game.restart_level)
3959 /* copy default game door content to main double buffer */
3961 /* !!! CHECK AGAIN !!! */
3962 SetPanelBackground();
3963 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3964 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3967 SetPanelBackground();
3968 SetDrawBackgroundMask(REDRAW_DOOR_1);
3970 UpdateAndDisplayGameControlValues();
3972 if (!game.restart_level)
3978 CreateGameButtons();
3980 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3981 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3982 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3987 /* copy actual game door content to door double buffer for OpenDoor() */
3988 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3990 OpenDoor(DOOR_OPEN_ALL);
3992 PlaySound(SND_GAME_STARTING);
3994 if (setup.sound_music)
3997 KeyboardAutoRepeatOffUnlessAutoplay();
3999 #if DEBUG_INIT_PLAYER
4002 printf("Player status (final):\n");
4004 for (i = 0; i < MAX_PLAYERS; i++)
4006 struct PlayerInfo *player = &stored_player[i];
4008 printf("- player %d: present == %d, connected == %d, active == %d",
4014 if (local_player == player)
4015 printf(" (local player)");
4028 if (!game.restart_level && !tape.playing)
4030 LevelStats_incPlayed(level_nr);
4032 SaveLevelSetup_SeriesInfo();
4035 game.restart_level = FALSE;
4037 SaveEngineSnapshotToListInitial();
4040 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4041 int actual_player_x, int actual_player_y)
4043 /* this is used for non-R'n'D game engines to update certain engine values */
4045 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4047 actual_player_x = correctLevelPosX_EM(actual_player_x);
4048 actual_player_y = correctLevelPosY_EM(actual_player_y);
4051 /* needed to determine if sounds are played within the visible screen area */
4052 scroll_x = actual_scroll_x;
4053 scroll_y = actual_scroll_y;
4055 /* needed to get player position for "follow finger" playing input method */
4056 local_player->jx = actual_player_x;
4057 local_player->jy = actual_player_y;
4060 void InitMovDir(int x, int y)
4062 int i, element = Feld[x][y];
4063 static int xy[4][2] =
4070 static int direction[3][4] =
4072 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4073 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4074 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4083 Feld[x][y] = EL_BUG;
4084 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4087 case EL_SPACESHIP_RIGHT:
4088 case EL_SPACESHIP_UP:
4089 case EL_SPACESHIP_LEFT:
4090 case EL_SPACESHIP_DOWN:
4091 Feld[x][y] = EL_SPACESHIP;
4092 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4095 case EL_BD_BUTTERFLY_RIGHT:
4096 case EL_BD_BUTTERFLY_UP:
4097 case EL_BD_BUTTERFLY_LEFT:
4098 case EL_BD_BUTTERFLY_DOWN:
4099 Feld[x][y] = EL_BD_BUTTERFLY;
4100 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4103 case EL_BD_FIREFLY_RIGHT:
4104 case EL_BD_FIREFLY_UP:
4105 case EL_BD_FIREFLY_LEFT:
4106 case EL_BD_FIREFLY_DOWN:
4107 Feld[x][y] = EL_BD_FIREFLY;
4108 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4111 case EL_PACMAN_RIGHT:
4113 case EL_PACMAN_LEFT:
4114 case EL_PACMAN_DOWN:
4115 Feld[x][y] = EL_PACMAN;
4116 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4119 case EL_YAMYAM_LEFT:
4120 case EL_YAMYAM_RIGHT:
4122 case EL_YAMYAM_DOWN:
4123 Feld[x][y] = EL_YAMYAM;
4124 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4127 case EL_SP_SNIKSNAK:
4128 MovDir[x][y] = MV_UP;
4131 case EL_SP_ELECTRON:
4132 MovDir[x][y] = MV_LEFT;
4139 Feld[x][y] = EL_MOLE;
4140 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4144 if (IS_CUSTOM_ELEMENT(element))
4146 struct ElementInfo *ei = &element_info[element];
4147 int move_direction_initial = ei->move_direction_initial;
4148 int move_pattern = ei->move_pattern;
4150 if (move_direction_initial == MV_START_PREVIOUS)
4152 if (MovDir[x][y] != MV_NONE)
4155 move_direction_initial = MV_START_AUTOMATIC;
4158 if (move_direction_initial == MV_START_RANDOM)
4159 MovDir[x][y] = 1 << RND(4);
4160 else if (move_direction_initial & MV_ANY_DIRECTION)
4161 MovDir[x][y] = move_direction_initial;
4162 else if (move_pattern == MV_ALL_DIRECTIONS ||
4163 move_pattern == MV_TURNING_LEFT ||
4164 move_pattern == MV_TURNING_RIGHT ||
4165 move_pattern == MV_TURNING_LEFT_RIGHT ||
4166 move_pattern == MV_TURNING_RIGHT_LEFT ||
4167 move_pattern == MV_TURNING_RANDOM)
4168 MovDir[x][y] = 1 << RND(4);
4169 else if (move_pattern == MV_HORIZONTAL)
4170 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4171 else if (move_pattern == MV_VERTICAL)
4172 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4173 else if (move_pattern & MV_ANY_DIRECTION)
4174 MovDir[x][y] = element_info[element].move_pattern;
4175 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4176 move_pattern == MV_ALONG_RIGHT_SIDE)
4178 /* use random direction as default start direction */
4179 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4180 MovDir[x][y] = 1 << RND(4);
4182 for (i = 0; i < NUM_DIRECTIONS; i++)
4184 int x1 = x + xy[i][0];
4185 int y1 = y + xy[i][1];
4187 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4189 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4190 MovDir[x][y] = direction[0][i];
4192 MovDir[x][y] = direction[1][i];
4201 MovDir[x][y] = 1 << RND(4);
4203 if (element != EL_BUG &&
4204 element != EL_SPACESHIP &&
4205 element != EL_BD_BUTTERFLY &&
4206 element != EL_BD_FIREFLY)
4209 for (i = 0; i < NUM_DIRECTIONS; i++)
4211 int x1 = x + xy[i][0];
4212 int y1 = y + xy[i][1];
4214 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4216 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4218 MovDir[x][y] = direction[0][i];
4221 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4222 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4224 MovDir[x][y] = direction[1][i];
4233 GfxDir[x][y] = MovDir[x][y];
4236 void InitAmoebaNr(int x, int y)
4239 int group_nr = AmoebeNachbarNr(x, y);
4243 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4245 if (AmoebaCnt[i] == 0)
4253 AmoebaNr[x][y] = group_nr;
4254 AmoebaCnt[group_nr]++;
4255 AmoebaCnt2[group_nr]++;
4258 static void PlayerWins(struct PlayerInfo *player)
4260 player->LevelSolved = TRUE;
4261 player->GameOver = TRUE;
4263 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4264 level.native_em_level->lev->score : player->score);
4266 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4268 player->LevelSolved_CountingScore = player->score_final;
4273 static int time, time_final;
4274 static int score, score_final;
4275 static int game_over_delay_1 = 0;
4276 static int game_over_delay_2 = 0;
4277 int game_over_delay_value_1 = 50;
4278 int game_over_delay_value_2 = 50;
4280 if (!local_player->LevelSolved_GameWon)
4284 /* do not start end game actions before the player stops moving (to exit) */
4285 if (local_player->MovPos)
4288 local_player->LevelSolved_GameWon = TRUE;
4289 local_player->LevelSolved_SaveTape = tape.recording;
4290 local_player->LevelSolved_SaveScore = !tape.playing;
4294 LevelStats_incSolved(level_nr);
4296 SaveLevelSetup_SeriesInfo();
4299 if (tape.auto_play) /* tape might already be stopped here */
4300 tape.auto_play_level_solved = TRUE;
4304 game_over_delay_1 = game_over_delay_value_1;
4305 game_over_delay_2 = game_over_delay_value_2;
4307 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4308 score = score_final = local_player->score_final;
4313 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4315 else if (game.no_time_limit && TimePlayed < 999)
4318 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4321 local_player->score_final = score_final;
4323 if (level_editor_test_game)
4326 score = score_final;
4328 local_player->LevelSolved_CountingTime = time;
4329 local_player->LevelSolved_CountingScore = score;
4331 game_panel_controls[GAME_PANEL_TIME].value = time;
4332 game_panel_controls[GAME_PANEL_SCORE].value = score;
4334 DisplayGameControlValues();
4337 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4339 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4341 /* close exit door after last player */
4342 if ((AllPlayersGone &&
4343 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4344 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4345 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4346 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4347 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4349 int element = Feld[ExitX][ExitY];
4351 Feld[ExitX][ExitY] =
4352 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4353 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4354 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4355 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4356 EL_EM_STEEL_EXIT_CLOSING);
4358 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4361 /* player disappears */
4362 DrawLevelField(ExitX, ExitY);
4365 for (i = 0; i < MAX_PLAYERS; i++)
4367 struct PlayerInfo *player = &stored_player[i];
4369 if (player->present)
4371 RemovePlayer(player);
4373 /* player disappears */
4374 DrawLevelField(player->jx, player->jy);
4379 PlaySound(SND_GAME_WINNING);
4382 if (game_over_delay_1 > 0)
4384 game_over_delay_1--;
4389 if (time != time_final)
4391 int time_to_go = ABS(time_final - time);
4392 int time_count_dir = (time < time_final ? +1 : -1);
4393 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4395 time += time_count_steps * time_count_dir;
4396 score += time_count_steps * level.score[SC_TIME_BONUS];
4398 local_player->LevelSolved_CountingTime = time;
4399 local_player->LevelSolved_CountingScore = score;
4401 game_panel_controls[GAME_PANEL_TIME].value = time;
4402 game_panel_controls[GAME_PANEL_SCORE].value = score;
4404 DisplayGameControlValues();
4406 if (time == time_final)
4407 StopSound(SND_GAME_LEVELTIME_BONUS);
4408 else if (setup.sound_loops)
4409 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4411 PlaySound(SND_GAME_LEVELTIME_BONUS);
4416 local_player->LevelSolved_PanelOff = TRUE;
4418 if (game_over_delay_2 > 0)
4420 game_over_delay_2--;
4431 boolean raise_level = FALSE;
4433 local_player->LevelSolved_GameEnd = TRUE;
4435 if (!global.use_envelope_request)
4436 CloseDoor(DOOR_CLOSE_1);
4438 if (local_player->LevelSolved_SaveTape)
4440 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4443 CloseDoor(DOOR_CLOSE_ALL);
4445 if (level_editor_test_game)
4447 SetGameStatus(GAME_MODE_MAIN);
4454 if (!local_player->LevelSolved_SaveScore)
4456 SetGameStatus(GAME_MODE_MAIN);
4463 if (level_nr == leveldir_current->handicap_level)
4465 leveldir_current->handicap_level++;
4467 SaveLevelSetup_SeriesInfo();
4470 if (level_nr < leveldir_current->last_level)
4471 raise_level = TRUE; /* advance to next level */
4473 if ((hi_pos = NewHiScore()) >= 0)
4475 SetGameStatus(GAME_MODE_SCORES);
4477 DrawHallOfFame(hi_pos);
4487 SetGameStatus(GAME_MODE_MAIN);
4504 LoadScore(level_nr);
4506 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4507 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4510 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4512 if (local_player->score_final > highscore[k].Score)
4514 /* player has made it to the hall of fame */
4516 if (k < MAX_SCORE_ENTRIES - 1)
4518 int m = MAX_SCORE_ENTRIES - 1;
4521 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4522 if (strEqual(setup.player_name, highscore[l].Name))
4524 if (m == k) /* player's new highscore overwrites his old one */
4528 for (l = m; l > k; l--)
4530 strcpy(highscore[l].Name, highscore[l - 1].Name);
4531 highscore[l].Score = highscore[l - 1].Score;
4538 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4539 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4540 highscore[k].Score = local_player->score_final;
4546 else if (!strncmp(setup.player_name, highscore[k].Name,
4547 MAX_PLAYER_NAME_LEN))
4548 break; /* player already there with a higher score */
4554 SaveScore(level_nr);
4559 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4561 int element = Feld[x][y];
4562 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4563 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4564 int horiz_move = (dx != 0);
4565 int sign = (horiz_move ? dx : dy);
4566 int step = sign * element_info[element].move_stepsize;
4568 /* special values for move stepsize for spring and things on conveyor belt */
4571 if (CAN_FALL(element) &&
4572 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4573 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4574 else if (element == EL_SPRING)
4575 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4581 inline static int getElementMoveStepsize(int x, int y)
4583 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4586 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4588 if (player->GfxAction != action || player->GfxDir != dir)
4590 player->GfxAction = action;
4591 player->GfxDir = dir;
4593 player->StepFrame = 0;
4597 static void ResetGfxFrame(int x, int y, boolean redraw)
4599 int element = Feld[x][y];
4600 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4601 int last_gfx_frame = GfxFrame[x][y];
4603 if (graphic_info[graphic].anim_global_sync)
4604 GfxFrame[x][y] = FrameCounter;
4605 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4606 GfxFrame[x][y] = CustomValue[x][y];
4607 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4608 GfxFrame[x][y] = element_info[element].collect_score;
4609 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4610 GfxFrame[x][y] = ChangeDelay[x][y];
4612 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4613 DrawLevelGraphicAnimation(x, y, graphic);
4616 static void ResetGfxAnimation(int x, int y)
4618 GfxAction[x][y] = ACTION_DEFAULT;
4619 GfxDir[x][y] = MovDir[x][y];
4622 ResetGfxFrame(x, y, FALSE);
4625 static void ResetRandomAnimationValue(int x, int y)
4627 GfxRandom[x][y] = INIT_GFX_RANDOM();
4630 void InitMovingField(int x, int y, int direction)
4632 int element = Feld[x][y];
4633 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4634 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4637 boolean is_moving_before, is_moving_after;
4639 /* check if element was/is moving or being moved before/after mode change */
4640 is_moving_before = (WasJustMoving[x][y] != 0);
4641 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4643 /* reset animation only for moving elements which change direction of moving
4644 or which just started or stopped moving
4645 (else CEs with property "can move" / "not moving" are reset each frame) */
4646 if (is_moving_before != is_moving_after ||
4647 direction != MovDir[x][y])
4648 ResetGfxAnimation(x, y);
4650 MovDir[x][y] = direction;
4651 GfxDir[x][y] = direction;
4653 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4654 direction == MV_DOWN && CAN_FALL(element) ?
4655 ACTION_FALLING : ACTION_MOVING);
4657 /* this is needed for CEs with property "can move" / "not moving" */
4659 if (is_moving_after)
4661 if (Feld[newx][newy] == EL_EMPTY)
4662 Feld[newx][newy] = EL_BLOCKED;
4664 MovDir[newx][newy] = MovDir[x][y];
4666 CustomValue[newx][newy] = CustomValue[x][y];
4668 GfxFrame[newx][newy] = GfxFrame[x][y];
4669 GfxRandom[newx][newy] = GfxRandom[x][y];
4670 GfxAction[newx][newy] = GfxAction[x][y];
4671 GfxDir[newx][newy] = GfxDir[x][y];
4675 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4677 int direction = MovDir[x][y];
4678 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4679 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4685 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4687 int oldx = x, oldy = y;
4688 int direction = MovDir[x][y];
4690 if (direction == MV_LEFT)
4692 else if (direction == MV_RIGHT)
4694 else if (direction == MV_UP)
4696 else if (direction == MV_DOWN)
4699 *comes_from_x = oldx;
4700 *comes_from_y = oldy;
4703 int MovingOrBlocked2Element(int x, int y)
4705 int element = Feld[x][y];
4707 if (element == EL_BLOCKED)
4711 Blocked2Moving(x, y, &oldx, &oldy);
4712 return Feld[oldx][oldy];
4718 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4720 /* like MovingOrBlocked2Element(), but if element is moving
4721 and (x,y) is the field the moving element is just leaving,
4722 return EL_BLOCKED instead of the element value */
4723 int element = Feld[x][y];
4725 if (IS_MOVING(x, y))
4727 if (element == EL_BLOCKED)
4731 Blocked2Moving(x, y, &oldx, &oldy);
4732 return Feld[oldx][oldy];
4741 static void RemoveField(int x, int y)
4743 Feld[x][y] = EL_EMPTY;
4749 CustomValue[x][y] = 0;
4752 ChangeDelay[x][y] = 0;
4753 ChangePage[x][y] = -1;
4754 Pushed[x][y] = FALSE;
4756 GfxElement[x][y] = EL_UNDEFINED;
4757 GfxAction[x][y] = ACTION_DEFAULT;
4758 GfxDir[x][y] = MV_NONE;
4761 void RemoveMovingField(int x, int y)
4763 int oldx = x, oldy = y, newx = x, newy = y;
4764 int element = Feld[x][y];
4765 int next_element = EL_UNDEFINED;
4767 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4770 if (IS_MOVING(x, y))
4772 Moving2Blocked(x, y, &newx, &newy);
4774 if (Feld[newx][newy] != EL_BLOCKED)
4776 /* element is moving, but target field is not free (blocked), but
4777 already occupied by something different (example: acid pool);
4778 in this case, only remove the moving field, but not the target */
4780 RemoveField(oldx, oldy);
4782 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4784 TEST_DrawLevelField(oldx, oldy);
4789 else if (element == EL_BLOCKED)
4791 Blocked2Moving(x, y, &oldx, &oldy);
4792 if (!IS_MOVING(oldx, oldy))
4796 if (element == EL_BLOCKED &&
4797 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4798 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4799 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4800 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4801 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4802 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4803 next_element = get_next_element(Feld[oldx][oldy]);
4805 RemoveField(oldx, oldy);
4806 RemoveField(newx, newy);
4808 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4810 if (next_element != EL_UNDEFINED)
4811 Feld[oldx][oldy] = next_element;
4813 TEST_DrawLevelField(oldx, oldy);
4814 TEST_DrawLevelField(newx, newy);
4817 void DrawDynamite(int x, int y)
4819 int sx = SCREENX(x), sy = SCREENY(y);
4820 int graphic = el2img(Feld[x][y]);
4823 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4826 if (IS_WALKABLE_INSIDE(Back[x][y]))
4830 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4831 else if (Store[x][y])
4832 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4834 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4836 if (Back[x][y] || Store[x][y])
4837 DrawGraphicThruMask(sx, sy, graphic, frame);
4839 DrawGraphic(sx, sy, graphic, frame);
4842 void CheckDynamite(int x, int y)
4844 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4848 if (MovDelay[x][y] != 0)
4851 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4857 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4862 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4864 boolean num_checked_players = 0;
4867 for (i = 0; i < MAX_PLAYERS; i++)
4869 if (stored_player[i].active)
4871 int sx = stored_player[i].jx;
4872 int sy = stored_player[i].jy;
4874 if (num_checked_players == 0)
4881 *sx1 = MIN(*sx1, sx);
4882 *sy1 = MIN(*sy1, sy);
4883 *sx2 = MAX(*sx2, sx);
4884 *sy2 = MAX(*sy2, sy);
4887 num_checked_players++;
4892 static boolean checkIfAllPlayersFitToScreen_RND()
4894 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4896 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4898 return (sx2 - sx1 < SCR_FIELDX &&
4899 sy2 - sy1 < SCR_FIELDY);
4902 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4904 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4906 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4908 *sx = (sx1 + sx2) / 2;
4909 *sy = (sy1 + sy2) / 2;
4912 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4913 boolean center_screen, boolean quick_relocation)
4915 unsigned int frame_delay_value_old = GetVideoFrameDelay();
4916 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4917 boolean no_delay = (tape.warp_forward);
4918 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4919 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4920 int new_scroll_x, new_scroll_y;
4922 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4924 /* case 1: quick relocation inside visible screen (without scrolling) */
4931 if (!level.shifted_relocation || center_screen)
4933 /* relocation _with_ centering of screen */
4935 new_scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4936 x > SBX_Right + MIDPOSX ? SBX_Right :
4939 new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4940 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4945 /* relocation _without_ centering of screen */
4947 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4948 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4951 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4952 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4955 int offset_x = x + (scroll_x - center_scroll_x);
4956 int offset_y = y + (scroll_y - center_scroll_y);
4958 new_scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4959 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4960 offset_x - MIDPOSX);
4962 new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4963 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4964 offset_y - MIDPOSY);
4967 if (quick_relocation)
4969 /* case 2: quick relocation (redraw without visible scrolling) */
4971 scroll_x = new_scroll_x;
4972 scroll_y = new_scroll_y;
4979 /* case 3: visible relocation (with scrolling to new position) */
4981 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4983 SetVideoFrameDelay(wait_delay_value);
4985 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4988 int fx = FX, fy = FY;
4990 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4991 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4993 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4999 fx += dx * TILEX / 2;
5000 fy += dy * TILEY / 2;
5002 ScrollLevel(dx, dy);
5005 /* scroll in two steps of half tile size to make things smoother */
5006 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5008 /* scroll second step to align at full tile size */
5009 BlitScreenToBitmap(window);
5015 SetVideoFrameDelay(frame_delay_value_old);
5018 void RelocatePlayer(int jx, int jy, int el_player_raw)
5020 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5021 int player_nr = GET_PLAYER_NR(el_player);
5022 struct PlayerInfo *player = &stored_player[player_nr];
5023 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5024 boolean no_delay = (tape.warp_forward);
5025 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5026 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5027 int old_jx = player->jx;
5028 int old_jy = player->jy;
5029 int old_element = Feld[old_jx][old_jy];
5030 int element = Feld[jx][jy];
5031 boolean player_relocated = (old_jx != jx || old_jy != jy);
5033 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5034 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5035 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5036 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5037 int leave_side_horiz = move_dir_horiz;
5038 int leave_side_vert = move_dir_vert;
5039 int enter_side = enter_side_horiz | enter_side_vert;
5040 int leave_side = leave_side_horiz | leave_side_vert;
5042 if (player->GameOver) /* do not reanimate dead player */
5045 if (!player_relocated) /* no need to relocate the player */
5048 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5050 RemoveField(jx, jy); /* temporarily remove newly placed player */
5051 DrawLevelField(jx, jy);
5054 if (player->present)
5056 while (player->MovPos)
5058 ScrollPlayer(player, SCROLL_GO_ON);
5059 ScrollScreen(NULL, SCROLL_GO_ON);
5061 AdvanceFrameAndPlayerCounters(player->index_nr);
5065 BackToFront_WithFrameDelay(wait_delay_value);
5068 DrawPlayer(player); /* needed here only to cleanup last field */
5069 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5071 player->is_moving = FALSE;
5074 if (IS_CUSTOM_ELEMENT(old_element))
5075 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5077 player->index_bit, leave_side);
5079 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5081 player->index_bit, leave_side);
5083 Feld[jx][jy] = el_player;
5084 InitPlayerField(jx, jy, el_player, TRUE);
5086 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5087 possible that the relocation target field did not contain a player element,
5088 but a walkable element, to which the new player was relocated -- in this
5089 case, restore that (already initialized!) element on the player field */
5090 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5092 Feld[jx][jy] = element; /* restore previously existing element */
5095 /* only visually relocate centered player */
5096 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5097 FALSE, level.instant_relocation);
5099 TestIfPlayerTouchesBadThing(jx, jy);
5100 TestIfPlayerTouchesCustomElement(jx, jy);
5102 if (IS_CUSTOM_ELEMENT(element))
5103 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5104 player->index_bit, enter_side);
5106 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5107 player->index_bit, enter_side);
5109 if (player->is_switching)
5111 /* ensure that relocation while still switching an element does not cause
5112 a new element to be treated as also switched directly after relocation
5113 (this is important for teleporter switches that teleport the player to
5114 a place where another teleporter switch is in the same direction, which
5115 would then incorrectly be treated as immediately switched before the
5116 direction key that caused the switch was released) */
5118 player->switch_x += jx - old_jx;
5119 player->switch_y += jy - old_jy;
5123 void Explode(int ex, int ey, int phase, int mode)
5129 /* !!! eliminate this variable !!! */
5130 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5132 if (game.explosions_delayed)
5134 ExplodeField[ex][ey] = mode;
5138 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5140 int center_element = Feld[ex][ey];
5141 int artwork_element, explosion_element; /* set these values later */
5143 /* remove things displayed in background while burning dynamite */
5144 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5147 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5149 /* put moving element to center field (and let it explode there) */
5150 center_element = MovingOrBlocked2Element(ex, ey);
5151 RemoveMovingField(ex, ey);
5152 Feld[ex][ey] = center_element;
5155 /* now "center_element" is finally determined -- set related values now */
5156 artwork_element = center_element; /* for custom player artwork */
5157 explosion_element = center_element; /* for custom player artwork */
5159 if (IS_PLAYER(ex, ey))
5161 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5163 artwork_element = stored_player[player_nr].artwork_element;
5165 if (level.use_explosion_element[player_nr])
5167 explosion_element = level.explosion_element[player_nr];
5168 artwork_element = explosion_element;
5172 if (mode == EX_TYPE_NORMAL ||
5173 mode == EX_TYPE_CENTER ||
5174 mode == EX_TYPE_CROSS)
5175 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5177 last_phase = element_info[explosion_element].explosion_delay + 1;
5179 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5181 int xx = x - ex + 1;
5182 int yy = y - ey + 1;
5185 if (!IN_LEV_FIELD(x, y) ||
5186 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5187 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5190 element = Feld[x][y];
5192 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5194 element = MovingOrBlocked2Element(x, y);
5196 if (!IS_EXPLOSION_PROOF(element))
5197 RemoveMovingField(x, y);
5200 /* indestructible elements can only explode in center (but not flames) */
5201 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5202 mode == EX_TYPE_BORDER)) ||
5203 element == EL_FLAMES)
5206 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5207 behaviour, for example when touching a yamyam that explodes to rocks
5208 with active deadly shield, a rock is created under the player !!! */
5209 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5211 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5212 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5213 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5215 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5218 if (IS_ACTIVE_BOMB(element))
5220 /* re-activate things under the bomb like gate or penguin */
5221 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5228 /* save walkable background elements while explosion on same tile */
5229 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5230 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5231 Back[x][y] = element;
5233 /* ignite explodable elements reached by other explosion */
5234 if (element == EL_EXPLOSION)
5235 element = Store2[x][y];
5237 if (AmoebaNr[x][y] &&
5238 (element == EL_AMOEBA_FULL ||
5239 element == EL_BD_AMOEBA ||
5240 element == EL_AMOEBA_GROWING))
5242 AmoebaCnt[AmoebaNr[x][y]]--;
5243 AmoebaCnt2[AmoebaNr[x][y]]--;
5248 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5250 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5252 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5254 if (PLAYERINFO(ex, ey)->use_murphy)
5255 Store[x][y] = EL_EMPTY;
5258 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5259 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5260 else if (ELEM_IS_PLAYER(center_element))
5261 Store[x][y] = EL_EMPTY;
5262 else if (center_element == EL_YAMYAM)
5263 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5264 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5265 Store[x][y] = element_info[center_element].content.e[xx][yy];
5267 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5268 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5269 otherwise) -- FIX THIS !!! */
5270 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5271 Store[x][y] = element_info[element].content.e[1][1];
5273 else if (!CAN_EXPLODE(element))
5274 Store[x][y] = element_info[element].content.e[1][1];
5277 Store[x][y] = EL_EMPTY;
5279 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5280 center_element == EL_AMOEBA_TO_DIAMOND)
5281 Store2[x][y] = element;
5283 Feld[x][y] = EL_EXPLOSION;
5284 GfxElement[x][y] = artwork_element;
5286 ExplodePhase[x][y] = 1;
5287 ExplodeDelay[x][y] = last_phase;
5292 if (center_element == EL_YAMYAM)
5293 game.yamyam_content_nr =
5294 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5306 GfxFrame[x][y] = 0; /* restart explosion animation */
5308 last_phase = ExplodeDelay[x][y];
5310 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5312 /* this can happen if the player leaves an explosion just in time */
5313 if (GfxElement[x][y] == EL_UNDEFINED)
5314 GfxElement[x][y] = EL_EMPTY;
5316 border_element = Store2[x][y];
5317 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5318 border_element = StorePlayer[x][y];
5320 if (phase == element_info[border_element].ignition_delay ||
5321 phase == last_phase)
5323 boolean border_explosion = FALSE;
5325 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5326 !PLAYER_EXPLOSION_PROTECTED(x, y))
5328 KillPlayerUnlessExplosionProtected(x, y);
5329 border_explosion = TRUE;
5331 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5333 Feld[x][y] = Store2[x][y];
5336 border_explosion = TRUE;
5338 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5340 AmoebeUmwandeln(x, y);
5342 border_explosion = TRUE;
5345 /* if an element just explodes due to another explosion (chain-reaction),
5346 do not immediately end the new explosion when it was the last frame of
5347 the explosion (as it would be done in the following "if"-statement!) */
5348 if (border_explosion && phase == last_phase)
5352 if (phase == last_phase)
5356 element = Feld[x][y] = Store[x][y];
5357 Store[x][y] = Store2[x][y] = 0;
5358 GfxElement[x][y] = EL_UNDEFINED;
5360 /* player can escape from explosions and might therefore be still alive */
5361 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5362 element <= EL_PLAYER_IS_EXPLODING_4)
5364 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5365 int explosion_element = EL_PLAYER_1 + player_nr;
5366 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5367 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5369 if (level.use_explosion_element[player_nr])
5370 explosion_element = level.explosion_element[player_nr];
5372 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5373 element_info[explosion_element].content.e[xx][yy]);
5376 /* restore probably existing indestructible background element */
5377 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5378 element = Feld[x][y] = Back[x][y];
5381 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5382 GfxDir[x][y] = MV_NONE;
5383 ChangeDelay[x][y] = 0;
5384 ChangePage[x][y] = -1;
5386 CustomValue[x][y] = 0;
5388 InitField_WithBug2(x, y, FALSE);
5390 TEST_DrawLevelField(x, y);
5392 TestIfElementTouchesCustomElement(x, y);
5394 if (GFX_CRUMBLED(element))
5395 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5397 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5398 StorePlayer[x][y] = 0;
5400 if (ELEM_IS_PLAYER(element))
5401 RelocatePlayer(x, y, element);
5403 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5405 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5406 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5409 TEST_DrawLevelFieldCrumbled(x, y);
5411 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5413 DrawLevelElement(x, y, Back[x][y]);
5414 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5416 else if (IS_WALKABLE_UNDER(Back[x][y]))
5418 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5419 DrawLevelElementThruMask(x, y, Back[x][y]);
5421 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5422 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5426 void DynaExplode(int ex, int ey)
5429 int dynabomb_element = Feld[ex][ey];
5430 int dynabomb_size = 1;
5431 boolean dynabomb_xl = FALSE;
5432 struct PlayerInfo *player;
5433 static int xy[4][2] =
5441 if (IS_ACTIVE_BOMB(dynabomb_element))
5443 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5444 dynabomb_size = player->dynabomb_size;
5445 dynabomb_xl = player->dynabomb_xl;
5446 player->dynabombs_left++;
5449 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5451 for (i = 0; i < NUM_DIRECTIONS; i++)
5453 for (j = 1; j <= dynabomb_size; j++)
5455 int x = ex + j * xy[i][0];
5456 int y = ey + j * xy[i][1];
5459 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5462 element = Feld[x][y];
5464 /* do not restart explosions of fields with active bombs */
5465 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5468 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5470 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5471 !IS_DIGGABLE(element) && !dynabomb_xl)
5477 void Bang(int x, int y)
5479 int element = MovingOrBlocked2Element(x, y);
5480 int explosion_type = EX_TYPE_NORMAL;
5482 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5484 struct PlayerInfo *player = PLAYERINFO(x, y);
5486 element = Feld[x][y] = player->initial_element;
5488 if (level.use_explosion_element[player->index_nr])
5490 int explosion_element = level.explosion_element[player->index_nr];
5492 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5493 explosion_type = EX_TYPE_CROSS;
5494 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5495 explosion_type = EX_TYPE_CENTER;
5503 case EL_BD_BUTTERFLY:
5506 case EL_DARK_YAMYAM:
5510 RaiseScoreElement(element);
5513 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5514 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5515 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5516 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5517 case EL_DYNABOMB_INCREASE_NUMBER:
5518 case EL_DYNABOMB_INCREASE_SIZE:
5519 case EL_DYNABOMB_INCREASE_POWER:
5520 explosion_type = EX_TYPE_DYNA;
5523 case EL_DC_LANDMINE:
5524 explosion_type = EX_TYPE_CENTER;
5529 case EL_LAMP_ACTIVE:
5530 case EL_AMOEBA_TO_DIAMOND:
5531 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5532 explosion_type = EX_TYPE_CENTER;
5536 if (element_info[element].explosion_type == EXPLODES_CROSS)
5537 explosion_type = EX_TYPE_CROSS;
5538 else if (element_info[element].explosion_type == EXPLODES_1X1)
5539 explosion_type = EX_TYPE_CENTER;
5543 if (explosion_type == EX_TYPE_DYNA)
5546 Explode(x, y, EX_PHASE_START, explosion_type);
5548 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5551 void SplashAcid(int x, int y)
5553 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5554 (!IN_LEV_FIELD(x - 1, y - 2) ||
5555 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5556 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5558 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5559 (!IN_LEV_FIELD(x + 1, y - 2) ||
5560 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5561 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5563 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5566 static void InitBeltMovement()
5568 static int belt_base_element[4] =
5570 EL_CONVEYOR_BELT_1_LEFT,
5571 EL_CONVEYOR_BELT_2_LEFT,
5572 EL_CONVEYOR_BELT_3_LEFT,
5573 EL_CONVEYOR_BELT_4_LEFT
5575 static int belt_base_active_element[4] =
5577 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5578 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5579 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5580 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5585 /* set frame order for belt animation graphic according to belt direction */
5586 for (i = 0; i < NUM_BELTS; i++)
5590 for (j = 0; j < NUM_BELT_PARTS; j++)
5592 int element = belt_base_active_element[belt_nr] + j;
5593 int graphic_1 = el2img(element);
5594 int graphic_2 = el2panelimg(element);
5596 if (game.belt_dir[i] == MV_LEFT)
5598 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5599 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5603 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5604 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5609 SCAN_PLAYFIELD(x, y)
5611 int element = Feld[x][y];
5613 for (i = 0; i < NUM_BELTS; i++)
5615 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5617 int e_belt_nr = getBeltNrFromBeltElement(element);
5620 if (e_belt_nr == belt_nr)
5622 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5624 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5631 static void ToggleBeltSwitch(int x, int y)
5633 static int belt_base_element[4] =
5635 EL_CONVEYOR_BELT_1_LEFT,
5636 EL_CONVEYOR_BELT_2_LEFT,
5637 EL_CONVEYOR_BELT_3_LEFT,
5638 EL_CONVEYOR_BELT_4_LEFT
5640 static int belt_base_active_element[4] =
5642 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5643 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5644 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5645 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5647 static int belt_base_switch_element[4] =
5649 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5650 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5651 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5652 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5654 static int belt_move_dir[4] =
5662 int element = Feld[x][y];
5663 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5664 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5665 int belt_dir = belt_move_dir[belt_dir_nr];
5668 if (!IS_BELT_SWITCH(element))
5671 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5672 game.belt_dir[belt_nr] = belt_dir;
5674 if (belt_dir_nr == 3)
5677 /* set frame order for belt animation graphic according to belt direction */
5678 for (i = 0; i < NUM_BELT_PARTS; i++)
5680 int element = belt_base_active_element[belt_nr] + i;
5681 int graphic_1 = el2img(element);
5682 int graphic_2 = el2panelimg(element);
5684 if (belt_dir == MV_LEFT)
5686 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5687 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5691 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5692 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5696 SCAN_PLAYFIELD(xx, yy)
5698 int element = Feld[xx][yy];
5700 if (IS_BELT_SWITCH(element))
5702 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5704 if (e_belt_nr == belt_nr)
5706 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5707 TEST_DrawLevelField(xx, yy);
5710 else if (IS_BELT(element) && belt_dir != MV_NONE)
5712 int e_belt_nr = getBeltNrFromBeltElement(element);
5714 if (e_belt_nr == belt_nr)
5716 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5718 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5719 TEST_DrawLevelField(xx, yy);
5722 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5724 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5726 if (e_belt_nr == belt_nr)
5728 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5730 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5731 TEST_DrawLevelField(xx, yy);
5737 static void ToggleSwitchgateSwitch(int x, int y)
5741 game.switchgate_pos = !game.switchgate_pos;
5743 SCAN_PLAYFIELD(xx, yy)
5745 int element = Feld[xx][yy];
5747 if (element == EL_SWITCHGATE_SWITCH_UP)
5749 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5750 TEST_DrawLevelField(xx, yy);
5752 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5754 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5755 TEST_DrawLevelField(xx, yy);
5757 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5759 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5760 TEST_DrawLevelField(xx, yy);
5762 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5764 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5765 TEST_DrawLevelField(xx, yy);
5767 else if (element == EL_SWITCHGATE_OPEN ||
5768 element == EL_SWITCHGATE_OPENING)
5770 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5772 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5774 else if (element == EL_SWITCHGATE_CLOSED ||
5775 element == EL_SWITCHGATE_CLOSING)
5777 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5779 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5784 static int getInvisibleActiveFromInvisibleElement(int element)
5786 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5787 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5788 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5792 static int getInvisibleFromInvisibleActiveElement(int element)
5794 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5795 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5796 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5800 static void RedrawAllLightSwitchesAndInvisibleElements()
5804 SCAN_PLAYFIELD(x, y)
5806 int element = Feld[x][y];
5808 if (element == EL_LIGHT_SWITCH &&
5809 game.light_time_left > 0)
5811 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5812 TEST_DrawLevelField(x, y);
5814 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5815 game.light_time_left == 0)
5817 Feld[x][y] = EL_LIGHT_SWITCH;
5818 TEST_DrawLevelField(x, y);
5820 else if (element == EL_EMC_DRIPPER &&
5821 game.light_time_left > 0)
5823 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5824 TEST_DrawLevelField(x, y);
5826 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5827 game.light_time_left == 0)
5829 Feld[x][y] = EL_EMC_DRIPPER;
5830 TEST_DrawLevelField(x, y);
5832 else if (element == EL_INVISIBLE_STEELWALL ||
5833 element == EL_INVISIBLE_WALL ||
5834 element == EL_INVISIBLE_SAND)
5836 if (game.light_time_left > 0)
5837 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5839 TEST_DrawLevelField(x, y);
5841 /* uncrumble neighbour fields, if needed */
5842 if (element == EL_INVISIBLE_SAND)
5843 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5845 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5846 element == EL_INVISIBLE_WALL_ACTIVE ||
5847 element == EL_INVISIBLE_SAND_ACTIVE)
5849 if (game.light_time_left == 0)
5850 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5852 TEST_DrawLevelField(x, y);
5854 /* re-crumble neighbour fields, if needed */
5855 if (element == EL_INVISIBLE_SAND)
5856 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5861 static void RedrawAllInvisibleElementsForLenses()
5865 SCAN_PLAYFIELD(x, y)
5867 int element = Feld[x][y];
5869 if (element == EL_EMC_DRIPPER &&
5870 game.lenses_time_left > 0)
5872 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5873 TEST_DrawLevelField(x, y);
5875 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5876 game.lenses_time_left == 0)
5878 Feld[x][y] = EL_EMC_DRIPPER;
5879 TEST_DrawLevelField(x, y);
5881 else if (element == EL_INVISIBLE_STEELWALL ||
5882 element == EL_INVISIBLE_WALL ||
5883 element == EL_INVISIBLE_SAND)
5885 if (game.lenses_time_left > 0)
5886 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5888 TEST_DrawLevelField(x, y);
5890 /* uncrumble neighbour fields, if needed */
5891 if (element == EL_INVISIBLE_SAND)
5892 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5894 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5895 element == EL_INVISIBLE_WALL_ACTIVE ||
5896 element == EL_INVISIBLE_SAND_ACTIVE)
5898 if (game.lenses_time_left == 0)
5899 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5901 TEST_DrawLevelField(x, y);
5903 /* re-crumble neighbour fields, if needed */
5904 if (element == EL_INVISIBLE_SAND)
5905 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5910 static void RedrawAllInvisibleElementsForMagnifier()
5914 SCAN_PLAYFIELD(x, y)
5916 int element = Feld[x][y];
5918 if (element == EL_EMC_FAKE_GRASS &&
5919 game.magnify_time_left > 0)
5921 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5922 TEST_DrawLevelField(x, y);
5924 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5925 game.magnify_time_left == 0)
5927 Feld[x][y] = EL_EMC_FAKE_GRASS;
5928 TEST_DrawLevelField(x, y);
5930 else if (IS_GATE_GRAY(element) &&
5931 game.magnify_time_left > 0)
5933 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5934 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5935 IS_EM_GATE_GRAY(element) ?
5936 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5937 IS_EMC_GATE_GRAY(element) ?
5938 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5939 IS_DC_GATE_GRAY(element) ?
5940 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5942 TEST_DrawLevelField(x, y);
5944 else if (IS_GATE_GRAY_ACTIVE(element) &&
5945 game.magnify_time_left == 0)
5947 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5948 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5949 IS_EM_GATE_GRAY_ACTIVE(element) ?
5950 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5951 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5952 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5953 IS_DC_GATE_GRAY_ACTIVE(element) ?
5954 EL_DC_GATE_WHITE_GRAY :
5956 TEST_DrawLevelField(x, y);
5961 static void ToggleLightSwitch(int x, int y)
5963 int element = Feld[x][y];
5965 game.light_time_left =
5966 (element == EL_LIGHT_SWITCH ?
5967 level.time_light * FRAMES_PER_SECOND : 0);
5969 RedrawAllLightSwitchesAndInvisibleElements();
5972 static void ActivateTimegateSwitch(int x, int y)
5976 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5978 SCAN_PLAYFIELD(xx, yy)
5980 int element = Feld[xx][yy];
5982 if (element == EL_TIMEGATE_CLOSED ||
5983 element == EL_TIMEGATE_CLOSING)
5985 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5986 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5990 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5992 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5993 TEST_DrawLevelField(xx, yy);
5999 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6000 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6003 void Impact(int x, int y)
6005 boolean last_line = (y == lev_fieldy - 1);
6006 boolean object_hit = FALSE;
6007 boolean impact = (last_line || object_hit);
6008 int element = Feld[x][y];
6009 int smashed = EL_STEELWALL;
6011 if (!last_line) /* check if element below was hit */
6013 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6016 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6017 MovDir[x][y + 1] != MV_DOWN ||
6018 MovPos[x][y + 1] <= TILEY / 2));
6020 /* do not smash moving elements that left the smashed field in time */
6021 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6022 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6025 #if USE_QUICKSAND_IMPACT_BUGFIX
6026 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6028 RemoveMovingField(x, y + 1);
6029 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6030 Feld[x][y + 2] = EL_ROCK;
6031 TEST_DrawLevelField(x, y + 2);
6036 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6038 RemoveMovingField(x, y + 1);
6039 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6040 Feld[x][y + 2] = EL_ROCK;
6041 TEST_DrawLevelField(x, y + 2);
6048 smashed = MovingOrBlocked2Element(x, y + 1);
6050 impact = (last_line || object_hit);
6053 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6055 SplashAcid(x, y + 1);
6059 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6060 /* only reset graphic animation if graphic really changes after impact */
6062 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6064 ResetGfxAnimation(x, y);
6065 TEST_DrawLevelField(x, y);
6068 if (impact && CAN_EXPLODE_IMPACT(element))
6073 else if (impact && element == EL_PEARL &&
6074 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6076 ResetGfxAnimation(x, y);
6078 Feld[x][y] = EL_PEARL_BREAKING;
6079 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6082 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6084 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6089 if (impact && element == EL_AMOEBA_DROP)
6091 if (object_hit && IS_PLAYER(x, y + 1))
6092 KillPlayerUnlessEnemyProtected(x, y + 1);
6093 else if (object_hit && smashed == EL_PENGUIN)
6097 Feld[x][y] = EL_AMOEBA_GROWING;
6098 Store[x][y] = EL_AMOEBA_WET;
6100 ResetRandomAnimationValue(x, y);
6105 if (object_hit) /* check which object was hit */
6107 if ((CAN_PASS_MAGIC_WALL(element) &&
6108 (smashed == EL_MAGIC_WALL ||
6109 smashed == EL_BD_MAGIC_WALL)) ||
6110 (CAN_PASS_DC_MAGIC_WALL(element) &&
6111 smashed == EL_DC_MAGIC_WALL))
6114 int activated_magic_wall =
6115 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6116 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6117 EL_DC_MAGIC_WALL_ACTIVE);
6119 /* activate magic wall / mill */
6120 SCAN_PLAYFIELD(xx, yy)
6122 if (Feld[xx][yy] == smashed)
6123 Feld[xx][yy] = activated_magic_wall;
6126 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6127 game.magic_wall_active = TRUE;
6129 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6130 SND_MAGIC_WALL_ACTIVATING :
6131 smashed == EL_BD_MAGIC_WALL ?
6132 SND_BD_MAGIC_WALL_ACTIVATING :
6133 SND_DC_MAGIC_WALL_ACTIVATING));
6136 if (IS_PLAYER(x, y + 1))
6138 if (CAN_SMASH_PLAYER(element))
6140 KillPlayerUnlessEnemyProtected(x, y + 1);
6144 else if (smashed == EL_PENGUIN)
6146 if (CAN_SMASH_PLAYER(element))
6152 else if (element == EL_BD_DIAMOND)
6154 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6160 else if (((element == EL_SP_INFOTRON ||
6161 element == EL_SP_ZONK) &&
6162 (smashed == EL_SP_SNIKSNAK ||
6163 smashed == EL_SP_ELECTRON ||
6164 smashed == EL_SP_DISK_ORANGE)) ||
6165 (element == EL_SP_INFOTRON &&
6166 smashed == EL_SP_DISK_YELLOW))
6171 else if (CAN_SMASH_EVERYTHING(element))
6173 if (IS_CLASSIC_ENEMY(smashed) ||
6174 CAN_EXPLODE_SMASHED(smashed))
6179 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6181 if (smashed == EL_LAMP ||
6182 smashed == EL_LAMP_ACTIVE)
6187 else if (smashed == EL_NUT)
6189 Feld[x][y + 1] = EL_NUT_BREAKING;
6190 PlayLevelSound(x, y, SND_NUT_BREAKING);
6191 RaiseScoreElement(EL_NUT);
6194 else if (smashed == EL_PEARL)
6196 ResetGfxAnimation(x, y);
6198 Feld[x][y + 1] = EL_PEARL_BREAKING;
6199 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6202 else if (smashed == EL_DIAMOND)
6204 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6205 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6208 else if (IS_BELT_SWITCH(smashed))
6210 ToggleBeltSwitch(x, y + 1);
6212 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6213 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6214 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6215 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6217 ToggleSwitchgateSwitch(x, y + 1);
6219 else if (smashed == EL_LIGHT_SWITCH ||
6220 smashed == EL_LIGHT_SWITCH_ACTIVE)
6222 ToggleLightSwitch(x, y + 1);
6226 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6228 CheckElementChangeBySide(x, y + 1, smashed, element,
6229 CE_SWITCHED, CH_SIDE_TOP);
6230 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6236 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6241 /* play sound of magic wall / mill */
6243 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6244 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6245 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6247 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6248 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6249 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6250 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6251 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6252 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6257 /* play sound of object that hits the ground */
6258 if (last_line || object_hit)
6259 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6262 inline static void TurnRoundExt(int x, int y)
6274 { 0, 0 }, { 0, 0 }, { 0, 0 },
6279 int left, right, back;
6283 { MV_DOWN, MV_UP, MV_RIGHT },
6284 { MV_UP, MV_DOWN, MV_LEFT },
6286 { MV_LEFT, MV_RIGHT, MV_DOWN },
6290 { MV_RIGHT, MV_LEFT, MV_UP }
6293 int element = Feld[x][y];
6294 int move_pattern = element_info[element].move_pattern;
6296 int old_move_dir = MovDir[x][y];
6297 int left_dir = turn[old_move_dir].left;
6298 int right_dir = turn[old_move_dir].right;
6299 int back_dir = turn[old_move_dir].back;
6301 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6302 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6303 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6304 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6306 int left_x = x + left_dx, left_y = y + left_dy;
6307 int right_x = x + right_dx, right_y = y + right_dy;
6308 int move_x = x + move_dx, move_y = y + move_dy;
6312 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6314 TestIfBadThingTouchesOtherBadThing(x, y);
6316 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6317 MovDir[x][y] = right_dir;
6318 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6319 MovDir[x][y] = left_dir;
6321 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6323 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6326 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6328 TestIfBadThingTouchesOtherBadThing(x, y);
6330 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6331 MovDir[x][y] = left_dir;
6332 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6333 MovDir[x][y] = right_dir;
6335 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6337 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6340 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6342 TestIfBadThingTouchesOtherBadThing(x, y);
6344 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6345 MovDir[x][y] = left_dir;
6346 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6347 MovDir[x][y] = right_dir;
6349 if (MovDir[x][y] != old_move_dir)
6352 else if (element == EL_YAMYAM)
6354 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6355 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6357 if (can_turn_left && can_turn_right)
6358 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6359 else if (can_turn_left)
6360 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6361 else if (can_turn_right)
6362 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6364 MovDir[x][y] = back_dir;
6366 MovDelay[x][y] = 16 + 16 * RND(3);
6368 else if (element == EL_DARK_YAMYAM)
6370 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6372 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6375 if (can_turn_left && can_turn_right)
6376 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6377 else if (can_turn_left)
6378 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6379 else if (can_turn_right)
6380 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6382 MovDir[x][y] = back_dir;
6384 MovDelay[x][y] = 16 + 16 * RND(3);
6386 else if (element == EL_PACMAN)
6388 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6389 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6391 if (can_turn_left && can_turn_right)
6392 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6393 else if (can_turn_left)
6394 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6395 else if (can_turn_right)
6396 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6398 MovDir[x][y] = back_dir;
6400 MovDelay[x][y] = 6 + RND(40);
6402 else if (element == EL_PIG)
6404 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6405 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6406 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6407 boolean should_turn_left, should_turn_right, should_move_on;
6409 int rnd = RND(rnd_value);
6411 should_turn_left = (can_turn_left &&
6413 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6414 y + back_dy + left_dy)));
6415 should_turn_right = (can_turn_right &&
6417 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6418 y + back_dy + right_dy)));
6419 should_move_on = (can_move_on &&
6422 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6423 y + move_dy + left_dy) ||
6424 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6425 y + move_dy + right_dy)));
6427 if (should_turn_left || should_turn_right || should_move_on)
6429 if (should_turn_left && should_turn_right && should_move_on)
6430 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6431 rnd < 2 * rnd_value / 3 ? right_dir :
6433 else if (should_turn_left && should_turn_right)
6434 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6435 else if (should_turn_left && should_move_on)
6436 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6437 else if (should_turn_right && should_move_on)
6438 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6439 else if (should_turn_left)
6440 MovDir[x][y] = left_dir;
6441 else if (should_turn_right)
6442 MovDir[x][y] = right_dir;
6443 else if (should_move_on)
6444 MovDir[x][y] = old_move_dir;
6446 else if (can_move_on && rnd > rnd_value / 8)
6447 MovDir[x][y] = old_move_dir;
6448 else if (can_turn_left && can_turn_right)
6449 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6450 else if (can_turn_left && rnd > rnd_value / 8)
6451 MovDir[x][y] = left_dir;
6452 else if (can_turn_right && rnd > rnd_value/8)
6453 MovDir[x][y] = right_dir;
6455 MovDir[x][y] = back_dir;
6457 xx = x + move_xy[MovDir[x][y]].dx;
6458 yy = y + move_xy[MovDir[x][y]].dy;
6460 if (!IN_LEV_FIELD(xx, yy) ||
6461 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6462 MovDir[x][y] = old_move_dir;
6466 else if (element == EL_DRAGON)
6468 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6469 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6470 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6472 int rnd = RND(rnd_value);
6474 if (can_move_on && rnd > rnd_value / 8)
6475 MovDir[x][y] = old_move_dir;
6476 else if (can_turn_left && can_turn_right)
6477 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6478 else if (can_turn_left && rnd > rnd_value / 8)
6479 MovDir[x][y] = left_dir;
6480 else if (can_turn_right && rnd > rnd_value / 8)
6481 MovDir[x][y] = right_dir;
6483 MovDir[x][y] = back_dir;
6485 xx = x + move_xy[MovDir[x][y]].dx;
6486 yy = y + move_xy[MovDir[x][y]].dy;
6488 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6489 MovDir[x][y] = old_move_dir;
6493 else if (element == EL_MOLE)
6495 boolean can_move_on =
6496 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6497 IS_AMOEBOID(Feld[move_x][move_y]) ||
6498 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6501 boolean can_turn_left =
6502 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6503 IS_AMOEBOID(Feld[left_x][left_y])));
6505 boolean can_turn_right =
6506 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6507 IS_AMOEBOID(Feld[right_x][right_y])));
6509 if (can_turn_left && can_turn_right)
6510 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6511 else if (can_turn_left)
6512 MovDir[x][y] = left_dir;
6514 MovDir[x][y] = right_dir;
6517 if (MovDir[x][y] != old_move_dir)
6520 else if (element == EL_BALLOON)
6522 MovDir[x][y] = game.wind_direction;
6525 else if (element == EL_SPRING)
6527 if (MovDir[x][y] & MV_HORIZONTAL)
6529 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6530 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6532 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6533 ResetGfxAnimation(move_x, move_y);
6534 TEST_DrawLevelField(move_x, move_y);
6536 MovDir[x][y] = back_dir;
6538 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6539 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6540 MovDir[x][y] = MV_NONE;
6545 else if (element == EL_ROBOT ||
6546 element == EL_SATELLITE ||
6547 element == EL_PENGUIN ||
6548 element == EL_EMC_ANDROID)
6550 int attr_x = -1, attr_y = -1;
6561 for (i = 0; i < MAX_PLAYERS; i++)
6563 struct PlayerInfo *player = &stored_player[i];
6564 int jx = player->jx, jy = player->jy;
6566 if (!player->active)
6570 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6578 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6579 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6580 game.engine_version < VERSION_IDENT(3,1,0,0)))
6586 if (element == EL_PENGUIN)
6589 static int xy[4][2] =
6597 for (i = 0; i < NUM_DIRECTIONS; i++)
6599 int ex = x + xy[i][0];
6600 int ey = y + xy[i][1];
6602 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6603 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6604 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6605 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6614 MovDir[x][y] = MV_NONE;
6616 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6617 else if (attr_x > x)
6618 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6620 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6621 else if (attr_y > y)
6622 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6624 if (element == EL_ROBOT)
6628 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6629 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6630 Moving2Blocked(x, y, &newx, &newy);
6632 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6633 MovDelay[x][y] = 8 + 8 * !RND(3);
6635 MovDelay[x][y] = 16;
6637 else if (element == EL_PENGUIN)
6643 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6645 boolean first_horiz = RND(2);
6646 int new_move_dir = MovDir[x][y];
6649 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6650 Moving2Blocked(x, y, &newx, &newy);
6652 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6656 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6657 Moving2Blocked(x, y, &newx, &newy);
6659 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6662 MovDir[x][y] = old_move_dir;
6666 else if (element == EL_SATELLITE)
6672 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6674 boolean first_horiz = RND(2);
6675 int new_move_dir = MovDir[x][y];
6678 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6679 Moving2Blocked(x, y, &newx, &newy);
6681 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6685 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6686 Moving2Blocked(x, y, &newx, &newy);
6688 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6691 MovDir[x][y] = old_move_dir;
6695 else if (element == EL_EMC_ANDROID)
6697 static int check_pos[16] =
6699 -1, /* 0 => (invalid) */
6700 7, /* 1 => MV_LEFT */
6701 3, /* 2 => MV_RIGHT */
6702 -1, /* 3 => (invalid) */
6704 0, /* 5 => MV_LEFT | MV_UP */
6705 2, /* 6 => MV_RIGHT | MV_UP */
6706 -1, /* 7 => (invalid) */
6707 5, /* 8 => MV_DOWN */
6708 6, /* 9 => MV_LEFT | MV_DOWN */
6709 4, /* 10 => MV_RIGHT | MV_DOWN */
6710 -1, /* 11 => (invalid) */
6711 -1, /* 12 => (invalid) */
6712 -1, /* 13 => (invalid) */
6713 -1, /* 14 => (invalid) */
6714 -1, /* 15 => (invalid) */
6722 { -1, -1, MV_LEFT | MV_UP },
6724 { +1, -1, MV_RIGHT | MV_UP },
6725 { +1, 0, MV_RIGHT },
6726 { +1, +1, MV_RIGHT | MV_DOWN },
6728 { -1, +1, MV_LEFT | MV_DOWN },
6731 int start_pos, check_order;
6732 boolean can_clone = FALSE;
6735 /* check if there is any free field around current position */
6736 for (i = 0; i < 8; i++)
6738 int newx = x + check_xy[i].dx;
6739 int newy = y + check_xy[i].dy;
6741 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6749 if (can_clone) /* randomly find an element to clone */
6753 start_pos = check_pos[RND(8)];
6754 check_order = (RND(2) ? -1 : +1);
6756 for (i = 0; i < 8; i++)
6758 int pos_raw = start_pos + i * check_order;
6759 int pos = (pos_raw + 8) % 8;
6760 int newx = x + check_xy[pos].dx;
6761 int newy = y + check_xy[pos].dy;
6763 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6765 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6766 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6768 Store[x][y] = Feld[newx][newy];
6777 if (can_clone) /* randomly find a direction to move */
6781 start_pos = check_pos[RND(8)];
6782 check_order = (RND(2) ? -1 : +1);
6784 for (i = 0; i < 8; i++)
6786 int pos_raw = start_pos + i * check_order;
6787 int pos = (pos_raw + 8) % 8;
6788 int newx = x + check_xy[pos].dx;
6789 int newy = y + check_xy[pos].dy;
6790 int new_move_dir = check_xy[pos].dir;
6792 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6794 MovDir[x][y] = new_move_dir;
6795 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6804 if (can_clone) /* cloning and moving successful */
6807 /* cannot clone -- try to move towards player */
6809 start_pos = check_pos[MovDir[x][y] & 0x0f];
6810 check_order = (RND(2) ? -1 : +1);
6812 for (i = 0; i < 3; i++)
6814 /* first check start_pos, then previous/next or (next/previous) pos */
6815 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6816 int pos = (pos_raw + 8) % 8;
6817 int newx = x + check_xy[pos].dx;
6818 int newy = y + check_xy[pos].dy;
6819 int new_move_dir = check_xy[pos].dir;
6821 if (IS_PLAYER(newx, newy))
6824 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6826 MovDir[x][y] = new_move_dir;
6827 MovDelay[x][y] = level.android_move_time * 8 + 1;
6834 else if (move_pattern == MV_TURNING_LEFT ||
6835 move_pattern == MV_TURNING_RIGHT ||
6836 move_pattern == MV_TURNING_LEFT_RIGHT ||
6837 move_pattern == MV_TURNING_RIGHT_LEFT ||
6838 move_pattern == MV_TURNING_RANDOM ||
6839 move_pattern == MV_ALL_DIRECTIONS)
6841 boolean can_turn_left =
6842 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6843 boolean can_turn_right =
6844 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6846 if (element_info[element].move_stepsize == 0) /* "not moving" */
6849 if (move_pattern == MV_TURNING_LEFT)
6850 MovDir[x][y] = left_dir;
6851 else if (move_pattern == MV_TURNING_RIGHT)
6852 MovDir[x][y] = right_dir;
6853 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6854 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6855 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6856 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6857 else if (move_pattern == MV_TURNING_RANDOM)
6858 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6859 can_turn_right && !can_turn_left ? right_dir :
6860 RND(2) ? left_dir : right_dir);
6861 else if (can_turn_left && can_turn_right)
6862 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6863 else if (can_turn_left)
6864 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6865 else if (can_turn_right)
6866 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6868 MovDir[x][y] = back_dir;
6870 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6872 else if (move_pattern == MV_HORIZONTAL ||
6873 move_pattern == MV_VERTICAL)
6875 if (move_pattern & old_move_dir)
6876 MovDir[x][y] = back_dir;
6877 else if (move_pattern == MV_HORIZONTAL)
6878 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6879 else if (move_pattern == MV_VERTICAL)
6880 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6882 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6884 else if (move_pattern & MV_ANY_DIRECTION)
6886 MovDir[x][y] = move_pattern;
6887 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6889 else if (move_pattern & MV_WIND_DIRECTION)
6891 MovDir[x][y] = game.wind_direction;
6892 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6894 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6896 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6897 MovDir[x][y] = left_dir;
6898 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6899 MovDir[x][y] = right_dir;
6901 if (MovDir[x][y] != old_move_dir)
6902 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6904 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6906 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6907 MovDir[x][y] = right_dir;
6908 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6909 MovDir[x][y] = left_dir;
6911 if (MovDir[x][y] != old_move_dir)
6912 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6914 else if (move_pattern == MV_TOWARDS_PLAYER ||
6915 move_pattern == MV_AWAY_FROM_PLAYER)
6917 int attr_x = -1, attr_y = -1;
6919 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6930 for (i = 0; i < MAX_PLAYERS; i++)
6932 struct PlayerInfo *player = &stored_player[i];
6933 int jx = player->jx, jy = player->jy;
6935 if (!player->active)
6939 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6947 MovDir[x][y] = MV_NONE;
6949 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6950 else if (attr_x > x)
6951 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6953 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6954 else if (attr_y > y)
6955 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6957 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6959 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6961 boolean first_horiz = RND(2);
6962 int new_move_dir = MovDir[x][y];
6964 if (element_info[element].move_stepsize == 0) /* "not moving" */
6966 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6967 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6973 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6974 Moving2Blocked(x, y, &newx, &newy);
6976 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6980 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6981 Moving2Blocked(x, y, &newx, &newy);
6983 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6986 MovDir[x][y] = old_move_dir;
6989 else if (move_pattern == MV_WHEN_PUSHED ||
6990 move_pattern == MV_WHEN_DROPPED)
6992 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6993 MovDir[x][y] = MV_NONE;
6997 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6999 static int test_xy[7][2] =
7009 static int test_dir[7] =
7019 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7020 int move_preference = -1000000; /* start with very low preference */
7021 int new_move_dir = MV_NONE;
7022 int start_test = RND(4);
7025 for (i = 0; i < NUM_DIRECTIONS; i++)
7027 int move_dir = test_dir[start_test + i];
7028 int move_dir_preference;
7030 xx = x + test_xy[start_test + i][0];
7031 yy = y + test_xy[start_test + i][1];
7033 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7034 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7036 new_move_dir = move_dir;
7041 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7044 move_dir_preference = -1 * RunnerVisit[xx][yy];
7045 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7046 move_dir_preference = PlayerVisit[xx][yy];
7048 if (move_dir_preference > move_preference)
7050 /* prefer field that has not been visited for the longest time */
7051 move_preference = move_dir_preference;
7052 new_move_dir = move_dir;
7054 else if (move_dir_preference == move_preference &&
7055 move_dir == old_move_dir)
7057 /* prefer last direction when all directions are preferred equally */
7058 move_preference = move_dir_preference;
7059 new_move_dir = move_dir;
7063 MovDir[x][y] = new_move_dir;
7064 if (old_move_dir != new_move_dir)
7065 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7069 static void TurnRound(int x, int y)
7071 int direction = MovDir[x][y];
7075 GfxDir[x][y] = MovDir[x][y];
7077 if (direction != MovDir[x][y])
7081 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7083 ResetGfxFrame(x, y, FALSE);
7086 static boolean JustBeingPushed(int x, int y)
7090 for (i = 0; i < MAX_PLAYERS; i++)
7092 struct PlayerInfo *player = &stored_player[i];
7094 if (player->active && player->is_pushing && player->MovPos)
7096 int next_jx = player->jx + (player->jx - player->last_jx);
7097 int next_jy = player->jy + (player->jy - player->last_jy);
7099 if (x == next_jx && y == next_jy)
7107 void StartMoving(int x, int y)
7109 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7110 int element = Feld[x][y];
7115 if (MovDelay[x][y] == 0)
7116 GfxAction[x][y] = ACTION_DEFAULT;
7118 if (CAN_FALL(element) && y < lev_fieldy - 1)
7120 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7121 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7122 if (JustBeingPushed(x, y))
7125 if (element == EL_QUICKSAND_FULL)
7127 if (IS_FREE(x, y + 1))
7129 InitMovingField(x, y, MV_DOWN);
7130 started_moving = TRUE;
7132 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7133 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7134 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7135 Store[x][y] = EL_ROCK;
7137 Store[x][y] = EL_ROCK;
7140 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7142 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7144 if (!MovDelay[x][y])
7146 MovDelay[x][y] = TILEY + 1;
7148 ResetGfxAnimation(x, y);
7149 ResetGfxAnimation(x, y + 1);
7154 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7155 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7162 Feld[x][y] = EL_QUICKSAND_EMPTY;
7163 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7164 Store[x][y + 1] = Store[x][y];
7167 PlayLevelSoundAction(x, y, ACTION_FILLING);
7169 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7171 if (!MovDelay[x][y])
7173 MovDelay[x][y] = TILEY + 1;
7175 ResetGfxAnimation(x, y);
7176 ResetGfxAnimation(x, y + 1);
7181 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7182 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7189 Feld[x][y] = EL_QUICKSAND_EMPTY;
7190 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7191 Store[x][y + 1] = Store[x][y];
7194 PlayLevelSoundAction(x, y, ACTION_FILLING);
7197 else if (element == EL_QUICKSAND_FAST_FULL)
7199 if (IS_FREE(x, y + 1))
7201 InitMovingField(x, y, MV_DOWN);
7202 started_moving = TRUE;
7204 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7205 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7206 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7207 Store[x][y] = EL_ROCK;
7209 Store[x][y] = EL_ROCK;
7212 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7214 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7216 if (!MovDelay[x][y])
7218 MovDelay[x][y] = TILEY + 1;
7220 ResetGfxAnimation(x, y);
7221 ResetGfxAnimation(x, y + 1);
7226 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7227 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7234 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7235 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7236 Store[x][y + 1] = Store[x][y];
7239 PlayLevelSoundAction(x, y, ACTION_FILLING);
7241 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7243 if (!MovDelay[x][y])
7245 MovDelay[x][y] = TILEY + 1;
7247 ResetGfxAnimation(x, y);
7248 ResetGfxAnimation(x, y + 1);
7253 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7254 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7261 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7262 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7263 Store[x][y + 1] = Store[x][y];
7266 PlayLevelSoundAction(x, y, ACTION_FILLING);
7269 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7270 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7272 InitMovingField(x, y, MV_DOWN);
7273 started_moving = TRUE;
7275 Feld[x][y] = EL_QUICKSAND_FILLING;
7276 Store[x][y] = element;
7278 PlayLevelSoundAction(x, y, ACTION_FILLING);
7280 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7281 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7283 InitMovingField(x, y, MV_DOWN);
7284 started_moving = TRUE;
7286 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7287 Store[x][y] = element;
7289 PlayLevelSoundAction(x, y, ACTION_FILLING);
7291 else if (element == EL_MAGIC_WALL_FULL)
7293 if (IS_FREE(x, y + 1))
7295 InitMovingField(x, y, MV_DOWN);
7296 started_moving = TRUE;
7298 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7299 Store[x][y] = EL_CHANGED(Store[x][y]);
7301 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7303 if (!MovDelay[x][y])
7304 MovDelay[x][y] = TILEY / 4 + 1;
7313 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7314 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7315 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7319 else if (element == EL_BD_MAGIC_WALL_FULL)
7321 if (IS_FREE(x, y + 1))
7323 InitMovingField(x, y, MV_DOWN);
7324 started_moving = TRUE;
7326 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7327 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7329 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7331 if (!MovDelay[x][y])
7332 MovDelay[x][y] = TILEY / 4 + 1;
7341 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7342 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7343 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7347 else if (element == EL_DC_MAGIC_WALL_FULL)
7349 if (IS_FREE(x, y + 1))
7351 InitMovingField(x, y, MV_DOWN);
7352 started_moving = TRUE;
7354 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7355 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7357 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7359 if (!MovDelay[x][y])
7360 MovDelay[x][y] = TILEY / 4 + 1;
7369 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7370 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7371 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7375 else if ((CAN_PASS_MAGIC_WALL(element) &&
7376 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7377 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7378 (CAN_PASS_DC_MAGIC_WALL(element) &&
7379 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7382 InitMovingField(x, y, MV_DOWN);
7383 started_moving = TRUE;
7386 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7387 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7388 EL_DC_MAGIC_WALL_FILLING);
7389 Store[x][y] = element;
7391 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7393 SplashAcid(x, y + 1);
7395 InitMovingField(x, y, MV_DOWN);
7396 started_moving = TRUE;
7398 Store[x][y] = EL_ACID;
7401 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7402 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7403 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7404 CAN_FALL(element) && WasJustFalling[x][y] &&
7405 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7407 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7408 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7409 (Feld[x][y + 1] == EL_BLOCKED)))
7411 /* this is needed for a special case not covered by calling "Impact()"
7412 from "ContinueMoving()": if an element moves to a tile directly below
7413 another element which was just falling on that tile (which was empty
7414 in the previous frame), the falling element above would just stop
7415 instead of smashing the element below (in previous version, the above
7416 element was just checked for "moving" instead of "falling", resulting
7417 in incorrect smashes caused by horizontal movement of the above
7418 element; also, the case of the player being the element to smash was
7419 simply not covered here... :-/ ) */
7421 CheckCollision[x][y] = 0;
7422 CheckImpact[x][y] = 0;
7426 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7428 if (MovDir[x][y] == MV_NONE)
7430 InitMovingField(x, y, MV_DOWN);
7431 started_moving = TRUE;
7434 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7436 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7437 MovDir[x][y] = MV_DOWN;
7439 InitMovingField(x, y, MV_DOWN);
7440 started_moving = TRUE;
7442 else if (element == EL_AMOEBA_DROP)
7444 Feld[x][y] = EL_AMOEBA_GROWING;
7445 Store[x][y] = EL_AMOEBA_WET;
7447 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7448 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7449 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7450 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7452 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7453 (IS_FREE(x - 1, y + 1) ||
7454 Feld[x - 1][y + 1] == EL_ACID));
7455 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7456 (IS_FREE(x + 1, y + 1) ||
7457 Feld[x + 1][y + 1] == EL_ACID));
7458 boolean can_fall_any = (can_fall_left || can_fall_right);
7459 boolean can_fall_both = (can_fall_left && can_fall_right);
7460 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7462 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7464 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7465 can_fall_right = FALSE;
7466 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7467 can_fall_left = FALSE;
7468 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7469 can_fall_right = FALSE;
7470 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7471 can_fall_left = FALSE;
7473 can_fall_any = (can_fall_left || can_fall_right);
7474 can_fall_both = FALSE;
7479 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7480 can_fall_right = FALSE; /* slip down on left side */
7482 can_fall_left = !(can_fall_right = RND(2));
7484 can_fall_both = FALSE;
7489 /* if not determined otherwise, prefer left side for slipping down */
7490 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7491 started_moving = TRUE;
7494 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7496 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7497 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7498 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7499 int belt_dir = game.belt_dir[belt_nr];
7501 if ((belt_dir == MV_LEFT && left_is_free) ||
7502 (belt_dir == MV_RIGHT && right_is_free))
7504 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7506 InitMovingField(x, y, belt_dir);
7507 started_moving = TRUE;
7509 Pushed[x][y] = TRUE;
7510 Pushed[nextx][y] = TRUE;
7512 GfxAction[x][y] = ACTION_DEFAULT;
7516 MovDir[x][y] = 0; /* if element was moving, stop it */
7521 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7522 if (CAN_MOVE(element) && !started_moving)
7524 int move_pattern = element_info[element].move_pattern;
7527 Moving2Blocked(x, y, &newx, &newy);
7529 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7532 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7533 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7535 WasJustMoving[x][y] = 0;
7536 CheckCollision[x][y] = 0;
7538 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7540 if (Feld[x][y] != element) /* element has changed */
7544 if (!MovDelay[x][y]) /* start new movement phase */
7546 /* all objects that can change their move direction after each step
7547 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7549 if (element != EL_YAMYAM &&
7550 element != EL_DARK_YAMYAM &&
7551 element != EL_PACMAN &&
7552 !(move_pattern & MV_ANY_DIRECTION) &&
7553 move_pattern != MV_TURNING_LEFT &&
7554 move_pattern != MV_TURNING_RIGHT &&
7555 move_pattern != MV_TURNING_LEFT_RIGHT &&
7556 move_pattern != MV_TURNING_RIGHT_LEFT &&
7557 move_pattern != MV_TURNING_RANDOM)
7561 if (MovDelay[x][y] && (element == EL_BUG ||
7562 element == EL_SPACESHIP ||
7563 element == EL_SP_SNIKSNAK ||
7564 element == EL_SP_ELECTRON ||
7565 element == EL_MOLE))
7566 TEST_DrawLevelField(x, y);
7570 if (MovDelay[x][y]) /* wait some time before next movement */
7574 if (element == EL_ROBOT ||
7575 element == EL_YAMYAM ||
7576 element == EL_DARK_YAMYAM)
7578 DrawLevelElementAnimationIfNeeded(x, y, element);
7579 PlayLevelSoundAction(x, y, ACTION_WAITING);
7581 else if (element == EL_SP_ELECTRON)
7582 DrawLevelElementAnimationIfNeeded(x, y, element);
7583 else if (element == EL_DRAGON)
7586 int dir = MovDir[x][y];
7587 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7588 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7589 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7590 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7591 dir == MV_UP ? IMG_FLAMES_1_UP :
7592 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7593 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7595 GfxAction[x][y] = ACTION_ATTACKING;
7597 if (IS_PLAYER(x, y))
7598 DrawPlayerField(x, y);
7600 TEST_DrawLevelField(x, y);
7602 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7604 for (i = 1; i <= 3; i++)
7606 int xx = x + i * dx;
7607 int yy = y + i * dy;
7608 int sx = SCREENX(xx);
7609 int sy = SCREENY(yy);
7610 int flame_graphic = graphic + (i - 1);
7612 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7617 int flamed = MovingOrBlocked2Element(xx, yy);
7619 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7622 RemoveMovingField(xx, yy);
7624 ChangeDelay[xx][yy] = 0;
7626 Feld[xx][yy] = EL_FLAMES;
7628 if (IN_SCR_FIELD(sx, sy))
7630 TEST_DrawLevelFieldCrumbled(xx, yy);
7631 DrawGraphic(sx, sy, flame_graphic, frame);
7636 if (Feld[xx][yy] == EL_FLAMES)
7637 Feld[xx][yy] = EL_EMPTY;
7638 TEST_DrawLevelField(xx, yy);
7643 if (MovDelay[x][y]) /* element still has to wait some time */
7645 PlayLevelSoundAction(x, y, ACTION_WAITING);
7651 /* now make next step */
7653 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7655 if (DONT_COLLIDE_WITH(element) &&
7656 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7657 !PLAYER_ENEMY_PROTECTED(newx, newy))
7659 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7664 else if (CAN_MOVE_INTO_ACID(element) &&
7665 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7666 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7667 (MovDir[x][y] == MV_DOWN ||
7668 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7670 SplashAcid(newx, newy);
7671 Store[x][y] = EL_ACID;
7673 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7675 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7676 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7677 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7678 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7681 TEST_DrawLevelField(x, y);
7683 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7684 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7685 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7687 local_player->friends_still_needed--;
7688 if (!local_player->friends_still_needed &&
7689 !local_player->GameOver && AllPlayersGone)
7690 PlayerWins(local_player);
7694 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7696 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7697 TEST_DrawLevelField(newx, newy);
7699 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7701 else if (!IS_FREE(newx, newy))
7703 GfxAction[x][y] = ACTION_WAITING;
7705 if (IS_PLAYER(x, y))
7706 DrawPlayerField(x, y);
7708 TEST_DrawLevelField(x, y);
7713 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7715 if (IS_FOOD_PIG(Feld[newx][newy]))
7717 if (IS_MOVING(newx, newy))
7718 RemoveMovingField(newx, newy);
7721 Feld[newx][newy] = EL_EMPTY;
7722 TEST_DrawLevelField(newx, newy);
7725 PlayLevelSound(x, y, SND_PIG_DIGGING);
7727 else if (!IS_FREE(newx, newy))
7729 if (IS_PLAYER(x, y))
7730 DrawPlayerField(x, y);
7732 TEST_DrawLevelField(x, y);
7737 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7739 if (Store[x][y] != EL_EMPTY)
7741 boolean can_clone = FALSE;
7744 /* check if element to clone is still there */
7745 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7747 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7755 /* cannot clone or target field not free anymore -- do not clone */
7756 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7757 Store[x][y] = EL_EMPTY;
7760 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7762 if (IS_MV_DIAGONAL(MovDir[x][y]))
7764 int diagonal_move_dir = MovDir[x][y];
7765 int stored = Store[x][y];
7766 int change_delay = 8;
7769 /* android is moving diagonally */
7771 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7773 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7774 GfxElement[x][y] = EL_EMC_ANDROID;
7775 GfxAction[x][y] = ACTION_SHRINKING;
7776 GfxDir[x][y] = diagonal_move_dir;
7777 ChangeDelay[x][y] = change_delay;
7779 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7782 DrawLevelGraphicAnimation(x, y, graphic);
7783 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7785 if (Feld[newx][newy] == EL_ACID)
7787 SplashAcid(newx, newy);
7792 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7794 Store[newx][newy] = EL_EMC_ANDROID;
7795 GfxElement[newx][newy] = EL_EMC_ANDROID;
7796 GfxAction[newx][newy] = ACTION_GROWING;
7797 GfxDir[newx][newy] = diagonal_move_dir;
7798 ChangeDelay[newx][newy] = change_delay;
7800 graphic = el_act_dir2img(GfxElement[newx][newy],
7801 GfxAction[newx][newy], GfxDir[newx][newy]);
7803 DrawLevelGraphicAnimation(newx, newy, graphic);
7804 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7810 Feld[newx][newy] = EL_EMPTY;
7811 TEST_DrawLevelField(newx, newy);
7813 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7816 else if (!IS_FREE(newx, newy))
7821 else if (IS_CUSTOM_ELEMENT(element) &&
7822 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7824 if (!DigFieldByCE(newx, newy, element))
7827 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7829 RunnerVisit[x][y] = FrameCounter;
7830 PlayerVisit[x][y] /= 8; /* expire player visit path */
7833 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7835 if (!IS_FREE(newx, newy))
7837 if (IS_PLAYER(x, y))
7838 DrawPlayerField(x, y);
7840 TEST_DrawLevelField(x, y);
7846 boolean wanna_flame = !RND(10);
7847 int dx = newx - x, dy = newy - y;
7848 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7849 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7850 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7851 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7852 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7853 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7856 IS_CLASSIC_ENEMY(element1) ||
7857 IS_CLASSIC_ENEMY(element2)) &&
7858 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7859 element1 != EL_FLAMES && element2 != EL_FLAMES)
7861 ResetGfxAnimation(x, y);
7862 GfxAction[x][y] = ACTION_ATTACKING;
7864 if (IS_PLAYER(x, y))
7865 DrawPlayerField(x, y);
7867 TEST_DrawLevelField(x, y);
7869 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7871 MovDelay[x][y] = 50;
7873 Feld[newx][newy] = EL_FLAMES;
7874 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7875 Feld[newx1][newy1] = EL_FLAMES;
7876 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7877 Feld[newx2][newy2] = EL_FLAMES;
7883 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7884 Feld[newx][newy] == EL_DIAMOND)
7886 if (IS_MOVING(newx, newy))
7887 RemoveMovingField(newx, newy);
7890 Feld[newx][newy] = EL_EMPTY;
7891 TEST_DrawLevelField(newx, newy);
7894 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7896 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7897 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7899 if (AmoebaNr[newx][newy])
7901 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7902 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7903 Feld[newx][newy] == EL_BD_AMOEBA)
7904 AmoebaCnt[AmoebaNr[newx][newy]]--;
7907 if (IS_MOVING(newx, newy))
7909 RemoveMovingField(newx, newy);
7913 Feld[newx][newy] = EL_EMPTY;
7914 TEST_DrawLevelField(newx, newy);
7917 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7919 else if ((element == EL_PACMAN || element == EL_MOLE)
7920 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7922 if (AmoebaNr[newx][newy])
7924 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7925 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7926 Feld[newx][newy] == EL_BD_AMOEBA)
7927 AmoebaCnt[AmoebaNr[newx][newy]]--;
7930 if (element == EL_MOLE)
7932 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7933 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7935 ResetGfxAnimation(x, y);
7936 GfxAction[x][y] = ACTION_DIGGING;
7937 TEST_DrawLevelField(x, y);
7939 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7941 return; /* wait for shrinking amoeba */
7943 else /* element == EL_PACMAN */
7945 Feld[newx][newy] = EL_EMPTY;
7946 TEST_DrawLevelField(newx, newy);
7947 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7950 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7951 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7952 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7954 /* wait for shrinking amoeba to completely disappear */
7957 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7959 /* object was running against a wall */
7963 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7964 DrawLevelElementAnimation(x, y, element);
7966 if (DONT_TOUCH(element))
7967 TestIfBadThingTouchesPlayer(x, y);
7972 InitMovingField(x, y, MovDir[x][y]);
7974 PlayLevelSoundAction(x, y, ACTION_MOVING);
7978 ContinueMoving(x, y);
7981 void ContinueMoving(int x, int y)
7983 int element = Feld[x][y];
7984 struct ElementInfo *ei = &element_info[element];
7985 int direction = MovDir[x][y];
7986 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7987 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7988 int newx = x + dx, newy = y + dy;
7989 int stored = Store[x][y];
7990 int stored_new = Store[newx][newy];
7991 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7992 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7993 boolean last_line = (newy == lev_fieldy - 1);
7995 MovPos[x][y] += getElementMoveStepsize(x, y);
7997 if (pushed_by_player) /* special case: moving object pushed by player */
7998 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8000 if (ABS(MovPos[x][y]) < TILEX)
8002 TEST_DrawLevelField(x, y);
8004 return; /* element is still moving */
8007 /* element reached destination field */
8009 Feld[x][y] = EL_EMPTY;
8010 Feld[newx][newy] = element;
8011 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8013 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8015 element = Feld[newx][newy] = EL_ACID;
8017 else if (element == EL_MOLE)
8019 Feld[x][y] = EL_SAND;
8021 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8023 else if (element == EL_QUICKSAND_FILLING)
8025 element = Feld[newx][newy] = get_next_element(element);
8026 Store[newx][newy] = Store[x][y];
8028 else if (element == EL_QUICKSAND_EMPTYING)
8030 Feld[x][y] = get_next_element(element);
8031 element = Feld[newx][newy] = Store[x][y];
8033 else if (element == EL_QUICKSAND_FAST_FILLING)
8035 element = Feld[newx][newy] = get_next_element(element);
8036 Store[newx][newy] = Store[x][y];
8038 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8040 Feld[x][y] = get_next_element(element);
8041 element = Feld[newx][newy] = Store[x][y];
8043 else if (element == EL_MAGIC_WALL_FILLING)
8045 element = Feld[newx][newy] = get_next_element(element);
8046 if (!game.magic_wall_active)
8047 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8048 Store[newx][newy] = Store[x][y];
8050 else if (element == EL_MAGIC_WALL_EMPTYING)
8052 Feld[x][y] = get_next_element(element);
8053 if (!game.magic_wall_active)
8054 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8055 element = Feld[newx][newy] = Store[x][y];
8057 InitField(newx, newy, FALSE);
8059 else if (element == EL_BD_MAGIC_WALL_FILLING)
8061 element = Feld[newx][newy] = get_next_element(element);
8062 if (!game.magic_wall_active)
8063 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8064 Store[newx][newy] = Store[x][y];
8066 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8068 Feld[x][y] = get_next_element(element);
8069 if (!game.magic_wall_active)
8070 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8071 element = Feld[newx][newy] = Store[x][y];
8073 InitField(newx, newy, FALSE);
8075 else if (element == EL_DC_MAGIC_WALL_FILLING)
8077 element = Feld[newx][newy] = get_next_element(element);
8078 if (!game.magic_wall_active)
8079 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8080 Store[newx][newy] = Store[x][y];
8082 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8084 Feld[x][y] = get_next_element(element);
8085 if (!game.magic_wall_active)
8086 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8087 element = Feld[newx][newy] = Store[x][y];
8089 InitField(newx, newy, FALSE);
8091 else if (element == EL_AMOEBA_DROPPING)
8093 Feld[x][y] = get_next_element(element);
8094 element = Feld[newx][newy] = Store[x][y];
8096 else if (element == EL_SOKOBAN_OBJECT)
8099 Feld[x][y] = Back[x][y];
8101 if (Back[newx][newy])
8102 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8104 Back[x][y] = Back[newx][newy] = 0;
8107 Store[x][y] = EL_EMPTY;
8112 MovDelay[newx][newy] = 0;
8114 if (CAN_CHANGE_OR_HAS_ACTION(element))
8116 /* copy element change control values to new field */
8117 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8118 ChangePage[newx][newy] = ChangePage[x][y];
8119 ChangeCount[newx][newy] = ChangeCount[x][y];
8120 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8123 CustomValue[newx][newy] = CustomValue[x][y];
8125 ChangeDelay[x][y] = 0;
8126 ChangePage[x][y] = -1;
8127 ChangeCount[x][y] = 0;
8128 ChangeEvent[x][y] = -1;
8130 CustomValue[x][y] = 0;
8132 /* copy animation control values to new field */
8133 GfxFrame[newx][newy] = GfxFrame[x][y];
8134 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8135 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8136 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8138 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8140 /* some elements can leave other elements behind after moving */
8141 if (ei->move_leave_element != EL_EMPTY &&
8142 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8143 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8145 int move_leave_element = ei->move_leave_element;
8147 /* this makes it possible to leave the removed element again */
8148 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8149 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8151 Feld[x][y] = move_leave_element;
8153 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8154 MovDir[x][y] = direction;
8156 InitField(x, y, FALSE);
8158 if (GFX_CRUMBLED(Feld[x][y]))
8159 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8161 if (ELEM_IS_PLAYER(move_leave_element))
8162 RelocatePlayer(x, y, move_leave_element);
8165 /* do this after checking for left-behind element */
8166 ResetGfxAnimation(x, y); /* reset animation values for old field */
8168 if (!CAN_MOVE(element) ||
8169 (CAN_FALL(element) && direction == MV_DOWN &&
8170 (element == EL_SPRING ||
8171 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8172 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8173 GfxDir[x][y] = MovDir[newx][newy] = 0;
8175 TEST_DrawLevelField(x, y);
8176 TEST_DrawLevelField(newx, newy);
8178 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8180 /* prevent pushed element from moving on in pushed direction */
8181 if (pushed_by_player && CAN_MOVE(element) &&
8182 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8183 !(element_info[element].move_pattern & direction))
8184 TurnRound(newx, newy);
8186 /* prevent elements on conveyor belt from moving on in last direction */
8187 if (pushed_by_conveyor && CAN_FALL(element) &&
8188 direction & MV_HORIZONTAL)
8189 MovDir[newx][newy] = 0;
8191 if (!pushed_by_player)
8193 int nextx = newx + dx, nexty = newy + dy;
8194 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8196 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8198 if (CAN_FALL(element) && direction == MV_DOWN)
8199 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8201 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8202 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8204 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8205 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8208 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8210 TestIfBadThingTouchesPlayer(newx, newy);
8211 TestIfBadThingTouchesFriend(newx, newy);
8213 if (!IS_CUSTOM_ELEMENT(element))
8214 TestIfBadThingTouchesOtherBadThing(newx, newy);
8216 else if (element == EL_PENGUIN)
8217 TestIfFriendTouchesBadThing(newx, newy);
8219 if (DONT_GET_HIT_BY(element))
8221 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8224 /* give the player one last chance (one more frame) to move away */
8225 if (CAN_FALL(element) && direction == MV_DOWN &&
8226 (last_line || (!IS_FREE(x, newy + 1) &&
8227 (!IS_PLAYER(x, newy + 1) ||
8228 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8231 if (pushed_by_player && !game.use_change_when_pushing_bug)
8233 int push_side = MV_DIR_OPPOSITE(direction);
8234 struct PlayerInfo *player = PLAYERINFO(x, y);
8236 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8237 player->index_bit, push_side);
8238 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8239 player->index_bit, push_side);
8242 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8243 MovDelay[newx][newy] = 1;
8245 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8247 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8248 TestIfElementHitsCustomElement(newx, newy, direction);
8249 TestIfPlayerTouchesCustomElement(newx, newy);
8250 TestIfElementTouchesCustomElement(newx, newy);
8252 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8253 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8254 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8255 MV_DIR_OPPOSITE(direction));
8258 int AmoebeNachbarNr(int ax, int ay)
8261 int element = Feld[ax][ay];
8263 static int xy[4][2] =
8271 for (i = 0; i < NUM_DIRECTIONS; i++)
8273 int x = ax + xy[i][0];
8274 int y = ay + xy[i][1];
8276 if (!IN_LEV_FIELD(x, y))
8279 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8280 group_nr = AmoebaNr[x][y];
8286 void AmoebenVereinigen(int ax, int ay)
8288 int i, x, y, xx, yy;
8289 int new_group_nr = AmoebaNr[ax][ay];
8290 static int xy[4][2] =
8298 if (new_group_nr == 0)
8301 for (i = 0; i < NUM_DIRECTIONS; i++)
8306 if (!IN_LEV_FIELD(x, y))
8309 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8310 Feld[x][y] == EL_BD_AMOEBA ||
8311 Feld[x][y] == EL_AMOEBA_DEAD) &&
8312 AmoebaNr[x][y] != new_group_nr)
8314 int old_group_nr = AmoebaNr[x][y];
8316 if (old_group_nr == 0)
8319 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8320 AmoebaCnt[old_group_nr] = 0;
8321 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8322 AmoebaCnt2[old_group_nr] = 0;
8324 SCAN_PLAYFIELD(xx, yy)
8326 if (AmoebaNr[xx][yy] == old_group_nr)
8327 AmoebaNr[xx][yy] = new_group_nr;
8333 void AmoebeUmwandeln(int ax, int ay)
8337 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8339 int group_nr = AmoebaNr[ax][ay];
8344 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8345 printf("AmoebeUmwandeln(): This should never happen!\n");
8350 SCAN_PLAYFIELD(x, y)
8352 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8355 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8359 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8360 SND_AMOEBA_TURNING_TO_GEM :
8361 SND_AMOEBA_TURNING_TO_ROCK));
8366 static int xy[4][2] =
8374 for (i = 0; i < NUM_DIRECTIONS; i++)
8379 if (!IN_LEV_FIELD(x, y))
8382 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8384 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8385 SND_AMOEBA_TURNING_TO_GEM :
8386 SND_AMOEBA_TURNING_TO_ROCK));
8393 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8396 int group_nr = AmoebaNr[ax][ay];
8397 boolean done = FALSE;
8402 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8403 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8408 SCAN_PLAYFIELD(x, y)
8410 if (AmoebaNr[x][y] == group_nr &&
8411 (Feld[x][y] == EL_AMOEBA_DEAD ||
8412 Feld[x][y] == EL_BD_AMOEBA ||
8413 Feld[x][y] == EL_AMOEBA_GROWING))
8416 Feld[x][y] = new_element;
8417 InitField(x, y, FALSE);
8418 TEST_DrawLevelField(x, y);
8424 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8425 SND_BD_AMOEBA_TURNING_TO_ROCK :
8426 SND_BD_AMOEBA_TURNING_TO_GEM));
8429 void AmoebeWaechst(int x, int y)
8431 static unsigned int sound_delay = 0;
8432 static unsigned int sound_delay_value = 0;
8434 if (!MovDelay[x][y]) /* start new growing cycle */
8438 if (DelayReached(&sound_delay, sound_delay_value))
8440 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8441 sound_delay_value = 30;
8445 if (MovDelay[x][y]) /* wait some time before growing bigger */
8448 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8450 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8451 6 - MovDelay[x][y]);
8453 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8456 if (!MovDelay[x][y])
8458 Feld[x][y] = Store[x][y];
8460 TEST_DrawLevelField(x, y);
8465 void AmoebaDisappearing(int x, int y)
8467 static unsigned int sound_delay = 0;
8468 static unsigned int sound_delay_value = 0;
8470 if (!MovDelay[x][y]) /* start new shrinking cycle */
8474 if (DelayReached(&sound_delay, sound_delay_value))
8475 sound_delay_value = 30;
8478 if (MovDelay[x][y]) /* wait some time before shrinking */
8481 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8483 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8484 6 - MovDelay[x][y]);
8486 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8489 if (!MovDelay[x][y])
8491 Feld[x][y] = EL_EMPTY;
8492 TEST_DrawLevelField(x, y);
8494 /* don't let mole enter this field in this cycle;
8495 (give priority to objects falling to this field from above) */
8501 void AmoebeAbleger(int ax, int ay)
8504 int element = Feld[ax][ay];
8505 int graphic = el2img(element);
8506 int newax = ax, neway = ay;
8507 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8508 static int xy[4][2] =
8516 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8518 Feld[ax][ay] = EL_AMOEBA_DEAD;
8519 TEST_DrawLevelField(ax, ay);
8523 if (IS_ANIMATED(graphic))
8524 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8526 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8527 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8529 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8532 if (MovDelay[ax][ay])
8536 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8539 int x = ax + xy[start][0];
8540 int y = ay + xy[start][1];
8542 if (!IN_LEV_FIELD(x, y))
8545 if (IS_FREE(x, y) ||
8546 CAN_GROW_INTO(Feld[x][y]) ||
8547 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8548 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8554 if (newax == ax && neway == ay)
8557 else /* normal or "filled" (BD style) amoeba */
8560 boolean waiting_for_player = FALSE;
8562 for (i = 0; i < NUM_DIRECTIONS; i++)
8564 int j = (start + i) % 4;
8565 int x = ax + xy[j][0];
8566 int y = ay + xy[j][1];
8568 if (!IN_LEV_FIELD(x, y))
8571 if (IS_FREE(x, y) ||
8572 CAN_GROW_INTO(Feld[x][y]) ||
8573 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8574 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8580 else if (IS_PLAYER(x, y))
8581 waiting_for_player = TRUE;
8584 if (newax == ax && neway == ay) /* amoeba cannot grow */
8586 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8588 Feld[ax][ay] = EL_AMOEBA_DEAD;
8589 TEST_DrawLevelField(ax, ay);
8590 AmoebaCnt[AmoebaNr[ax][ay]]--;
8592 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8594 if (element == EL_AMOEBA_FULL)
8595 AmoebeUmwandeln(ax, ay);
8596 else if (element == EL_BD_AMOEBA)
8597 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8602 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8604 /* amoeba gets larger by growing in some direction */
8606 int new_group_nr = AmoebaNr[ax][ay];
8609 if (new_group_nr == 0)
8611 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8612 printf("AmoebeAbleger(): This should never happen!\n");
8617 AmoebaNr[newax][neway] = new_group_nr;
8618 AmoebaCnt[new_group_nr]++;
8619 AmoebaCnt2[new_group_nr]++;
8621 /* if amoeba touches other amoeba(s) after growing, unify them */
8622 AmoebenVereinigen(newax, neway);
8624 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8626 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8632 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8633 (neway == lev_fieldy - 1 && newax != ax))
8635 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8636 Store[newax][neway] = element;
8638 else if (neway == ay || element == EL_EMC_DRIPPER)
8640 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8642 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8646 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8647 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8648 Store[ax][ay] = EL_AMOEBA_DROP;
8649 ContinueMoving(ax, ay);
8653 TEST_DrawLevelField(newax, neway);
8656 void Life(int ax, int ay)
8660 int element = Feld[ax][ay];
8661 int graphic = el2img(element);
8662 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8664 boolean changed = FALSE;
8666 if (IS_ANIMATED(graphic))
8667 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8672 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8673 MovDelay[ax][ay] = life_time;
8675 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8678 if (MovDelay[ax][ay])
8682 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8684 int xx = ax+x1, yy = ay+y1;
8687 if (!IN_LEV_FIELD(xx, yy))
8690 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8692 int x = xx+x2, y = yy+y2;
8694 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8697 if (((Feld[x][y] == element ||
8698 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8700 (IS_FREE(x, y) && Stop[x][y]))
8704 if (xx == ax && yy == ay) /* field in the middle */
8706 if (nachbarn < life_parameter[0] ||
8707 nachbarn > life_parameter[1])
8709 Feld[xx][yy] = EL_EMPTY;
8711 TEST_DrawLevelField(xx, yy);
8712 Stop[xx][yy] = TRUE;
8716 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8717 { /* free border field */
8718 if (nachbarn >= life_parameter[2] &&
8719 nachbarn <= life_parameter[3])
8721 Feld[xx][yy] = element;
8722 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8724 TEST_DrawLevelField(xx, yy);
8725 Stop[xx][yy] = TRUE;
8732 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8733 SND_GAME_OF_LIFE_GROWING);
8736 static void InitRobotWheel(int x, int y)
8738 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8741 static void RunRobotWheel(int x, int y)
8743 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8746 static void StopRobotWheel(int x, int y)
8748 if (ZX == x && ZY == y)
8752 game.robot_wheel_active = FALSE;
8756 static void InitTimegateWheel(int x, int y)
8758 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8761 static void RunTimegateWheel(int x, int y)
8763 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8766 static void InitMagicBallDelay(int x, int y)
8768 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8771 static void ActivateMagicBall(int bx, int by)
8775 if (level.ball_random)
8777 int pos_border = RND(8); /* select one of the eight border elements */
8778 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8779 int xx = pos_content % 3;
8780 int yy = pos_content / 3;
8785 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8786 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8790 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8792 int xx = x - bx + 1;
8793 int yy = y - by + 1;
8795 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8796 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8800 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8803 void CheckExit(int x, int y)
8805 if (local_player->gems_still_needed > 0 ||
8806 local_player->sokobanfields_still_needed > 0 ||
8807 local_player->lights_still_needed > 0)
8809 int element = Feld[x][y];
8810 int graphic = el2img(element);
8812 if (IS_ANIMATED(graphic))
8813 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8818 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8821 Feld[x][y] = EL_EXIT_OPENING;
8823 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8826 void CheckExitEM(int x, int y)
8828 if (local_player->gems_still_needed > 0 ||
8829 local_player->sokobanfields_still_needed > 0 ||
8830 local_player->lights_still_needed > 0)
8832 int element = Feld[x][y];
8833 int graphic = el2img(element);
8835 if (IS_ANIMATED(graphic))
8836 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8841 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8844 Feld[x][y] = EL_EM_EXIT_OPENING;
8846 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8849 void CheckExitSteel(int x, int y)
8851 if (local_player->gems_still_needed > 0 ||
8852 local_player->sokobanfields_still_needed > 0 ||
8853 local_player->lights_still_needed > 0)
8855 int element = Feld[x][y];
8856 int graphic = el2img(element);
8858 if (IS_ANIMATED(graphic))
8859 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8864 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8867 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8869 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8872 void CheckExitSteelEM(int x, int y)
8874 if (local_player->gems_still_needed > 0 ||
8875 local_player->sokobanfields_still_needed > 0 ||
8876 local_player->lights_still_needed > 0)
8878 int element = Feld[x][y];
8879 int graphic = el2img(element);
8881 if (IS_ANIMATED(graphic))
8882 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8887 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8890 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8892 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8895 void CheckExitSP(int x, int y)
8897 if (local_player->gems_still_needed > 0)
8899 int element = Feld[x][y];
8900 int graphic = el2img(element);
8902 if (IS_ANIMATED(graphic))
8903 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8908 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8911 Feld[x][y] = EL_SP_EXIT_OPENING;
8913 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8916 static void CloseAllOpenTimegates()
8920 SCAN_PLAYFIELD(x, y)
8922 int element = Feld[x][y];
8924 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8926 Feld[x][y] = EL_TIMEGATE_CLOSING;
8928 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8933 void DrawTwinkleOnField(int x, int y)
8935 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8938 if (Feld[x][y] == EL_BD_DIAMOND)
8941 if (MovDelay[x][y] == 0) /* next animation frame */
8942 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8944 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8948 DrawLevelElementAnimation(x, y, Feld[x][y]);
8950 if (MovDelay[x][y] != 0)
8952 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8953 10 - MovDelay[x][y]);
8955 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8960 void MauerWaechst(int x, int y)
8964 if (!MovDelay[x][y]) /* next animation frame */
8965 MovDelay[x][y] = 3 * delay;
8967 if (MovDelay[x][y]) /* wait some time before next frame */
8971 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8973 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8974 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8976 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8979 if (!MovDelay[x][y])
8981 if (MovDir[x][y] == MV_LEFT)
8983 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8984 TEST_DrawLevelField(x - 1, y);
8986 else if (MovDir[x][y] == MV_RIGHT)
8988 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8989 TEST_DrawLevelField(x + 1, y);
8991 else if (MovDir[x][y] == MV_UP)
8993 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8994 TEST_DrawLevelField(x, y - 1);
8998 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8999 TEST_DrawLevelField(x, y + 1);
9002 Feld[x][y] = Store[x][y];
9004 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9005 TEST_DrawLevelField(x, y);
9010 void MauerAbleger(int ax, int ay)
9012 int element = Feld[ax][ay];
9013 int graphic = el2img(element);
9014 boolean oben_frei = FALSE, unten_frei = FALSE;
9015 boolean links_frei = FALSE, rechts_frei = FALSE;
9016 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9017 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9018 boolean new_wall = FALSE;
9020 if (IS_ANIMATED(graphic))
9021 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9023 if (!MovDelay[ax][ay]) /* start building new wall */
9024 MovDelay[ax][ay] = 6;
9026 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9029 if (MovDelay[ax][ay])
9033 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9035 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9037 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9039 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9042 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9043 element == EL_EXPANDABLE_WALL_ANY)
9047 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9048 Store[ax][ay-1] = element;
9049 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9050 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9051 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9052 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9057 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9058 Store[ax][ay+1] = element;
9059 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9060 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9061 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9062 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9067 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9068 element == EL_EXPANDABLE_WALL_ANY ||
9069 element == EL_EXPANDABLE_WALL ||
9070 element == EL_BD_EXPANDABLE_WALL)
9074 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9075 Store[ax-1][ay] = element;
9076 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9077 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9078 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9079 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9085 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9086 Store[ax+1][ay] = element;
9087 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9088 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9089 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9090 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9095 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9096 TEST_DrawLevelField(ax, ay);
9098 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9100 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9101 unten_massiv = TRUE;
9102 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9103 links_massiv = TRUE;
9104 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9105 rechts_massiv = TRUE;
9107 if (((oben_massiv && unten_massiv) ||
9108 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9109 element == EL_EXPANDABLE_WALL) &&
9110 ((links_massiv && rechts_massiv) ||
9111 element == EL_EXPANDABLE_WALL_VERTICAL))
9112 Feld[ax][ay] = EL_WALL;
9115 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9118 void MauerAblegerStahl(int ax, int ay)
9120 int element = Feld[ax][ay];
9121 int graphic = el2img(element);
9122 boolean oben_frei = FALSE, unten_frei = FALSE;
9123 boolean links_frei = FALSE, rechts_frei = FALSE;
9124 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9125 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9126 boolean new_wall = FALSE;
9128 if (IS_ANIMATED(graphic))
9129 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9131 if (!MovDelay[ax][ay]) /* start building new wall */
9132 MovDelay[ax][ay] = 6;
9134 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9137 if (MovDelay[ax][ay])
9141 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9143 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9145 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9147 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9150 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9151 element == EL_EXPANDABLE_STEELWALL_ANY)
9155 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9156 Store[ax][ay-1] = element;
9157 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9158 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9159 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9160 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9165 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9166 Store[ax][ay+1] = element;
9167 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9168 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9169 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9170 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9175 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9176 element == EL_EXPANDABLE_STEELWALL_ANY)
9180 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9181 Store[ax-1][ay] = element;
9182 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9183 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9184 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9185 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9191 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9192 Store[ax+1][ay] = element;
9193 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9194 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9195 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9196 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9201 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9203 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9204 unten_massiv = TRUE;
9205 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9206 links_massiv = TRUE;
9207 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9208 rechts_massiv = TRUE;
9210 if (((oben_massiv && unten_massiv) ||
9211 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9212 ((links_massiv && rechts_massiv) ||
9213 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9214 Feld[ax][ay] = EL_STEELWALL;
9217 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9220 void CheckForDragon(int x, int y)
9223 boolean dragon_found = FALSE;
9224 static int xy[4][2] =
9232 for (i = 0; i < NUM_DIRECTIONS; i++)
9234 for (j = 0; j < 4; j++)
9236 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9238 if (IN_LEV_FIELD(xx, yy) &&
9239 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9241 if (Feld[xx][yy] == EL_DRAGON)
9242 dragon_found = TRUE;
9251 for (i = 0; i < NUM_DIRECTIONS; i++)
9253 for (j = 0; j < 3; j++)
9255 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9257 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9259 Feld[xx][yy] = EL_EMPTY;
9260 TEST_DrawLevelField(xx, yy);
9269 static void InitBuggyBase(int x, int y)
9271 int element = Feld[x][y];
9272 int activating_delay = FRAMES_PER_SECOND / 4;
9275 (element == EL_SP_BUGGY_BASE ?
9276 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9277 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9279 element == EL_SP_BUGGY_BASE_ACTIVE ?
9280 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9283 static void WarnBuggyBase(int x, int y)
9286 static int xy[4][2] =
9294 for (i = 0; i < NUM_DIRECTIONS; i++)
9296 int xx = x + xy[i][0];
9297 int yy = y + xy[i][1];
9299 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9301 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9308 static void InitTrap(int x, int y)
9310 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9313 static void ActivateTrap(int x, int y)
9315 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9318 static void ChangeActiveTrap(int x, int y)
9320 int graphic = IMG_TRAP_ACTIVE;
9322 /* if new animation frame was drawn, correct crumbled sand border */
9323 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9324 TEST_DrawLevelFieldCrumbled(x, y);
9327 static int getSpecialActionElement(int element, int number, int base_element)
9329 return (element != EL_EMPTY ? element :
9330 number != -1 ? base_element + number - 1 :
9334 static int getModifiedActionNumber(int value_old, int operator, int operand,
9335 int value_min, int value_max)
9337 int value_new = (operator == CA_MODE_SET ? operand :
9338 operator == CA_MODE_ADD ? value_old + operand :
9339 operator == CA_MODE_SUBTRACT ? value_old - operand :
9340 operator == CA_MODE_MULTIPLY ? value_old * operand :
9341 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9342 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9345 return (value_new < value_min ? value_min :
9346 value_new > value_max ? value_max :
9350 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9352 struct ElementInfo *ei = &element_info[element];
9353 struct ElementChangeInfo *change = &ei->change_page[page];
9354 int target_element = change->target_element;
9355 int action_type = change->action_type;
9356 int action_mode = change->action_mode;
9357 int action_arg = change->action_arg;
9358 int action_element = change->action_element;
9361 if (!change->has_action)
9364 /* ---------- determine action paramater values -------------------------- */
9366 int level_time_value =
9367 (level.time > 0 ? TimeLeft :
9370 int action_arg_element_raw =
9371 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9372 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9373 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9374 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9375 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9376 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9377 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9379 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9381 int action_arg_direction =
9382 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9383 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9384 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9385 change->actual_trigger_side :
9386 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9387 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9390 int action_arg_number_min =
9391 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9394 int action_arg_number_max =
9395 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9396 action_type == CA_SET_LEVEL_GEMS ? 999 :
9397 action_type == CA_SET_LEVEL_TIME ? 9999 :
9398 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9399 action_type == CA_SET_CE_VALUE ? 9999 :
9400 action_type == CA_SET_CE_SCORE ? 9999 :
9403 int action_arg_number_reset =
9404 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9405 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9406 action_type == CA_SET_LEVEL_TIME ? level.time :
9407 action_type == CA_SET_LEVEL_SCORE ? 0 :
9408 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9409 action_type == CA_SET_CE_SCORE ? 0 :
9412 int action_arg_number =
9413 (action_arg <= CA_ARG_MAX ? action_arg :
9414 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9415 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9416 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9417 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9418 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9419 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9420 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9421 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9422 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9423 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9424 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9425 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9426 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9427 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9428 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9429 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9430 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9431 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9432 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9433 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9434 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9437 int action_arg_number_old =
9438 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9439 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9440 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9441 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9442 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9445 int action_arg_number_new =
9446 getModifiedActionNumber(action_arg_number_old,
9447 action_mode, action_arg_number,
9448 action_arg_number_min, action_arg_number_max);
9450 int trigger_player_bits =
9451 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9452 change->actual_trigger_player_bits : change->trigger_player);
9454 int action_arg_player_bits =
9455 (action_arg >= CA_ARG_PLAYER_1 &&
9456 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9457 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9458 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9461 /* ---------- execute action -------------------------------------------- */
9463 switch (action_type)
9470 /* ---------- level actions ------------------------------------------- */
9472 case CA_RESTART_LEVEL:
9474 game.restart_level = TRUE;
9479 case CA_SHOW_ENVELOPE:
9481 int element = getSpecialActionElement(action_arg_element,
9482 action_arg_number, EL_ENVELOPE_1);
9484 if (IS_ENVELOPE(element))
9485 local_player->show_envelope = element;
9490 case CA_SET_LEVEL_TIME:
9492 if (level.time > 0) /* only modify limited time value */
9494 TimeLeft = action_arg_number_new;
9496 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9498 DisplayGameControlValues();
9500 if (!TimeLeft && setup.time_limit)
9501 for (i = 0; i < MAX_PLAYERS; i++)
9502 KillPlayer(&stored_player[i]);
9508 case CA_SET_LEVEL_SCORE:
9510 local_player->score = action_arg_number_new;
9512 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9514 DisplayGameControlValues();
9519 case CA_SET_LEVEL_GEMS:
9521 local_player->gems_still_needed = action_arg_number_new;
9523 game.snapshot.collected_item = TRUE;
9525 game_panel_controls[GAME_PANEL_GEMS].value =
9526 local_player->gems_still_needed;
9528 DisplayGameControlValues();
9533 case CA_SET_LEVEL_WIND:
9535 game.wind_direction = action_arg_direction;
9540 case CA_SET_LEVEL_RANDOM_SEED:
9542 /* ensure that setting a new random seed while playing is predictable */
9543 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9548 /* ---------- player actions ------------------------------------------ */
9550 case CA_MOVE_PLAYER:
9552 /* automatically move to the next field in specified direction */
9553 for (i = 0; i < MAX_PLAYERS; i++)
9554 if (trigger_player_bits & (1 << i))
9555 stored_player[i].programmed_action = action_arg_direction;
9560 case CA_EXIT_PLAYER:
9562 for (i = 0; i < MAX_PLAYERS; i++)
9563 if (action_arg_player_bits & (1 << i))
9564 PlayerWins(&stored_player[i]);
9569 case CA_KILL_PLAYER:
9571 for (i = 0; i < MAX_PLAYERS; i++)
9572 if (action_arg_player_bits & (1 << i))
9573 KillPlayer(&stored_player[i]);
9578 case CA_SET_PLAYER_KEYS:
9580 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9581 int element = getSpecialActionElement(action_arg_element,
9582 action_arg_number, EL_KEY_1);
9584 if (IS_KEY(element))
9586 for (i = 0; i < MAX_PLAYERS; i++)
9588 if (trigger_player_bits & (1 << i))
9590 stored_player[i].key[KEY_NR(element)] = key_state;
9592 DrawGameDoorValues();
9600 case CA_SET_PLAYER_SPEED:
9602 for (i = 0; i < MAX_PLAYERS; i++)
9604 if (trigger_player_bits & (1 << i))
9606 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9608 if (action_arg == CA_ARG_SPEED_FASTER &&
9609 stored_player[i].cannot_move)
9611 action_arg_number = STEPSIZE_VERY_SLOW;
9613 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9614 action_arg == CA_ARG_SPEED_FASTER)
9616 action_arg_number = 2;
9617 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9620 else if (action_arg == CA_ARG_NUMBER_RESET)
9622 action_arg_number = level.initial_player_stepsize[i];
9626 getModifiedActionNumber(move_stepsize,
9629 action_arg_number_min,
9630 action_arg_number_max);
9632 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9639 case CA_SET_PLAYER_SHIELD:
9641 for (i = 0; i < MAX_PLAYERS; i++)
9643 if (trigger_player_bits & (1 << i))
9645 if (action_arg == CA_ARG_SHIELD_OFF)
9647 stored_player[i].shield_normal_time_left = 0;
9648 stored_player[i].shield_deadly_time_left = 0;
9650 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9652 stored_player[i].shield_normal_time_left = 999999;
9654 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9656 stored_player[i].shield_normal_time_left = 999999;
9657 stored_player[i].shield_deadly_time_left = 999999;
9665 case CA_SET_PLAYER_GRAVITY:
9667 for (i = 0; i < MAX_PLAYERS; i++)
9669 if (trigger_player_bits & (1 << i))
9671 stored_player[i].gravity =
9672 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9673 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9674 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9675 stored_player[i].gravity);
9682 case CA_SET_PLAYER_ARTWORK:
9684 for (i = 0; i < MAX_PLAYERS; i++)
9686 if (trigger_player_bits & (1 << i))
9688 int artwork_element = action_arg_element;
9690 if (action_arg == CA_ARG_ELEMENT_RESET)
9692 (level.use_artwork_element[i] ? level.artwork_element[i] :
9693 stored_player[i].element_nr);
9695 if (stored_player[i].artwork_element != artwork_element)
9696 stored_player[i].Frame = 0;
9698 stored_player[i].artwork_element = artwork_element;
9700 SetPlayerWaiting(&stored_player[i], FALSE);
9702 /* set number of special actions for bored and sleeping animation */
9703 stored_player[i].num_special_action_bored =
9704 get_num_special_action(artwork_element,
9705 ACTION_BORING_1, ACTION_BORING_LAST);
9706 stored_player[i].num_special_action_sleeping =
9707 get_num_special_action(artwork_element,
9708 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9715 case CA_SET_PLAYER_INVENTORY:
9717 for (i = 0; i < MAX_PLAYERS; i++)
9719 struct PlayerInfo *player = &stored_player[i];
9722 if (trigger_player_bits & (1 << i))
9724 int inventory_element = action_arg_element;
9726 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9727 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9728 action_arg == CA_ARG_ELEMENT_ACTION)
9730 int element = inventory_element;
9731 int collect_count = element_info[element].collect_count_initial;
9733 if (!IS_CUSTOM_ELEMENT(element))
9736 if (collect_count == 0)
9737 player->inventory_infinite_element = element;
9739 for (k = 0; k < collect_count; k++)
9740 if (player->inventory_size < MAX_INVENTORY_SIZE)
9741 player->inventory_element[player->inventory_size++] =
9744 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9745 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9746 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9748 if (player->inventory_infinite_element != EL_UNDEFINED &&
9749 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9750 action_arg_element_raw))
9751 player->inventory_infinite_element = EL_UNDEFINED;
9753 for (k = 0, j = 0; j < player->inventory_size; j++)
9755 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9756 action_arg_element_raw))
9757 player->inventory_element[k++] = player->inventory_element[j];
9760 player->inventory_size = k;
9762 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9764 if (player->inventory_size > 0)
9766 for (j = 0; j < player->inventory_size - 1; j++)
9767 player->inventory_element[j] = player->inventory_element[j + 1];
9769 player->inventory_size--;
9772 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9774 if (player->inventory_size > 0)
9775 player->inventory_size--;
9777 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9779 player->inventory_infinite_element = EL_UNDEFINED;
9780 player->inventory_size = 0;
9782 else if (action_arg == CA_ARG_INVENTORY_RESET)
9784 player->inventory_infinite_element = EL_UNDEFINED;
9785 player->inventory_size = 0;
9787 if (level.use_initial_inventory[i])
9789 for (j = 0; j < level.initial_inventory_size[i]; j++)
9791 int element = level.initial_inventory_content[i][j];
9792 int collect_count = element_info[element].collect_count_initial;
9794 if (!IS_CUSTOM_ELEMENT(element))
9797 if (collect_count == 0)
9798 player->inventory_infinite_element = element;
9800 for (k = 0; k < collect_count; k++)
9801 if (player->inventory_size < MAX_INVENTORY_SIZE)
9802 player->inventory_element[player->inventory_size++] =
9813 /* ---------- CE actions ---------------------------------------------- */
9815 case CA_SET_CE_VALUE:
9817 int last_ce_value = CustomValue[x][y];
9819 CustomValue[x][y] = action_arg_number_new;
9821 if (CustomValue[x][y] != last_ce_value)
9823 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9824 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9826 if (CustomValue[x][y] == 0)
9828 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9829 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9836 case CA_SET_CE_SCORE:
9838 int last_ce_score = ei->collect_score;
9840 ei->collect_score = action_arg_number_new;
9842 if (ei->collect_score != last_ce_score)
9844 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9845 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9847 if (ei->collect_score == 0)
9851 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9852 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9855 This is a very special case that seems to be a mixture between
9856 CheckElementChange() and CheckTriggeredElementChange(): while
9857 the first one only affects single elements that are triggered
9858 directly, the second one affects multiple elements in the playfield
9859 that are triggered indirectly by another element. This is a third
9860 case: Changing the CE score always affects multiple identical CEs,
9861 so every affected CE must be checked, not only the single CE for
9862 which the CE score was changed in the first place (as every instance
9863 of that CE shares the same CE score, and therefore also can change)!
9865 SCAN_PLAYFIELD(xx, yy)
9867 if (Feld[xx][yy] == element)
9868 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9869 CE_SCORE_GETS_ZERO);
9877 case CA_SET_CE_ARTWORK:
9879 int artwork_element = action_arg_element;
9880 boolean reset_frame = FALSE;
9883 if (action_arg == CA_ARG_ELEMENT_RESET)
9884 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9887 if (ei->gfx_element != artwork_element)
9890 ei->gfx_element = artwork_element;
9892 SCAN_PLAYFIELD(xx, yy)
9894 if (Feld[xx][yy] == element)
9898 ResetGfxAnimation(xx, yy);
9899 ResetRandomAnimationValue(xx, yy);
9902 TEST_DrawLevelField(xx, yy);
9909 /* ---------- engine actions ------------------------------------------ */
9911 case CA_SET_ENGINE_SCAN_MODE:
9913 InitPlayfieldScanMode(action_arg);
9923 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9925 int old_element = Feld[x][y];
9926 int new_element = GetElementFromGroupElement(element);
9927 int previous_move_direction = MovDir[x][y];
9928 int last_ce_value = CustomValue[x][y];
9929 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9930 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9931 boolean add_player_onto_element = (new_element_is_player &&
9932 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9933 IS_WALKABLE(old_element));
9935 if (!add_player_onto_element)
9937 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9938 RemoveMovingField(x, y);
9942 Feld[x][y] = new_element;
9944 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9945 MovDir[x][y] = previous_move_direction;
9947 if (element_info[new_element].use_last_ce_value)
9948 CustomValue[x][y] = last_ce_value;
9950 InitField_WithBug1(x, y, FALSE);
9952 new_element = Feld[x][y]; /* element may have changed */
9954 ResetGfxAnimation(x, y);
9955 ResetRandomAnimationValue(x, y);
9957 TEST_DrawLevelField(x, y);
9959 if (GFX_CRUMBLED(new_element))
9960 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9963 /* check if element under the player changes from accessible to unaccessible
9964 (needed for special case of dropping element which then changes) */
9965 /* (must be checked after creating new element for walkable group elements) */
9966 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9967 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9974 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9975 if (new_element_is_player)
9976 RelocatePlayer(x, y, new_element);
9979 ChangeCount[x][y]++; /* count number of changes in the same frame */
9981 TestIfBadThingTouchesPlayer(x, y);
9982 TestIfPlayerTouchesCustomElement(x, y);
9983 TestIfElementTouchesCustomElement(x, y);
9986 static void CreateField(int x, int y, int element)
9988 CreateFieldExt(x, y, element, FALSE);
9991 static void CreateElementFromChange(int x, int y, int element)
9993 element = GET_VALID_RUNTIME_ELEMENT(element);
9995 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9997 int old_element = Feld[x][y];
9999 /* prevent changed element from moving in same engine frame
10000 unless both old and new element can either fall or move */
10001 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10002 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10006 CreateFieldExt(x, y, element, TRUE);
10009 static boolean ChangeElement(int x, int y, int element, int page)
10011 struct ElementInfo *ei = &element_info[element];
10012 struct ElementChangeInfo *change = &ei->change_page[page];
10013 int ce_value = CustomValue[x][y];
10014 int ce_score = ei->collect_score;
10015 int target_element;
10016 int old_element = Feld[x][y];
10018 /* always use default change event to prevent running into a loop */
10019 if (ChangeEvent[x][y] == -1)
10020 ChangeEvent[x][y] = CE_DELAY;
10022 if (ChangeEvent[x][y] == CE_DELAY)
10024 /* reset actual trigger element, trigger player and action element */
10025 change->actual_trigger_element = EL_EMPTY;
10026 change->actual_trigger_player = EL_EMPTY;
10027 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10028 change->actual_trigger_side = CH_SIDE_NONE;
10029 change->actual_trigger_ce_value = 0;
10030 change->actual_trigger_ce_score = 0;
10033 /* do not change elements more than a specified maximum number of changes */
10034 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10037 ChangeCount[x][y]++; /* count number of changes in the same frame */
10039 if (change->explode)
10046 if (change->use_target_content)
10048 boolean complete_replace = TRUE;
10049 boolean can_replace[3][3];
10052 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10055 boolean is_walkable;
10056 boolean is_diggable;
10057 boolean is_collectible;
10058 boolean is_removable;
10059 boolean is_destructible;
10060 int ex = x + xx - 1;
10061 int ey = y + yy - 1;
10062 int content_element = change->target_content.e[xx][yy];
10065 can_replace[xx][yy] = TRUE;
10067 if (ex == x && ey == y) /* do not check changing element itself */
10070 if (content_element == EL_EMPTY_SPACE)
10072 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10077 if (!IN_LEV_FIELD(ex, ey))
10079 can_replace[xx][yy] = FALSE;
10080 complete_replace = FALSE;
10087 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10088 e = MovingOrBlocked2Element(ex, ey);
10090 is_empty = (IS_FREE(ex, ey) ||
10091 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10093 is_walkable = (is_empty || IS_WALKABLE(e));
10094 is_diggable = (is_empty || IS_DIGGABLE(e));
10095 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10096 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10097 is_removable = (is_diggable || is_collectible);
10099 can_replace[xx][yy] =
10100 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10101 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10102 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10103 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10104 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10105 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10106 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10108 if (!can_replace[xx][yy])
10109 complete_replace = FALSE;
10112 if (!change->only_if_complete || complete_replace)
10114 boolean something_has_changed = FALSE;
10116 if (change->only_if_complete && change->use_random_replace &&
10117 RND(100) < change->random_percentage)
10120 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10122 int ex = x + xx - 1;
10123 int ey = y + yy - 1;
10124 int content_element;
10126 if (can_replace[xx][yy] && (!change->use_random_replace ||
10127 RND(100) < change->random_percentage))
10129 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10130 RemoveMovingField(ex, ey);
10132 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10134 content_element = change->target_content.e[xx][yy];
10135 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10136 ce_value, ce_score);
10138 CreateElementFromChange(ex, ey, target_element);
10140 something_has_changed = TRUE;
10142 /* for symmetry reasons, freeze newly created border elements */
10143 if (ex != x || ey != y)
10144 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10148 if (something_has_changed)
10150 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10151 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10157 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10158 ce_value, ce_score);
10160 if (element == EL_DIAGONAL_GROWING ||
10161 element == EL_DIAGONAL_SHRINKING)
10163 target_element = Store[x][y];
10165 Store[x][y] = EL_EMPTY;
10168 CreateElementFromChange(x, y, target_element);
10170 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10171 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10174 /* this uses direct change before indirect change */
10175 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10180 static void HandleElementChange(int x, int y, int page)
10182 int element = MovingOrBlocked2Element(x, y);
10183 struct ElementInfo *ei = &element_info[element];
10184 struct ElementChangeInfo *change = &ei->change_page[page];
10185 boolean handle_action_before_change = FALSE;
10188 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10189 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10192 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10193 x, y, element, element_info[element].token_name);
10194 printf("HandleElementChange(): This should never happen!\n");
10199 /* this can happen with classic bombs on walkable, changing elements */
10200 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10205 if (ChangeDelay[x][y] == 0) /* initialize element change */
10207 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10209 if (change->can_change)
10211 /* !!! not clear why graphic animation should be reset at all here !!! */
10212 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10213 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10216 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10218 When using an animation frame delay of 1 (this only happens with
10219 "sp_zonk.moving.left/right" in the classic graphics), the default
10220 (non-moving) animation shows wrong animation frames (while the
10221 moving animation, like "sp_zonk.moving.left/right", is correct,
10222 so this graphical bug never shows up with the classic graphics).
10223 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10224 be drawn instead of the correct frames 0,1,2,3. This is caused by
10225 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10226 an element change: First when the change delay ("ChangeDelay[][]")
10227 counter has reached zero after decrementing, then a second time in
10228 the next frame (after "GfxFrame[][]" was already incremented) when
10229 "ChangeDelay[][]" is reset to the initial delay value again.
10231 This causes frame 0 to be drawn twice, while the last frame won't
10232 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10234 As some animations may already be cleverly designed around this bug
10235 (at least the "Snake Bite" snake tail animation does this), it cannot
10236 simply be fixed here without breaking such existing animations.
10237 Unfortunately, it cannot easily be detected if a graphics set was
10238 designed "before" or "after" the bug was fixed. As a workaround,
10239 a new graphics set option "game.graphics_engine_version" was added
10240 to be able to specify the game's major release version for which the
10241 graphics set was designed, which can then be used to decide if the
10242 bugfix should be used (version 4 and above) or not (version 3 or
10243 below, or if no version was specified at all, as with old sets).
10245 (The wrong/fixed animation frames can be tested with the test level set
10246 "test_gfxframe" and level "000", which contains a specially prepared
10247 custom element at level position (x/y) == (11/9) which uses the zonk
10248 animation mentioned above. Using "game.graphics_engine_version: 4"
10249 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10250 This can also be seen from the debug output for this test element.)
10253 /* when a custom element is about to change (for example by change delay),
10254 do not reset graphic animation when the custom element is moving */
10255 if (game.graphics_engine_version < 4 &&
10258 ResetGfxAnimation(x, y);
10259 ResetRandomAnimationValue(x, y);
10262 if (change->pre_change_function)
10263 change->pre_change_function(x, y);
10267 ChangeDelay[x][y]--;
10269 if (ChangeDelay[x][y] != 0) /* continue element change */
10271 if (change->can_change)
10273 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10275 if (IS_ANIMATED(graphic))
10276 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10278 if (change->change_function)
10279 change->change_function(x, y);
10282 else /* finish element change */
10284 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10286 page = ChangePage[x][y];
10287 ChangePage[x][y] = -1;
10289 change = &ei->change_page[page];
10292 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10294 ChangeDelay[x][y] = 1; /* try change after next move step */
10295 ChangePage[x][y] = page; /* remember page to use for change */
10300 /* special case: set new level random seed before changing element */
10301 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10302 handle_action_before_change = TRUE;
10304 if (change->has_action && handle_action_before_change)
10305 ExecuteCustomElementAction(x, y, element, page);
10307 if (change->can_change)
10309 if (ChangeElement(x, y, element, page))
10311 if (change->post_change_function)
10312 change->post_change_function(x, y);
10316 if (change->has_action && !handle_action_before_change)
10317 ExecuteCustomElementAction(x, y, element, page);
10321 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10322 int trigger_element,
10324 int trigger_player,
10328 boolean change_done_any = FALSE;
10329 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10332 if (!(trigger_events[trigger_element][trigger_event]))
10335 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10337 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10339 int element = EL_CUSTOM_START + i;
10340 boolean change_done = FALSE;
10343 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10344 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10347 for (p = 0; p < element_info[element].num_change_pages; p++)
10349 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10351 if (change->can_change_or_has_action &&
10352 change->has_event[trigger_event] &&
10353 change->trigger_side & trigger_side &&
10354 change->trigger_player & trigger_player &&
10355 change->trigger_page & trigger_page_bits &&
10356 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10358 change->actual_trigger_element = trigger_element;
10359 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10360 change->actual_trigger_player_bits = trigger_player;
10361 change->actual_trigger_side = trigger_side;
10362 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10363 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10365 if ((change->can_change && !change_done) || change->has_action)
10369 SCAN_PLAYFIELD(x, y)
10371 if (Feld[x][y] == element)
10373 if (change->can_change && !change_done)
10375 /* if element already changed in this frame, not only prevent
10376 another element change (checked in ChangeElement()), but
10377 also prevent additional element actions for this element */
10379 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10380 !level.use_action_after_change_bug)
10383 ChangeDelay[x][y] = 1;
10384 ChangeEvent[x][y] = trigger_event;
10386 HandleElementChange(x, y, p);
10388 else if (change->has_action)
10390 /* if element already changed in this frame, not only prevent
10391 another element change (checked in ChangeElement()), but
10392 also prevent additional element actions for this element */
10394 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10395 !level.use_action_after_change_bug)
10398 ExecuteCustomElementAction(x, y, element, p);
10399 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10404 if (change->can_change)
10406 change_done = TRUE;
10407 change_done_any = TRUE;
10414 RECURSION_LOOP_DETECTION_END();
10416 return change_done_any;
10419 static boolean CheckElementChangeExt(int x, int y,
10421 int trigger_element,
10423 int trigger_player,
10426 boolean change_done = FALSE;
10429 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10430 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10433 if (Feld[x][y] == EL_BLOCKED)
10435 Blocked2Moving(x, y, &x, &y);
10436 element = Feld[x][y];
10439 /* check if element has already changed or is about to change after moving */
10440 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10441 Feld[x][y] != element) ||
10443 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10444 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10445 ChangePage[x][y] != -1)))
10448 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10450 for (p = 0; p < element_info[element].num_change_pages; p++)
10452 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10454 /* check trigger element for all events where the element that is checked
10455 for changing interacts with a directly adjacent element -- this is
10456 different to element changes that affect other elements to change on the
10457 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10458 boolean check_trigger_element =
10459 (trigger_event == CE_TOUCHING_X ||
10460 trigger_event == CE_HITTING_X ||
10461 trigger_event == CE_HIT_BY_X ||
10462 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10464 if (change->can_change_or_has_action &&
10465 change->has_event[trigger_event] &&
10466 change->trigger_side & trigger_side &&
10467 change->trigger_player & trigger_player &&
10468 (!check_trigger_element ||
10469 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10471 change->actual_trigger_element = trigger_element;
10472 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10473 change->actual_trigger_player_bits = trigger_player;
10474 change->actual_trigger_side = trigger_side;
10475 change->actual_trigger_ce_value = CustomValue[x][y];
10476 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10478 /* special case: trigger element not at (x,y) position for some events */
10479 if (check_trigger_element)
10491 { 0, 0 }, { 0, 0 }, { 0, 0 },
10495 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10496 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10498 change->actual_trigger_ce_value = CustomValue[xx][yy];
10499 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10502 if (change->can_change && !change_done)
10504 ChangeDelay[x][y] = 1;
10505 ChangeEvent[x][y] = trigger_event;
10507 HandleElementChange(x, y, p);
10509 change_done = TRUE;
10511 else if (change->has_action)
10513 ExecuteCustomElementAction(x, y, element, p);
10514 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10519 RECURSION_LOOP_DETECTION_END();
10521 return change_done;
10524 static void PlayPlayerSound(struct PlayerInfo *player)
10526 int jx = player->jx, jy = player->jy;
10527 int sound_element = player->artwork_element;
10528 int last_action = player->last_action_waiting;
10529 int action = player->action_waiting;
10531 if (player->is_waiting)
10533 if (action != last_action)
10534 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10536 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10540 if (action != last_action)
10541 StopSound(element_info[sound_element].sound[last_action]);
10543 if (last_action == ACTION_SLEEPING)
10544 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10548 static void PlayAllPlayersSound()
10552 for (i = 0; i < MAX_PLAYERS; i++)
10553 if (stored_player[i].active)
10554 PlayPlayerSound(&stored_player[i]);
10557 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10559 boolean last_waiting = player->is_waiting;
10560 int move_dir = player->MovDir;
10562 player->dir_waiting = move_dir;
10563 player->last_action_waiting = player->action_waiting;
10567 if (!last_waiting) /* not waiting -> waiting */
10569 player->is_waiting = TRUE;
10571 player->frame_counter_bored =
10573 game.player_boring_delay_fixed +
10574 GetSimpleRandom(game.player_boring_delay_random);
10575 player->frame_counter_sleeping =
10577 game.player_sleeping_delay_fixed +
10578 GetSimpleRandom(game.player_sleeping_delay_random);
10580 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10583 if (game.player_sleeping_delay_fixed +
10584 game.player_sleeping_delay_random > 0 &&
10585 player->anim_delay_counter == 0 &&
10586 player->post_delay_counter == 0 &&
10587 FrameCounter >= player->frame_counter_sleeping)
10588 player->is_sleeping = TRUE;
10589 else if (game.player_boring_delay_fixed +
10590 game.player_boring_delay_random > 0 &&
10591 FrameCounter >= player->frame_counter_bored)
10592 player->is_bored = TRUE;
10594 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10595 player->is_bored ? ACTION_BORING :
10598 if (player->is_sleeping && player->use_murphy)
10600 /* special case for sleeping Murphy when leaning against non-free tile */
10602 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_LEFT;
10606 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10607 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10608 !IS_MOVING(player->jx + 1, player->jy)))
10609 move_dir = MV_RIGHT;
10611 player->is_sleeping = FALSE;
10613 player->dir_waiting = move_dir;
10616 if (player->is_sleeping)
10618 if (player->num_special_action_sleeping > 0)
10620 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10622 int last_special_action = player->special_action_sleeping;
10623 int num_special_action = player->num_special_action_sleeping;
10624 int special_action =
10625 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10626 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10627 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10628 last_special_action + 1 : ACTION_SLEEPING);
10629 int special_graphic =
10630 el_act_dir2img(player->artwork_element, special_action, move_dir);
10632 player->anim_delay_counter =
10633 graphic_info[special_graphic].anim_delay_fixed +
10634 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10635 player->post_delay_counter =
10636 graphic_info[special_graphic].post_delay_fixed +
10637 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10639 player->special_action_sleeping = special_action;
10642 if (player->anim_delay_counter > 0)
10644 player->action_waiting = player->special_action_sleeping;
10645 player->anim_delay_counter--;
10647 else if (player->post_delay_counter > 0)
10649 player->post_delay_counter--;
10653 else if (player->is_bored)
10655 if (player->num_special_action_bored > 0)
10657 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10659 int special_action =
10660 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10661 int special_graphic =
10662 el_act_dir2img(player->artwork_element, special_action, move_dir);
10664 player->anim_delay_counter =
10665 graphic_info[special_graphic].anim_delay_fixed +
10666 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10667 player->post_delay_counter =
10668 graphic_info[special_graphic].post_delay_fixed +
10669 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10671 player->special_action_bored = special_action;
10674 if (player->anim_delay_counter > 0)
10676 player->action_waiting = player->special_action_bored;
10677 player->anim_delay_counter--;
10679 else if (player->post_delay_counter > 0)
10681 player->post_delay_counter--;
10686 else if (last_waiting) /* waiting -> not waiting */
10688 player->is_waiting = FALSE;
10689 player->is_bored = FALSE;
10690 player->is_sleeping = FALSE;
10692 player->frame_counter_bored = -1;
10693 player->frame_counter_sleeping = -1;
10695 player->anim_delay_counter = 0;
10696 player->post_delay_counter = 0;
10698 player->dir_waiting = player->MovDir;
10699 player->action_waiting = ACTION_DEFAULT;
10701 player->special_action_bored = ACTION_DEFAULT;
10702 player->special_action_sleeping = ACTION_DEFAULT;
10706 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10708 if ((!player->is_moving && player->was_moving) ||
10709 (player->MovPos == 0 && player->was_moving) ||
10710 (player->is_snapping && !player->was_snapping) ||
10711 (player->is_dropping && !player->was_dropping))
10713 if (!CheckSaveEngineSnapshotToList())
10716 player->was_moving = FALSE;
10717 player->was_snapping = TRUE;
10718 player->was_dropping = TRUE;
10722 if (player->is_moving)
10723 player->was_moving = TRUE;
10725 if (!player->is_snapping)
10726 player->was_snapping = FALSE;
10728 if (!player->is_dropping)
10729 player->was_dropping = FALSE;
10733 static void CheckSingleStepMode(struct PlayerInfo *player)
10735 if (tape.single_step && tape.recording && !tape.pausing)
10737 /* as it is called "single step mode", just return to pause mode when the
10738 player stopped moving after one tile (or never starts moving at all) */
10739 if (!player->is_moving && !player->is_pushing)
10741 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10742 SnapField(player, 0, 0); /* stop snapping */
10746 CheckSaveEngineSnapshot(player);
10749 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10751 int left = player_action & JOY_LEFT;
10752 int right = player_action & JOY_RIGHT;
10753 int up = player_action & JOY_UP;
10754 int down = player_action & JOY_DOWN;
10755 int button1 = player_action & JOY_BUTTON_1;
10756 int button2 = player_action & JOY_BUTTON_2;
10757 int dx = (left ? -1 : right ? 1 : 0);
10758 int dy = (up ? -1 : down ? 1 : 0);
10760 if (!player->active || tape.pausing)
10766 SnapField(player, dx, dy);
10770 DropElement(player);
10772 MovePlayer(player, dx, dy);
10775 CheckSingleStepMode(player);
10777 SetPlayerWaiting(player, FALSE);
10779 return player_action;
10783 /* no actions for this player (no input at player's configured device) */
10785 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10786 SnapField(player, 0, 0);
10787 CheckGravityMovementWhenNotMoving(player);
10789 if (player->MovPos == 0)
10790 SetPlayerWaiting(player, TRUE);
10792 if (player->MovPos == 0) /* needed for tape.playing */
10793 player->is_moving = FALSE;
10795 player->is_dropping = FALSE;
10796 player->is_dropping_pressed = FALSE;
10797 player->drop_pressed_delay = 0;
10799 CheckSingleStepMode(player);
10805 static void CheckLevelTime()
10809 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10810 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10812 if (level.native_em_level->lev->home == 0) /* all players at home */
10814 PlayerWins(local_player);
10816 AllPlayersGone = TRUE;
10818 level.native_em_level->lev->home = -1;
10821 if (level.native_em_level->ply[0]->alive == 0 &&
10822 level.native_em_level->ply[1]->alive == 0 &&
10823 level.native_em_level->ply[2]->alive == 0 &&
10824 level.native_em_level->ply[3]->alive == 0) /* all dead */
10825 AllPlayersGone = TRUE;
10827 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10829 if (game_sp.LevelSolved &&
10830 !game_sp.GameOver) /* game won */
10832 PlayerWins(local_player);
10834 game_sp.GameOver = TRUE;
10836 AllPlayersGone = TRUE;
10839 if (game_sp.GameOver) /* game lost */
10840 AllPlayersGone = TRUE;
10843 if (TimeFrames >= FRAMES_PER_SECOND)
10848 for (i = 0; i < MAX_PLAYERS; i++)
10850 struct PlayerInfo *player = &stored_player[i];
10852 if (SHIELD_ON(player))
10854 player->shield_normal_time_left--;
10856 if (player->shield_deadly_time_left > 0)
10857 player->shield_deadly_time_left--;
10861 if (!local_player->LevelSolved && !level.use_step_counter)
10869 if (TimeLeft <= 10 && setup.time_limit)
10870 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10872 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10873 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10875 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10877 if (!TimeLeft && setup.time_limit)
10879 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10880 level.native_em_level->lev->killed_out_of_time = TRUE;
10882 for (i = 0; i < MAX_PLAYERS; i++)
10883 KillPlayer(&stored_player[i]);
10886 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10888 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10891 level.native_em_level->lev->time =
10892 (game.no_time_limit ? TimePlayed : TimeLeft);
10895 if (tape.recording || tape.playing)
10896 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10899 if (tape.recording || tape.playing)
10900 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10902 UpdateAndDisplayGameControlValues();
10905 void AdvanceFrameAndPlayerCounters(int player_nr)
10909 /* advance frame counters (global frame counter and time frame counter) */
10913 /* advance player counters (counters for move delay, move animation etc.) */
10914 for (i = 0; i < MAX_PLAYERS; i++)
10916 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10917 int move_delay_value = stored_player[i].move_delay_value;
10918 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10920 if (!advance_player_counters) /* not all players may be affected */
10923 if (move_frames == 0) /* less than one move per game frame */
10925 int stepsize = TILEX / move_delay_value;
10926 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10927 int count = (stored_player[i].is_moving ?
10928 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10930 if (count % delay == 0)
10934 stored_player[i].Frame += move_frames;
10936 if (stored_player[i].MovPos != 0)
10937 stored_player[i].StepFrame += move_frames;
10939 if (stored_player[i].move_delay > 0)
10940 stored_player[i].move_delay--;
10942 /* due to bugs in previous versions, counter must count up, not down */
10943 if (stored_player[i].push_delay != -1)
10944 stored_player[i].push_delay++;
10946 if (stored_player[i].drop_delay > 0)
10947 stored_player[i].drop_delay--;
10949 if (stored_player[i].is_dropping_pressed)
10950 stored_player[i].drop_pressed_delay++;
10954 void StartGameActions(boolean init_network_game, boolean record_tape,
10957 unsigned int new_random_seed = InitRND(random_seed);
10960 TapeStartRecording(new_random_seed);
10962 #if defined(NETWORK_AVALIABLE)
10963 if (init_network_game)
10965 SendToServer_StartPlaying();
10974 void GameActionsExt()
10977 static unsigned int game_frame_delay = 0;
10979 unsigned int game_frame_delay_value;
10980 byte *recorded_player_action;
10981 byte summarized_player_action = 0;
10982 byte tape_action[MAX_PLAYERS];
10985 /* detect endless loops, caused by custom element programming */
10986 if (recursion_loop_detected && recursion_loop_depth == 0)
10988 char *message = getStringCat3("Internal Error! Element ",
10989 EL_NAME(recursion_loop_element),
10990 " caused endless loop! Quit the game?");
10992 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10993 EL_NAME(recursion_loop_element));
10995 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10997 recursion_loop_detected = FALSE; /* if game should be continued */
11004 if (game.restart_level)
11005 StartGameActions(options.network, setup.autorecord, level.random_seed);
11007 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11008 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11010 if (level.native_em_level->lev->home == 0) /* all players at home */
11012 PlayerWins(local_player);
11014 AllPlayersGone = TRUE;
11016 level.native_em_level->lev->home = -1;
11019 if (level.native_em_level->ply[0]->alive == 0 &&
11020 level.native_em_level->ply[1]->alive == 0 &&
11021 level.native_em_level->ply[2]->alive == 0 &&
11022 level.native_em_level->ply[3]->alive == 0) /* all dead */
11023 AllPlayersGone = TRUE;
11025 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11027 if (game_sp.LevelSolved &&
11028 !game_sp.GameOver) /* game won */
11030 PlayerWins(local_player);
11032 game_sp.GameOver = TRUE;
11034 AllPlayersGone = TRUE;
11037 if (game_sp.GameOver) /* game lost */
11038 AllPlayersGone = TRUE;
11041 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11044 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11047 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11050 game_frame_delay_value =
11051 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11053 if (tape.playing && tape.warp_forward && !tape.pausing)
11054 game_frame_delay_value = 0;
11056 SetVideoFrameDelay(game_frame_delay_value);
11060 /* ---------- main game synchronization point ---------- */
11062 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11064 printf("::: skip == %d\n", skip);
11067 /* ---------- main game synchronization point ---------- */
11069 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11073 if (network_playing && !network_player_action_received)
11075 /* try to get network player actions in time */
11077 #if defined(NETWORK_AVALIABLE)
11078 /* last chance to get network player actions without main loop delay */
11079 HandleNetworking();
11082 /* game was quit by network peer */
11083 if (game_status != GAME_MODE_PLAYING)
11086 if (!network_player_action_received)
11087 return; /* failed to get network player actions in time */
11089 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11095 /* at this point we know that we really continue executing the game */
11097 network_player_action_received = FALSE;
11099 /* when playing tape, read previously recorded player input from tape data */
11100 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11102 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11106 if (tape.set_centered_player)
11108 game.centered_player_nr_next = tape.centered_player_nr_next;
11109 game.set_centered_player = TRUE;
11112 for (i = 0; i < MAX_PLAYERS; i++)
11114 summarized_player_action |= stored_player[i].action;
11116 if (!network_playing && (game.team_mode || tape.playing))
11117 stored_player[i].effective_action = stored_player[i].action;
11120 #if defined(NETWORK_AVALIABLE)
11121 if (network_playing)
11122 SendToServer_MovePlayer(summarized_player_action);
11125 // summarize all actions at local players mapped input device position
11126 // (this allows using different input devices in single player mode)
11127 if (!options.network && !game.team_mode)
11128 stored_player[map_player_action[local_player->index_nr]].effective_action =
11129 summarized_player_action;
11131 if (tape.recording &&
11133 setup.input_on_focus &&
11134 game.centered_player_nr != -1)
11136 for (i = 0; i < MAX_PLAYERS; i++)
11137 stored_player[i].effective_action =
11138 (i == game.centered_player_nr ? summarized_player_action : 0);
11141 if (recorded_player_action != NULL)
11142 for (i = 0; i < MAX_PLAYERS; i++)
11143 stored_player[i].effective_action = recorded_player_action[i];
11145 for (i = 0; i < MAX_PLAYERS; i++)
11147 tape_action[i] = stored_player[i].effective_action;
11149 /* (this may happen in the RND game engine if a player was not present on
11150 the playfield on level start, but appeared later from a custom element */
11151 if (setup.team_mode &&
11154 !tape.player_participates[i])
11155 tape.player_participates[i] = TRUE;
11158 /* only record actions from input devices, but not programmed actions */
11159 if (tape.recording)
11160 TapeRecordAction(tape_action);
11162 #if USE_NEW_PLAYER_ASSIGNMENTS
11163 // !!! also map player actions in single player mode !!!
11164 // if (game.team_mode)
11167 byte mapped_action[MAX_PLAYERS];
11169 #if DEBUG_PLAYER_ACTIONS
11171 for (i = 0; i < MAX_PLAYERS; i++)
11172 printf(" %d, ", stored_player[i].effective_action);
11175 for (i = 0; i < MAX_PLAYERS; i++)
11176 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11178 for (i = 0; i < MAX_PLAYERS; i++)
11179 stored_player[i].effective_action = mapped_action[i];
11181 #if DEBUG_PLAYER_ACTIONS
11183 for (i = 0; i < MAX_PLAYERS; i++)
11184 printf(" %d, ", stored_player[i].effective_action);
11188 #if DEBUG_PLAYER_ACTIONS
11192 for (i = 0; i < MAX_PLAYERS; i++)
11193 printf(" %d, ", stored_player[i].effective_action);
11199 for (i = 0; i < MAX_PLAYERS; i++)
11201 // allow engine snapshot in case of changed movement attempt
11202 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11203 (stored_player[i].effective_action & KEY_MOTION))
11204 game.snapshot.changed_action = TRUE;
11206 // allow engine snapshot in case of snapping/dropping attempt
11207 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11208 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11209 game.snapshot.changed_action = TRUE;
11211 game.snapshot.last_action[i] = stored_player[i].effective_action;
11214 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11216 GameActions_EM_Main();
11218 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11220 GameActions_SP_Main();
11224 GameActions_RND_Main();
11227 BlitScreenToBitmap(backbuffer);
11231 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11233 if (options.debug) /* calculate frames per second */
11235 static unsigned int fps_counter = 0;
11236 static int fps_frames = 0;
11237 unsigned int fps_delay_ms = Counter() - fps_counter;
11241 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11243 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11246 fps_counter = Counter();
11249 redraw_mask |= REDRAW_FPS;
11253 static void GameActions_CheckSaveEngineSnapshot()
11255 if (!game.snapshot.save_snapshot)
11258 // clear flag for saving snapshot _before_ saving snapshot
11259 game.snapshot.save_snapshot = FALSE;
11261 SaveEngineSnapshotToList();
11268 GameActions_CheckSaveEngineSnapshot();
11271 void GameActions_EM_Main()
11273 byte effective_action[MAX_PLAYERS];
11274 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11277 for (i = 0; i < MAX_PLAYERS; i++)
11278 effective_action[i] = stored_player[i].effective_action;
11280 GameActions_EM(effective_action, warp_mode);
11283 void GameActions_SP_Main()
11285 byte effective_action[MAX_PLAYERS];
11286 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11289 for (i = 0; i < MAX_PLAYERS; i++)
11290 effective_action[i] = stored_player[i].effective_action;
11292 GameActions_SP(effective_action, warp_mode);
11295 void GameActions_RND_Main()
11300 void GameActions_RND()
11302 int magic_wall_x = 0, magic_wall_y = 0;
11303 int i, x, y, element, graphic;
11305 InitPlayfieldScanModeVars();
11307 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11309 SCAN_PLAYFIELD(x, y)
11311 ChangeCount[x][y] = 0;
11312 ChangeEvent[x][y] = -1;
11316 if (game.set_centered_player)
11318 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11320 /* switching to "all players" only possible if all players fit to screen */
11321 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11323 game.centered_player_nr_next = game.centered_player_nr;
11324 game.set_centered_player = FALSE;
11327 /* do not switch focus to non-existing (or non-active) player */
11328 if (game.centered_player_nr_next >= 0 &&
11329 !stored_player[game.centered_player_nr_next].active)
11331 game.centered_player_nr_next = game.centered_player_nr;
11332 game.set_centered_player = FALSE;
11336 if (game.set_centered_player &&
11337 ScreenMovPos == 0) /* screen currently aligned at tile position */
11341 if (game.centered_player_nr_next == -1)
11343 setScreenCenteredToAllPlayers(&sx, &sy);
11347 sx = stored_player[game.centered_player_nr_next].jx;
11348 sy = stored_player[game.centered_player_nr_next].jy;
11351 game.centered_player_nr = game.centered_player_nr_next;
11352 game.set_centered_player = FALSE;
11354 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11355 DrawGameDoorValues();
11358 for (i = 0; i < MAX_PLAYERS; i++)
11360 int actual_player_action = stored_player[i].effective_action;
11363 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11364 - rnd_equinox_tetrachloride 048
11365 - rnd_equinox_tetrachloride_ii 096
11366 - rnd_emanuel_schmieg 002
11367 - doctor_sloan_ww 001, 020
11369 if (stored_player[i].MovPos == 0)
11370 CheckGravityMovement(&stored_player[i]);
11373 /* overwrite programmed action with tape action */
11374 if (stored_player[i].programmed_action)
11375 actual_player_action = stored_player[i].programmed_action;
11377 PlayerActions(&stored_player[i], actual_player_action);
11379 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11382 ScrollScreen(NULL, SCROLL_GO_ON);
11384 /* for backwards compatibility, the following code emulates a fixed bug that
11385 occured when pushing elements (causing elements that just made their last
11386 pushing step to already (if possible) make their first falling step in the
11387 same game frame, which is bad); this code is also needed to use the famous
11388 "spring push bug" which is used in older levels and might be wanted to be
11389 used also in newer levels, but in this case the buggy pushing code is only
11390 affecting the "spring" element and no other elements */
11392 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11394 for (i = 0; i < MAX_PLAYERS; i++)
11396 struct PlayerInfo *player = &stored_player[i];
11397 int x = player->jx;
11398 int y = player->jy;
11400 if (player->active && player->is_pushing && player->is_moving &&
11402 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11403 Feld[x][y] == EL_SPRING))
11405 ContinueMoving(x, y);
11407 /* continue moving after pushing (this is actually a bug) */
11408 if (!IS_MOVING(x, y))
11409 Stop[x][y] = FALSE;
11414 SCAN_PLAYFIELD(x, y)
11416 ChangeCount[x][y] = 0;
11417 ChangeEvent[x][y] = -1;
11419 /* this must be handled before main playfield loop */
11420 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11423 if (MovDelay[x][y] <= 0)
11427 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11430 if (MovDelay[x][y] <= 0)
11433 TEST_DrawLevelField(x, y);
11435 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11440 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11442 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11443 printf("GameActions(): This should never happen!\n");
11445 ChangePage[x][y] = -1;
11449 Stop[x][y] = FALSE;
11450 if (WasJustMoving[x][y] > 0)
11451 WasJustMoving[x][y]--;
11452 if (WasJustFalling[x][y] > 0)
11453 WasJustFalling[x][y]--;
11454 if (CheckCollision[x][y] > 0)
11455 CheckCollision[x][y]--;
11456 if (CheckImpact[x][y] > 0)
11457 CheckImpact[x][y]--;
11461 /* reset finished pushing action (not done in ContinueMoving() to allow
11462 continuous pushing animation for elements with zero push delay) */
11463 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11465 ResetGfxAnimation(x, y);
11466 TEST_DrawLevelField(x, y);
11470 if (IS_BLOCKED(x, y))
11474 Blocked2Moving(x, y, &oldx, &oldy);
11475 if (!IS_MOVING(oldx, oldy))
11477 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11478 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11479 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11480 printf("GameActions(): This should never happen!\n");
11486 SCAN_PLAYFIELD(x, y)
11488 element = Feld[x][y];
11489 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11491 ResetGfxFrame(x, y, TRUE);
11493 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11494 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11495 ResetRandomAnimationValue(x, y);
11497 SetRandomAnimationValue(x, y);
11499 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11501 if (IS_INACTIVE(element))
11503 if (IS_ANIMATED(graphic))
11504 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11509 /* this may take place after moving, so 'element' may have changed */
11510 if (IS_CHANGING(x, y) &&
11511 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11513 int page = element_info[element].event_page_nr[CE_DELAY];
11515 HandleElementChange(x, y, page);
11517 element = Feld[x][y];
11518 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11521 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11525 element = Feld[x][y];
11526 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11528 if (IS_ANIMATED(graphic) &&
11529 !IS_MOVING(x, y) &&
11531 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11533 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11534 TEST_DrawTwinkleOnField(x, y);
11536 else if ((element == EL_ACID ||
11537 element == EL_EXIT_OPEN ||
11538 element == EL_EM_EXIT_OPEN ||
11539 element == EL_SP_EXIT_OPEN ||
11540 element == EL_STEEL_EXIT_OPEN ||
11541 element == EL_EM_STEEL_EXIT_OPEN ||
11542 element == EL_SP_TERMINAL ||
11543 element == EL_SP_TERMINAL_ACTIVE ||
11544 element == EL_EXTRA_TIME ||
11545 element == EL_SHIELD_NORMAL ||
11546 element == EL_SHIELD_DEADLY) &&
11547 IS_ANIMATED(graphic))
11548 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11549 else if (IS_MOVING(x, y))
11550 ContinueMoving(x, y);
11551 else if (IS_ACTIVE_BOMB(element))
11552 CheckDynamite(x, y);
11553 else if (element == EL_AMOEBA_GROWING)
11554 AmoebeWaechst(x, y);
11555 else if (element == EL_AMOEBA_SHRINKING)
11556 AmoebaDisappearing(x, y);
11558 #if !USE_NEW_AMOEBA_CODE
11559 else if (IS_AMOEBALIVE(element))
11560 AmoebeAbleger(x, y);
11563 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11565 else if (element == EL_EXIT_CLOSED)
11567 else if (element == EL_EM_EXIT_CLOSED)
11569 else if (element == EL_STEEL_EXIT_CLOSED)
11570 CheckExitSteel(x, y);
11571 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11572 CheckExitSteelEM(x, y);
11573 else if (element == EL_SP_EXIT_CLOSED)
11575 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11576 element == EL_EXPANDABLE_STEELWALL_GROWING)
11577 MauerWaechst(x, y);
11578 else if (element == EL_EXPANDABLE_WALL ||
11579 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11580 element == EL_EXPANDABLE_WALL_VERTICAL ||
11581 element == EL_EXPANDABLE_WALL_ANY ||
11582 element == EL_BD_EXPANDABLE_WALL)
11583 MauerAbleger(x, y);
11584 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11585 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11586 element == EL_EXPANDABLE_STEELWALL_ANY)
11587 MauerAblegerStahl(x, y);
11588 else if (element == EL_FLAMES)
11589 CheckForDragon(x, y);
11590 else if (element == EL_EXPLOSION)
11591 ; /* drawing of correct explosion animation is handled separately */
11592 else if (element == EL_ELEMENT_SNAPPING ||
11593 element == EL_DIAGONAL_SHRINKING ||
11594 element == EL_DIAGONAL_GROWING)
11596 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11598 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11600 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11601 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11603 if (IS_BELT_ACTIVE(element))
11604 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11606 if (game.magic_wall_active)
11608 int jx = local_player->jx, jy = local_player->jy;
11610 /* play the element sound at the position nearest to the player */
11611 if ((element == EL_MAGIC_WALL_FULL ||
11612 element == EL_MAGIC_WALL_ACTIVE ||
11613 element == EL_MAGIC_WALL_EMPTYING ||
11614 element == EL_BD_MAGIC_WALL_FULL ||
11615 element == EL_BD_MAGIC_WALL_ACTIVE ||
11616 element == EL_BD_MAGIC_WALL_EMPTYING ||
11617 element == EL_DC_MAGIC_WALL_FULL ||
11618 element == EL_DC_MAGIC_WALL_ACTIVE ||
11619 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11620 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11628 #if USE_NEW_AMOEBA_CODE
11629 /* new experimental amoeba growth stuff */
11630 if (!(FrameCounter % 8))
11632 static unsigned int random = 1684108901;
11634 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11636 x = RND(lev_fieldx);
11637 y = RND(lev_fieldy);
11638 element = Feld[x][y];
11640 if (!IS_PLAYER(x,y) &&
11641 (element == EL_EMPTY ||
11642 CAN_GROW_INTO(element) ||
11643 element == EL_QUICKSAND_EMPTY ||
11644 element == EL_QUICKSAND_FAST_EMPTY ||
11645 element == EL_ACID_SPLASH_LEFT ||
11646 element == EL_ACID_SPLASH_RIGHT))
11648 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11649 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11650 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11651 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11652 Feld[x][y] = EL_AMOEBA_DROP;
11655 random = random * 129 + 1;
11660 game.explosions_delayed = FALSE;
11662 SCAN_PLAYFIELD(x, y)
11664 element = Feld[x][y];
11666 if (ExplodeField[x][y])
11667 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11668 else if (element == EL_EXPLOSION)
11669 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11671 ExplodeField[x][y] = EX_TYPE_NONE;
11674 game.explosions_delayed = TRUE;
11676 if (game.magic_wall_active)
11678 if (!(game.magic_wall_time_left % 4))
11680 int element = Feld[magic_wall_x][magic_wall_y];
11682 if (element == EL_BD_MAGIC_WALL_FULL ||
11683 element == EL_BD_MAGIC_WALL_ACTIVE ||
11684 element == EL_BD_MAGIC_WALL_EMPTYING)
11685 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11686 else if (element == EL_DC_MAGIC_WALL_FULL ||
11687 element == EL_DC_MAGIC_WALL_ACTIVE ||
11688 element == EL_DC_MAGIC_WALL_EMPTYING)
11689 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11691 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11694 if (game.magic_wall_time_left > 0)
11696 game.magic_wall_time_left--;
11698 if (!game.magic_wall_time_left)
11700 SCAN_PLAYFIELD(x, y)
11702 element = Feld[x][y];
11704 if (element == EL_MAGIC_WALL_ACTIVE ||
11705 element == EL_MAGIC_WALL_FULL)
11707 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11708 TEST_DrawLevelField(x, y);
11710 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11711 element == EL_BD_MAGIC_WALL_FULL)
11713 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11714 TEST_DrawLevelField(x, y);
11716 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11717 element == EL_DC_MAGIC_WALL_FULL)
11719 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11720 TEST_DrawLevelField(x, y);
11724 game.magic_wall_active = FALSE;
11729 if (game.light_time_left > 0)
11731 game.light_time_left--;
11733 if (game.light_time_left == 0)
11734 RedrawAllLightSwitchesAndInvisibleElements();
11737 if (game.timegate_time_left > 0)
11739 game.timegate_time_left--;
11741 if (game.timegate_time_left == 0)
11742 CloseAllOpenTimegates();
11745 if (game.lenses_time_left > 0)
11747 game.lenses_time_left--;
11749 if (game.lenses_time_left == 0)
11750 RedrawAllInvisibleElementsForLenses();
11753 if (game.magnify_time_left > 0)
11755 game.magnify_time_left--;
11757 if (game.magnify_time_left == 0)
11758 RedrawAllInvisibleElementsForMagnifier();
11761 for (i = 0; i < MAX_PLAYERS; i++)
11763 struct PlayerInfo *player = &stored_player[i];
11765 if (SHIELD_ON(player))
11767 if (player->shield_deadly_time_left)
11768 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11769 else if (player->shield_normal_time_left)
11770 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11774 #if USE_DELAYED_GFX_REDRAW
11775 SCAN_PLAYFIELD(x, y)
11777 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11779 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11780 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11782 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11783 DrawLevelField(x, y);
11785 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11786 DrawLevelFieldCrumbled(x, y);
11788 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11789 DrawLevelFieldCrumbledNeighbours(x, y);
11791 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11792 DrawTwinkleOnField(x, y);
11795 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11800 PlayAllPlayersSound();
11802 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11804 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11806 local_player->show_envelope = 0;
11809 /* use random number generator in every frame to make it less predictable */
11810 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11814 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11816 int min_x = x, min_y = y, max_x = x, max_y = y;
11819 for (i = 0; i < MAX_PLAYERS; i++)
11821 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11823 if (!stored_player[i].active || &stored_player[i] == player)
11826 min_x = MIN(min_x, jx);
11827 min_y = MIN(min_y, jy);
11828 max_x = MAX(max_x, jx);
11829 max_y = MAX(max_y, jy);
11832 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11835 static boolean AllPlayersInVisibleScreen()
11839 for (i = 0; i < MAX_PLAYERS; i++)
11841 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11843 if (!stored_player[i].active)
11846 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11853 void ScrollLevel(int dx, int dy)
11855 int scroll_offset = 2 * TILEX_VAR;
11858 BlitBitmap(drawto_field, drawto_field,
11859 FX + TILEX_VAR * (dx == -1) - scroll_offset,
11860 FY + TILEY_VAR * (dy == -1) - scroll_offset,
11861 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11862 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11863 FX + TILEX_VAR * (dx == 1) - scroll_offset,
11864 FY + TILEY_VAR * (dy == 1) - scroll_offset);
11868 x = (dx == 1 ? BX1 : BX2);
11869 for (y = BY1; y <= BY2; y++)
11870 DrawScreenField(x, y);
11875 y = (dy == 1 ? BY1 : BY2);
11876 for (x = BX1; x <= BX2; x++)
11877 DrawScreenField(x, y);
11880 redraw_mask |= REDRAW_FIELD;
11883 static boolean canFallDown(struct PlayerInfo *player)
11885 int jx = player->jx, jy = player->jy;
11887 return (IN_LEV_FIELD(jx, jy + 1) &&
11888 (IS_FREE(jx, jy + 1) ||
11889 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11890 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11891 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11894 static boolean canPassField(int x, int y, int move_dir)
11896 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11897 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11898 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11899 int nextx = x + dx;
11900 int nexty = y + dy;
11901 int element = Feld[x][y];
11903 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11904 !CAN_MOVE(element) &&
11905 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11906 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11907 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11910 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11912 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11913 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11914 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11918 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11919 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11920 (IS_DIGGABLE(Feld[newx][newy]) ||
11921 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11922 canPassField(newx, newy, move_dir)));
11925 static void CheckGravityMovement(struct PlayerInfo *player)
11927 if (player->gravity && !player->programmed_action)
11929 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11930 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11931 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11932 int jx = player->jx, jy = player->jy;
11933 boolean player_is_moving_to_valid_field =
11934 (!player_is_snapping &&
11935 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11936 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11937 boolean player_can_fall_down = canFallDown(player);
11939 if (player_can_fall_down &&
11940 !player_is_moving_to_valid_field)
11941 player->programmed_action = MV_DOWN;
11945 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11947 return CheckGravityMovement(player);
11949 if (player->gravity && !player->programmed_action)
11951 int jx = player->jx, jy = player->jy;
11952 boolean field_under_player_is_free =
11953 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11954 boolean player_is_standing_on_valid_field =
11955 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11956 (IS_WALKABLE(Feld[jx][jy]) &&
11957 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11959 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11960 player->programmed_action = MV_DOWN;
11965 MovePlayerOneStep()
11966 -----------------------------------------------------------------------------
11967 dx, dy: direction (non-diagonal) to try to move the player to
11968 real_dx, real_dy: direction as read from input device (can be diagonal)
11971 boolean MovePlayerOneStep(struct PlayerInfo *player,
11972 int dx, int dy, int real_dx, int real_dy)
11974 int jx = player->jx, jy = player->jy;
11975 int new_jx = jx + dx, new_jy = jy + dy;
11977 boolean player_can_move = !player->cannot_move;
11979 if (!player->active || (!dx && !dy))
11980 return MP_NO_ACTION;
11982 player->MovDir = (dx < 0 ? MV_LEFT :
11983 dx > 0 ? MV_RIGHT :
11985 dy > 0 ? MV_DOWN : MV_NONE);
11987 if (!IN_LEV_FIELD(new_jx, new_jy))
11988 return MP_NO_ACTION;
11990 if (!player_can_move)
11992 if (player->MovPos == 0)
11994 player->is_moving = FALSE;
11995 player->is_digging = FALSE;
11996 player->is_collecting = FALSE;
11997 player->is_snapping = FALSE;
11998 player->is_pushing = FALSE;
12002 if (!options.network && game.centered_player_nr == -1 &&
12003 !AllPlayersInSight(player, new_jx, new_jy))
12004 return MP_NO_ACTION;
12006 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12007 if (can_move != MP_MOVING)
12010 /* check if DigField() has caused relocation of the player */
12011 if (player->jx != jx || player->jy != jy)
12012 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12014 StorePlayer[jx][jy] = 0;
12015 player->last_jx = jx;
12016 player->last_jy = jy;
12017 player->jx = new_jx;
12018 player->jy = new_jy;
12019 StorePlayer[new_jx][new_jy] = player->element_nr;
12021 if (player->move_delay_value_next != -1)
12023 player->move_delay_value = player->move_delay_value_next;
12024 player->move_delay_value_next = -1;
12028 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12030 player->step_counter++;
12032 PlayerVisit[jx][jy] = FrameCounter;
12034 player->is_moving = TRUE;
12037 /* should better be called in MovePlayer(), but this breaks some tapes */
12038 ScrollPlayer(player, SCROLL_INIT);
12044 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12046 int jx = player->jx, jy = player->jy;
12047 int old_jx = jx, old_jy = jy;
12048 int moved = MP_NO_ACTION;
12050 if (!player->active)
12055 if (player->MovPos == 0)
12057 player->is_moving = FALSE;
12058 player->is_digging = FALSE;
12059 player->is_collecting = FALSE;
12060 player->is_snapping = FALSE;
12061 player->is_pushing = FALSE;
12067 if (player->move_delay > 0)
12070 player->move_delay = -1; /* set to "uninitialized" value */
12072 /* store if player is automatically moved to next field */
12073 player->is_auto_moving = (player->programmed_action != MV_NONE);
12075 /* remove the last programmed player action */
12076 player->programmed_action = 0;
12078 if (player->MovPos)
12080 /* should only happen if pre-1.2 tape recordings are played */
12081 /* this is only for backward compatibility */
12083 int original_move_delay_value = player->move_delay_value;
12086 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12090 /* scroll remaining steps with finest movement resolution */
12091 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12093 while (player->MovPos)
12095 ScrollPlayer(player, SCROLL_GO_ON);
12096 ScrollScreen(NULL, SCROLL_GO_ON);
12098 AdvanceFrameAndPlayerCounters(player->index_nr);
12101 BackToFront_WithFrameDelay(0);
12104 player->move_delay_value = original_move_delay_value;
12107 player->is_active = FALSE;
12109 if (player->last_move_dir & MV_HORIZONTAL)
12111 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12112 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12116 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12117 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12120 if (!moved && !player->is_active)
12122 player->is_moving = FALSE;
12123 player->is_digging = FALSE;
12124 player->is_collecting = FALSE;
12125 player->is_snapping = FALSE;
12126 player->is_pushing = FALSE;
12132 if (moved & MP_MOVING && !ScreenMovPos &&
12133 (player->index_nr == game.centered_player_nr ||
12134 game.centered_player_nr == -1))
12136 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12137 int offset = game.scroll_delay_value;
12139 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12141 /* actual player has left the screen -- scroll in that direction */
12142 if (jx != old_jx) /* player has moved horizontally */
12143 scroll_x += (jx - old_jx);
12144 else /* player has moved vertically */
12145 scroll_y += (jy - old_jy);
12149 if (jx != old_jx) /* player has moved horizontally */
12151 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12152 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12153 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12155 /* don't scroll over playfield boundaries */
12156 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12157 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12159 /* don't scroll more than one field at a time */
12160 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12162 /* don't scroll against the player's moving direction */
12163 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12164 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12165 scroll_x = old_scroll_x;
12167 else /* player has moved vertically */
12169 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12170 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12171 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12173 /* don't scroll over playfield boundaries */
12174 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12175 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12177 /* don't scroll more than one field at a time */
12178 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12180 /* don't scroll against the player's moving direction */
12181 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12182 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12183 scroll_y = old_scroll_y;
12187 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12189 if (!options.network && game.centered_player_nr == -1 &&
12190 !AllPlayersInVisibleScreen())
12192 scroll_x = old_scroll_x;
12193 scroll_y = old_scroll_y;
12197 ScrollScreen(player, SCROLL_INIT);
12198 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12203 player->StepFrame = 0;
12205 if (moved & MP_MOVING)
12207 if (old_jx != jx && old_jy == jy)
12208 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12209 else if (old_jx == jx && old_jy != jy)
12210 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12212 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12214 player->last_move_dir = player->MovDir;
12215 player->is_moving = TRUE;
12216 player->is_snapping = FALSE;
12217 player->is_switching = FALSE;
12218 player->is_dropping = FALSE;
12219 player->is_dropping_pressed = FALSE;
12220 player->drop_pressed_delay = 0;
12223 /* should better be called here than above, but this breaks some tapes */
12224 ScrollPlayer(player, SCROLL_INIT);
12229 CheckGravityMovementWhenNotMoving(player);
12231 player->is_moving = FALSE;
12233 /* at this point, the player is allowed to move, but cannot move right now
12234 (e.g. because of something blocking the way) -- ensure that the player
12235 is also allowed to move in the next frame (in old versions before 3.1.1,
12236 the player was forced to wait again for eight frames before next try) */
12238 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12239 player->move_delay = 0; /* allow direct movement in the next frame */
12242 if (player->move_delay == -1) /* not yet initialized by DigField() */
12243 player->move_delay = player->move_delay_value;
12245 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12247 TestIfPlayerTouchesBadThing(jx, jy);
12248 TestIfPlayerTouchesCustomElement(jx, jy);
12251 if (!player->active)
12252 RemovePlayer(player);
12257 void ScrollPlayer(struct PlayerInfo *player, int mode)
12259 int jx = player->jx, jy = player->jy;
12260 int last_jx = player->last_jx, last_jy = player->last_jy;
12261 int move_stepsize = TILEX / player->move_delay_value;
12263 if (!player->active)
12266 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12269 if (mode == SCROLL_INIT)
12271 player->actual_frame_counter = FrameCounter;
12272 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12274 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12275 Feld[last_jx][last_jy] == EL_EMPTY)
12277 int last_field_block_delay = 0; /* start with no blocking at all */
12278 int block_delay_adjustment = player->block_delay_adjustment;
12280 /* if player blocks last field, add delay for exactly one move */
12281 if (player->block_last_field)
12283 last_field_block_delay += player->move_delay_value;
12285 /* when blocking enabled, prevent moving up despite gravity */
12286 if (player->gravity && player->MovDir == MV_UP)
12287 block_delay_adjustment = -1;
12290 /* add block delay adjustment (also possible when not blocking) */
12291 last_field_block_delay += block_delay_adjustment;
12293 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12294 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12297 if (player->MovPos != 0) /* player has not yet reached destination */
12300 else if (!FrameReached(&player->actual_frame_counter, 1))
12303 if (player->MovPos != 0)
12305 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12306 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12308 /* before DrawPlayer() to draw correct player graphic for this case */
12309 if (player->MovPos == 0)
12310 CheckGravityMovement(player);
12313 if (player->MovPos == 0) /* player reached destination field */
12315 if (player->move_delay_reset_counter > 0)
12317 player->move_delay_reset_counter--;
12319 if (player->move_delay_reset_counter == 0)
12321 /* continue with normal speed after quickly moving through gate */
12322 HALVE_PLAYER_SPEED(player);
12324 /* be able to make the next move without delay */
12325 player->move_delay = 0;
12329 player->last_jx = jx;
12330 player->last_jy = jy;
12332 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12333 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12334 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12335 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12336 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12337 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12338 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12339 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12341 DrawPlayer(player); /* needed here only to cleanup last field */
12342 RemovePlayer(player);
12344 if (local_player->friends_still_needed == 0 ||
12345 IS_SP_ELEMENT(Feld[jx][jy]))
12346 PlayerWins(player);
12349 /* this breaks one level: "machine", level 000 */
12351 int move_direction = player->MovDir;
12352 int enter_side = MV_DIR_OPPOSITE(move_direction);
12353 int leave_side = move_direction;
12354 int old_jx = last_jx;
12355 int old_jy = last_jy;
12356 int old_element = Feld[old_jx][old_jy];
12357 int new_element = Feld[jx][jy];
12359 if (IS_CUSTOM_ELEMENT(old_element))
12360 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12362 player->index_bit, leave_side);
12364 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12365 CE_PLAYER_LEAVES_X,
12366 player->index_bit, leave_side);
12368 if (IS_CUSTOM_ELEMENT(new_element))
12369 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12370 player->index_bit, enter_side);
12372 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12373 CE_PLAYER_ENTERS_X,
12374 player->index_bit, enter_side);
12376 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12377 CE_MOVE_OF_X, move_direction);
12380 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12382 TestIfPlayerTouchesBadThing(jx, jy);
12383 TestIfPlayerTouchesCustomElement(jx, jy);
12385 /* needed because pushed element has not yet reached its destination,
12386 so it would trigger a change event at its previous field location */
12387 if (!player->is_pushing)
12388 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12390 if (!player->active)
12391 RemovePlayer(player);
12394 if (!local_player->LevelSolved && level.use_step_counter)
12404 if (TimeLeft <= 10 && setup.time_limit)
12405 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12407 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12409 DisplayGameControlValues();
12411 if (!TimeLeft && setup.time_limit)
12412 for (i = 0; i < MAX_PLAYERS; i++)
12413 KillPlayer(&stored_player[i]);
12415 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12417 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12419 DisplayGameControlValues();
12423 if (tape.single_step && tape.recording && !tape.pausing &&
12424 !player->programmed_action)
12425 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12427 if (!player->programmed_action)
12428 CheckSaveEngineSnapshot(player);
12432 void ScrollScreen(struct PlayerInfo *player, int mode)
12434 static unsigned int screen_frame_counter = 0;
12436 if (mode == SCROLL_INIT)
12438 /* set scrolling step size according to actual player's moving speed */
12439 ScrollStepSize = TILEX / player->move_delay_value;
12441 screen_frame_counter = FrameCounter;
12442 ScreenMovDir = player->MovDir;
12443 ScreenMovPos = player->MovPos;
12444 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12447 else if (!FrameReached(&screen_frame_counter, 1))
12452 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12453 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12454 redraw_mask |= REDRAW_FIELD;
12457 ScreenMovDir = MV_NONE;
12460 void TestIfPlayerTouchesCustomElement(int x, int y)
12462 static int xy[4][2] =
12469 static int trigger_sides[4][2] =
12471 /* center side border side */
12472 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12473 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12474 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12475 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12477 static int touch_dir[4] =
12479 MV_LEFT | MV_RIGHT,
12484 int center_element = Feld[x][y]; /* should always be non-moving! */
12487 for (i = 0; i < NUM_DIRECTIONS; i++)
12489 int xx = x + xy[i][0];
12490 int yy = y + xy[i][1];
12491 int center_side = trigger_sides[i][0];
12492 int border_side = trigger_sides[i][1];
12493 int border_element;
12495 if (!IN_LEV_FIELD(xx, yy))
12498 if (IS_PLAYER(x, y)) /* player found at center element */
12500 struct PlayerInfo *player = PLAYERINFO(x, y);
12502 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12503 border_element = Feld[xx][yy]; /* may be moving! */
12504 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12505 border_element = Feld[xx][yy];
12506 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12507 border_element = MovingOrBlocked2Element(xx, yy);
12509 continue; /* center and border element do not touch */
12511 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12512 player->index_bit, border_side);
12513 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12514 CE_PLAYER_TOUCHES_X,
12515 player->index_bit, border_side);
12518 /* use player element that is initially defined in the level playfield,
12519 not the player element that corresponds to the runtime player number
12520 (example: a level that contains EL_PLAYER_3 as the only player would
12521 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12522 int player_element = PLAYERINFO(x, y)->initial_element;
12524 CheckElementChangeBySide(xx, yy, border_element, player_element,
12525 CE_TOUCHING_X, border_side);
12528 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12530 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12532 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12534 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12535 continue; /* center and border element do not touch */
12538 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12539 player->index_bit, center_side);
12540 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12541 CE_PLAYER_TOUCHES_X,
12542 player->index_bit, center_side);
12545 /* use player element that is initially defined in the level playfield,
12546 not the player element that corresponds to the runtime player number
12547 (example: a level that contains EL_PLAYER_3 as the only player would
12548 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12549 int player_element = PLAYERINFO(xx, yy)->initial_element;
12551 CheckElementChangeBySide(x, y, center_element, player_element,
12552 CE_TOUCHING_X, center_side);
12560 void TestIfElementTouchesCustomElement(int x, int y)
12562 static int xy[4][2] =
12569 static int trigger_sides[4][2] =
12571 /* center side border side */
12572 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12573 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12574 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12575 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12577 static int touch_dir[4] =
12579 MV_LEFT | MV_RIGHT,
12584 boolean change_center_element = FALSE;
12585 int center_element = Feld[x][y]; /* should always be non-moving! */
12586 int border_element_old[NUM_DIRECTIONS];
12589 for (i = 0; i < NUM_DIRECTIONS; i++)
12591 int xx = x + xy[i][0];
12592 int yy = y + xy[i][1];
12593 int border_element;
12595 border_element_old[i] = -1;
12597 if (!IN_LEV_FIELD(xx, yy))
12600 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12601 border_element = Feld[xx][yy]; /* may be moving! */
12602 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12603 border_element = Feld[xx][yy];
12604 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12605 border_element = MovingOrBlocked2Element(xx, yy);
12607 continue; /* center and border element do not touch */
12609 border_element_old[i] = border_element;
12612 for (i = 0; i < NUM_DIRECTIONS; i++)
12614 int xx = x + xy[i][0];
12615 int yy = y + xy[i][1];
12616 int center_side = trigger_sides[i][0];
12617 int border_element = border_element_old[i];
12619 if (border_element == -1)
12622 /* check for change of border element */
12623 CheckElementChangeBySide(xx, yy, border_element, center_element,
12624 CE_TOUCHING_X, center_side);
12626 /* (center element cannot be player, so we dont have to check this here) */
12629 for (i = 0; i < NUM_DIRECTIONS; i++)
12631 int xx = x + xy[i][0];
12632 int yy = y + xy[i][1];
12633 int border_side = trigger_sides[i][1];
12634 int border_element = border_element_old[i];
12636 if (border_element == -1)
12639 /* check for change of center element (but change it only once) */
12640 if (!change_center_element)
12641 change_center_element =
12642 CheckElementChangeBySide(x, y, center_element, border_element,
12643 CE_TOUCHING_X, border_side);
12645 if (IS_PLAYER(xx, yy))
12647 /* use player element that is initially defined in the level playfield,
12648 not the player element that corresponds to the runtime player number
12649 (example: a level that contains EL_PLAYER_3 as the only player would
12650 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12651 int player_element = PLAYERINFO(xx, yy)->initial_element;
12653 CheckElementChangeBySide(x, y, center_element, player_element,
12654 CE_TOUCHING_X, border_side);
12659 void TestIfElementHitsCustomElement(int x, int y, int direction)
12661 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12662 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12663 int hitx = x + dx, hity = y + dy;
12664 int hitting_element = Feld[x][y];
12665 int touched_element;
12667 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12670 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12671 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12673 if (IN_LEV_FIELD(hitx, hity))
12675 int opposite_direction = MV_DIR_OPPOSITE(direction);
12676 int hitting_side = direction;
12677 int touched_side = opposite_direction;
12678 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12679 MovDir[hitx][hity] != direction ||
12680 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12686 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12687 CE_HITTING_X, touched_side);
12689 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12690 CE_HIT_BY_X, hitting_side);
12692 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12693 CE_HIT_BY_SOMETHING, opposite_direction);
12695 if (IS_PLAYER(hitx, hity))
12697 /* use player element that is initially defined in the level playfield,
12698 not the player element that corresponds to the runtime player number
12699 (example: a level that contains EL_PLAYER_3 as the only player would
12700 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12701 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12703 CheckElementChangeBySide(x, y, hitting_element, player_element,
12704 CE_HITTING_X, touched_side);
12709 /* "hitting something" is also true when hitting the playfield border */
12710 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12711 CE_HITTING_SOMETHING, direction);
12714 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12716 int i, kill_x = -1, kill_y = -1;
12718 int bad_element = -1;
12719 static int test_xy[4][2] =
12726 static int test_dir[4] =
12734 for (i = 0; i < NUM_DIRECTIONS; i++)
12736 int test_x, test_y, test_move_dir, test_element;
12738 test_x = good_x + test_xy[i][0];
12739 test_y = good_y + test_xy[i][1];
12741 if (!IN_LEV_FIELD(test_x, test_y))
12745 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12747 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12749 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12750 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12752 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12753 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12757 bad_element = test_element;
12763 if (kill_x != -1 || kill_y != -1)
12765 if (IS_PLAYER(good_x, good_y))
12767 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12769 if (player->shield_deadly_time_left > 0 &&
12770 !IS_INDESTRUCTIBLE(bad_element))
12771 Bang(kill_x, kill_y);
12772 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12773 KillPlayer(player);
12776 Bang(good_x, good_y);
12780 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12782 int i, kill_x = -1, kill_y = -1;
12783 int bad_element = Feld[bad_x][bad_y];
12784 static int test_xy[4][2] =
12791 static int touch_dir[4] =
12793 MV_LEFT | MV_RIGHT,
12798 static int test_dir[4] =
12806 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12809 for (i = 0; i < NUM_DIRECTIONS; i++)
12811 int test_x, test_y, test_move_dir, test_element;
12813 test_x = bad_x + test_xy[i][0];
12814 test_y = bad_y + test_xy[i][1];
12816 if (!IN_LEV_FIELD(test_x, test_y))
12820 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12822 test_element = Feld[test_x][test_y];
12824 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12825 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12827 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12828 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12830 /* good thing is player or penguin that does not move away */
12831 if (IS_PLAYER(test_x, test_y))
12833 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12835 if (bad_element == EL_ROBOT && player->is_moving)
12836 continue; /* robot does not kill player if he is moving */
12838 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12840 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12841 continue; /* center and border element do not touch */
12849 else if (test_element == EL_PENGUIN)
12859 if (kill_x != -1 || kill_y != -1)
12861 if (IS_PLAYER(kill_x, kill_y))
12863 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12865 if (player->shield_deadly_time_left > 0 &&
12866 !IS_INDESTRUCTIBLE(bad_element))
12867 Bang(bad_x, bad_y);
12868 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12869 KillPlayer(player);
12872 Bang(kill_x, kill_y);
12876 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12878 int bad_element = Feld[bad_x][bad_y];
12879 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12880 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12881 int test_x = bad_x + dx, test_y = bad_y + dy;
12882 int test_move_dir, test_element;
12883 int kill_x = -1, kill_y = -1;
12885 if (!IN_LEV_FIELD(test_x, test_y))
12889 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12891 test_element = Feld[test_x][test_y];
12893 if (test_move_dir != bad_move_dir)
12895 /* good thing can be player or penguin that does not move away */
12896 if (IS_PLAYER(test_x, test_y))
12898 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12900 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12901 player as being hit when he is moving towards the bad thing, because
12902 the "get hit by" condition would be lost after the player stops) */
12903 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12904 return; /* player moves away from bad thing */
12909 else if (test_element == EL_PENGUIN)
12916 if (kill_x != -1 || kill_y != -1)
12918 if (IS_PLAYER(kill_x, kill_y))
12920 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12922 if (player->shield_deadly_time_left > 0 &&
12923 !IS_INDESTRUCTIBLE(bad_element))
12924 Bang(bad_x, bad_y);
12925 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12926 KillPlayer(player);
12929 Bang(kill_x, kill_y);
12933 void TestIfPlayerTouchesBadThing(int x, int y)
12935 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12938 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12940 TestIfGoodThingHitsBadThing(x, y, move_dir);
12943 void TestIfBadThingTouchesPlayer(int x, int y)
12945 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12948 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12950 TestIfBadThingHitsGoodThing(x, y, move_dir);
12953 void TestIfFriendTouchesBadThing(int x, int y)
12955 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12958 void TestIfBadThingTouchesFriend(int x, int y)
12960 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12963 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12965 int i, kill_x = bad_x, kill_y = bad_y;
12966 static int xy[4][2] =
12974 for (i = 0; i < NUM_DIRECTIONS; i++)
12978 x = bad_x + xy[i][0];
12979 y = bad_y + xy[i][1];
12980 if (!IN_LEV_FIELD(x, y))
12983 element = Feld[x][y];
12984 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12985 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12993 if (kill_x != bad_x || kill_y != bad_y)
12994 Bang(bad_x, bad_y);
12997 void KillPlayer(struct PlayerInfo *player)
12999 int jx = player->jx, jy = player->jy;
13001 if (!player->active)
13005 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13006 player->killed, player->active, player->reanimated);
13009 /* the following code was introduced to prevent an infinite loop when calling
13011 -> CheckTriggeredElementChangeExt()
13012 -> ExecuteCustomElementAction()
13014 -> (infinitely repeating the above sequence of function calls)
13015 which occurs when killing the player while having a CE with the setting
13016 "kill player X when explosion of <player X>"; the solution using a new
13017 field "player->killed" was chosen for backwards compatibility, although
13018 clever use of the fields "player->active" etc. would probably also work */
13020 if (player->killed)
13024 player->killed = TRUE;
13026 /* remove accessible field at the player's position */
13027 Feld[jx][jy] = EL_EMPTY;
13029 /* deactivate shield (else Bang()/Explode() would not work right) */
13030 player->shield_normal_time_left = 0;
13031 player->shield_deadly_time_left = 0;
13034 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13035 player->killed, player->active, player->reanimated);
13041 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13042 player->killed, player->active, player->reanimated);
13045 if (player->reanimated) /* killed player may have been reanimated */
13046 player->killed = player->reanimated = FALSE;
13048 BuryPlayer(player);
13051 static void KillPlayerUnlessEnemyProtected(int x, int y)
13053 if (!PLAYER_ENEMY_PROTECTED(x, y))
13054 KillPlayer(PLAYERINFO(x, y));
13057 static void KillPlayerUnlessExplosionProtected(int x, int y)
13059 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13060 KillPlayer(PLAYERINFO(x, y));
13063 void BuryPlayer(struct PlayerInfo *player)
13065 int jx = player->jx, jy = player->jy;
13067 if (!player->active)
13070 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13071 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13073 player->GameOver = TRUE;
13074 RemovePlayer(player);
13077 void RemovePlayer(struct PlayerInfo *player)
13079 int jx = player->jx, jy = player->jy;
13080 int i, found = FALSE;
13082 player->present = FALSE;
13083 player->active = FALSE;
13085 if (!ExplodeField[jx][jy])
13086 StorePlayer[jx][jy] = 0;
13088 if (player->is_moving)
13089 TEST_DrawLevelField(player->last_jx, player->last_jy);
13091 for (i = 0; i < MAX_PLAYERS; i++)
13092 if (stored_player[i].active)
13096 AllPlayersGone = TRUE;
13102 static void setFieldForSnapping(int x, int y, int element, int direction)
13104 struct ElementInfo *ei = &element_info[element];
13105 int direction_bit = MV_DIR_TO_BIT(direction);
13106 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13107 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13108 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13110 Feld[x][y] = EL_ELEMENT_SNAPPING;
13111 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13113 ResetGfxAnimation(x, y);
13115 GfxElement[x][y] = element;
13116 GfxAction[x][y] = action;
13117 GfxDir[x][y] = direction;
13118 GfxFrame[x][y] = -1;
13122 =============================================================================
13123 checkDiagonalPushing()
13124 -----------------------------------------------------------------------------
13125 check if diagonal input device direction results in pushing of object
13126 (by checking if the alternative direction is walkable, diggable, ...)
13127 =============================================================================
13130 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13131 int x, int y, int real_dx, int real_dy)
13133 int jx, jy, dx, dy, xx, yy;
13135 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13138 /* diagonal direction: check alternative direction */
13143 xx = jx + (dx == 0 ? real_dx : 0);
13144 yy = jy + (dy == 0 ? real_dy : 0);
13146 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13150 =============================================================================
13152 -----------------------------------------------------------------------------
13153 x, y: field next to player (non-diagonal) to try to dig to
13154 real_dx, real_dy: direction as read from input device (can be diagonal)
13155 =============================================================================
13158 static int DigField(struct PlayerInfo *player,
13159 int oldx, int oldy, int x, int y,
13160 int real_dx, int real_dy, int mode)
13162 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13163 boolean player_was_pushing = player->is_pushing;
13164 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13165 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13166 int jx = oldx, jy = oldy;
13167 int dx = x - jx, dy = y - jy;
13168 int nextx = x + dx, nexty = y + dy;
13169 int move_direction = (dx == -1 ? MV_LEFT :
13170 dx == +1 ? MV_RIGHT :
13172 dy == +1 ? MV_DOWN : MV_NONE);
13173 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13174 int dig_side = MV_DIR_OPPOSITE(move_direction);
13175 int old_element = Feld[jx][jy];
13176 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13179 if (is_player) /* function can also be called by EL_PENGUIN */
13181 if (player->MovPos == 0)
13183 player->is_digging = FALSE;
13184 player->is_collecting = FALSE;
13187 if (player->MovPos == 0) /* last pushing move finished */
13188 player->is_pushing = FALSE;
13190 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13192 player->is_switching = FALSE;
13193 player->push_delay = -1;
13195 return MP_NO_ACTION;
13199 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13200 old_element = Back[jx][jy];
13202 /* in case of element dropped at player position, check background */
13203 else if (Back[jx][jy] != EL_EMPTY &&
13204 game.engine_version >= VERSION_IDENT(2,2,0,0))
13205 old_element = Back[jx][jy];
13207 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13208 return MP_NO_ACTION; /* field has no opening in this direction */
13210 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13211 return MP_NO_ACTION; /* field has no opening in this direction */
13213 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13217 Feld[jx][jy] = player->artwork_element;
13218 InitMovingField(jx, jy, MV_DOWN);
13219 Store[jx][jy] = EL_ACID;
13220 ContinueMoving(jx, jy);
13221 BuryPlayer(player);
13223 return MP_DONT_RUN_INTO;
13226 if (player_can_move && DONT_RUN_INTO(element))
13228 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13230 return MP_DONT_RUN_INTO;
13233 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13234 return MP_NO_ACTION;
13236 collect_count = element_info[element].collect_count_initial;
13238 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13239 return MP_NO_ACTION;
13241 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13242 player_can_move = player_can_move_or_snap;
13244 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13245 game.engine_version >= VERSION_IDENT(2,2,0,0))
13247 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13248 player->index_bit, dig_side);
13249 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13250 player->index_bit, dig_side);
13252 if (element == EL_DC_LANDMINE)
13255 if (Feld[x][y] != element) /* field changed by snapping */
13258 return MP_NO_ACTION;
13261 if (player->gravity && is_player && !player->is_auto_moving &&
13262 canFallDown(player) && move_direction != MV_DOWN &&
13263 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13264 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13266 if (player_can_move &&
13267 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13269 int sound_element = SND_ELEMENT(element);
13270 int sound_action = ACTION_WALKING;
13272 if (IS_RND_GATE(element))
13274 if (!player->key[RND_GATE_NR(element)])
13275 return MP_NO_ACTION;
13277 else if (IS_RND_GATE_GRAY(element))
13279 if (!player->key[RND_GATE_GRAY_NR(element)])
13280 return MP_NO_ACTION;
13282 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13284 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13285 return MP_NO_ACTION;
13287 else if (element == EL_EXIT_OPEN ||
13288 element == EL_EM_EXIT_OPEN ||
13289 element == EL_EM_EXIT_OPENING ||
13290 element == EL_STEEL_EXIT_OPEN ||
13291 element == EL_EM_STEEL_EXIT_OPEN ||
13292 element == EL_EM_STEEL_EXIT_OPENING ||
13293 element == EL_SP_EXIT_OPEN ||
13294 element == EL_SP_EXIT_OPENING)
13296 sound_action = ACTION_PASSING; /* player is passing exit */
13298 else if (element == EL_EMPTY)
13300 sound_action = ACTION_MOVING; /* nothing to walk on */
13303 /* play sound from background or player, whatever is available */
13304 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13305 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13307 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13309 else if (player_can_move &&
13310 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13312 if (!ACCESS_FROM(element, opposite_direction))
13313 return MP_NO_ACTION; /* field not accessible from this direction */
13315 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13316 return MP_NO_ACTION;
13318 if (IS_EM_GATE(element))
13320 if (!player->key[EM_GATE_NR(element)])
13321 return MP_NO_ACTION;
13323 else if (IS_EM_GATE_GRAY(element))
13325 if (!player->key[EM_GATE_GRAY_NR(element)])
13326 return MP_NO_ACTION;
13328 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13330 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13331 return MP_NO_ACTION;
13333 else if (IS_EMC_GATE(element))
13335 if (!player->key[EMC_GATE_NR(element)])
13336 return MP_NO_ACTION;
13338 else if (IS_EMC_GATE_GRAY(element))
13340 if (!player->key[EMC_GATE_GRAY_NR(element)])
13341 return MP_NO_ACTION;
13343 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13345 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13346 return MP_NO_ACTION;
13348 else if (element == EL_DC_GATE_WHITE ||
13349 element == EL_DC_GATE_WHITE_GRAY ||
13350 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13352 if (player->num_white_keys == 0)
13353 return MP_NO_ACTION;
13355 player->num_white_keys--;
13357 else if (IS_SP_PORT(element))
13359 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13360 element == EL_SP_GRAVITY_PORT_RIGHT ||
13361 element == EL_SP_GRAVITY_PORT_UP ||
13362 element == EL_SP_GRAVITY_PORT_DOWN)
13363 player->gravity = !player->gravity;
13364 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13365 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13366 element == EL_SP_GRAVITY_ON_PORT_UP ||
13367 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13368 player->gravity = TRUE;
13369 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13370 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13371 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13372 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13373 player->gravity = FALSE;
13376 /* automatically move to the next field with double speed */
13377 player->programmed_action = move_direction;
13379 if (player->move_delay_reset_counter == 0)
13381 player->move_delay_reset_counter = 2; /* two double speed steps */
13383 DOUBLE_PLAYER_SPEED(player);
13386 PlayLevelSoundAction(x, y, ACTION_PASSING);
13388 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13392 if (mode != DF_SNAP)
13394 GfxElement[x][y] = GFX_ELEMENT(element);
13395 player->is_digging = TRUE;
13398 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13400 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13401 player->index_bit, dig_side);
13403 if (mode == DF_SNAP)
13405 if (level.block_snap_field)
13406 setFieldForSnapping(x, y, element, move_direction);
13408 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13410 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13411 player->index_bit, dig_side);
13414 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13418 if (is_player && mode != DF_SNAP)
13420 GfxElement[x][y] = element;
13421 player->is_collecting = TRUE;
13424 if (element == EL_SPEED_PILL)
13426 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13428 else if (element == EL_EXTRA_TIME && level.time > 0)
13430 TimeLeft += level.extra_time;
13432 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13434 DisplayGameControlValues();
13436 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13438 player->shield_normal_time_left += level.shield_normal_time;
13439 if (element == EL_SHIELD_DEADLY)
13440 player->shield_deadly_time_left += level.shield_deadly_time;
13442 else if (element == EL_DYNAMITE ||
13443 element == EL_EM_DYNAMITE ||
13444 element == EL_SP_DISK_RED)
13446 if (player->inventory_size < MAX_INVENTORY_SIZE)
13447 player->inventory_element[player->inventory_size++] = element;
13449 DrawGameDoorValues();
13451 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13453 player->dynabomb_count++;
13454 player->dynabombs_left++;
13456 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13458 player->dynabomb_size++;
13460 else if (element == EL_DYNABOMB_INCREASE_POWER)
13462 player->dynabomb_xl = TRUE;
13464 else if (IS_KEY(element))
13466 player->key[KEY_NR(element)] = TRUE;
13468 DrawGameDoorValues();
13470 else if (element == EL_DC_KEY_WHITE)
13472 player->num_white_keys++;
13474 /* display white keys? */
13475 /* DrawGameDoorValues(); */
13477 else if (IS_ENVELOPE(element))
13479 player->show_envelope = element;
13481 else if (element == EL_EMC_LENSES)
13483 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13485 RedrawAllInvisibleElementsForLenses();
13487 else if (element == EL_EMC_MAGNIFIER)
13489 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13491 RedrawAllInvisibleElementsForMagnifier();
13493 else if (IS_DROPPABLE(element) ||
13494 IS_THROWABLE(element)) /* can be collected and dropped */
13498 if (collect_count == 0)
13499 player->inventory_infinite_element = element;
13501 for (i = 0; i < collect_count; i++)
13502 if (player->inventory_size < MAX_INVENTORY_SIZE)
13503 player->inventory_element[player->inventory_size++] = element;
13505 DrawGameDoorValues();
13507 else if (collect_count > 0)
13509 local_player->gems_still_needed -= collect_count;
13510 if (local_player->gems_still_needed < 0)
13511 local_player->gems_still_needed = 0;
13513 game.snapshot.collected_item = TRUE;
13515 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13517 DisplayGameControlValues();
13520 RaiseScoreElement(element);
13521 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13524 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13525 player->index_bit, dig_side);
13527 if (mode == DF_SNAP)
13529 if (level.block_snap_field)
13530 setFieldForSnapping(x, y, element, move_direction);
13532 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13534 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13535 player->index_bit, dig_side);
13538 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13540 if (mode == DF_SNAP && element != EL_BD_ROCK)
13541 return MP_NO_ACTION;
13543 if (CAN_FALL(element) && dy)
13544 return MP_NO_ACTION;
13546 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13547 !(element == EL_SPRING && level.use_spring_bug))
13548 return MP_NO_ACTION;
13550 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13551 ((move_direction & MV_VERTICAL &&
13552 ((element_info[element].move_pattern & MV_LEFT &&
13553 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13554 (element_info[element].move_pattern & MV_RIGHT &&
13555 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13556 (move_direction & MV_HORIZONTAL &&
13557 ((element_info[element].move_pattern & MV_UP &&
13558 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13559 (element_info[element].move_pattern & MV_DOWN &&
13560 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13561 return MP_NO_ACTION;
13563 /* do not push elements already moving away faster than player */
13564 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13565 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13566 return MP_NO_ACTION;
13568 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13570 if (player->push_delay_value == -1 || !player_was_pushing)
13571 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13573 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13575 if (player->push_delay_value == -1)
13576 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13578 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13580 if (!player->is_pushing)
13581 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13584 player->is_pushing = TRUE;
13585 player->is_active = TRUE;
13587 if (!(IN_LEV_FIELD(nextx, nexty) &&
13588 (IS_FREE(nextx, nexty) ||
13589 (IS_SB_ELEMENT(element) &&
13590 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13591 (IS_CUSTOM_ELEMENT(element) &&
13592 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13593 return MP_NO_ACTION;
13595 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13596 return MP_NO_ACTION;
13598 if (player->push_delay == -1) /* new pushing; restart delay */
13599 player->push_delay = 0;
13601 if (player->push_delay < player->push_delay_value &&
13602 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13603 element != EL_SPRING && element != EL_BALLOON)
13605 /* make sure that there is no move delay before next try to push */
13606 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13607 player->move_delay = 0;
13609 return MP_NO_ACTION;
13612 if (IS_CUSTOM_ELEMENT(element) &&
13613 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13615 if (!DigFieldByCE(nextx, nexty, element))
13616 return MP_NO_ACTION;
13619 if (IS_SB_ELEMENT(element))
13621 if (element == EL_SOKOBAN_FIELD_FULL)
13623 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13624 local_player->sokobanfields_still_needed++;
13627 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13629 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13630 local_player->sokobanfields_still_needed--;
13633 Feld[x][y] = EL_SOKOBAN_OBJECT;
13635 if (Back[x][y] == Back[nextx][nexty])
13636 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13637 else if (Back[x][y] != 0)
13638 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13641 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13644 if (local_player->sokobanfields_still_needed == 0 &&
13645 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13647 PlayerWins(player);
13649 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13653 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13655 InitMovingField(x, y, move_direction);
13656 GfxAction[x][y] = ACTION_PUSHING;
13658 if (mode == DF_SNAP)
13659 ContinueMoving(x, y);
13661 MovPos[x][y] = (dx != 0 ? dx : dy);
13663 Pushed[x][y] = TRUE;
13664 Pushed[nextx][nexty] = TRUE;
13666 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13667 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13669 player->push_delay_value = -1; /* get new value later */
13671 /* check for element change _after_ element has been pushed */
13672 if (game.use_change_when_pushing_bug)
13674 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13675 player->index_bit, dig_side);
13676 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13677 player->index_bit, dig_side);
13680 else if (IS_SWITCHABLE(element))
13682 if (PLAYER_SWITCHING(player, x, y))
13684 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13685 player->index_bit, dig_side);
13690 player->is_switching = TRUE;
13691 player->switch_x = x;
13692 player->switch_y = y;
13694 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13696 if (element == EL_ROBOT_WHEEL)
13698 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13702 game.robot_wheel_active = TRUE;
13704 TEST_DrawLevelField(x, y);
13706 else if (element == EL_SP_TERMINAL)
13710 SCAN_PLAYFIELD(xx, yy)
13712 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13716 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13718 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13720 ResetGfxAnimation(xx, yy);
13721 TEST_DrawLevelField(xx, yy);
13725 else if (IS_BELT_SWITCH(element))
13727 ToggleBeltSwitch(x, y);
13729 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13730 element == EL_SWITCHGATE_SWITCH_DOWN ||
13731 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13732 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13734 ToggleSwitchgateSwitch(x, y);
13736 else if (element == EL_LIGHT_SWITCH ||
13737 element == EL_LIGHT_SWITCH_ACTIVE)
13739 ToggleLightSwitch(x, y);
13741 else if (element == EL_TIMEGATE_SWITCH ||
13742 element == EL_DC_TIMEGATE_SWITCH)
13744 ActivateTimegateSwitch(x, y);
13746 else if (element == EL_BALLOON_SWITCH_LEFT ||
13747 element == EL_BALLOON_SWITCH_RIGHT ||
13748 element == EL_BALLOON_SWITCH_UP ||
13749 element == EL_BALLOON_SWITCH_DOWN ||
13750 element == EL_BALLOON_SWITCH_NONE ||
13751 element == EL_BALLOON_SWITCH_ANY)
13753 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13754 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13755 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13756 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13757 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13760 else if (element == EL_LAMP)
13762 Feld[x][y] = EL_LAMP_ACTIVE;
13763 local_player->lights_still_needed--;
13765 ResetGfxAnimation(x, y);
13766 TEST_DrawLevelField(x, y);
13768 else if (element == EL_TIME_ORB_FULL)
13770 Feld[x][y] = EL_TIME_ORB_EMPTY;
13772 if (level.time > 0 || level.use_time_orb_bug)
13774 TimeLeft += level.time_orb_time;
13775 game.no_time_limit = FALSE;
13777 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13779 DisplayGameControlValues();
13782 ResetGfxAnimation(x, y);
13783 TEST_DrawLevelField(x, y);
13785 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13786 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13790 game.ball_state = !game.ball_state;
13792 SCAN_PLAYFIELD(xx, yy)
13794 int e = Feld[xx][yy];
13796 if (game.ball_state)
13798 if (e == EL_EMC_MAGIC_BALL)
13799 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13800 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13801 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13805 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13806 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13807 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13808 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13813 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13814 player->index_bit, dig_side);
13816 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13817 player->index_bit, dig_side);
13819 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13820 player->index_bit, dig_side);
13826 if (!PLAYER_SWITCHING(player, x, y))
13828 player->is_switching = TRUE;
13829 player->switch_x = x;
13830 player->switch_y = y;
13832 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13833 player->index_bit, dig_side);
13834 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13835 player->index_bit, dig_side);
13837 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13838 player->index_bit, dig_side);
13839 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13840 player->index_bit, dig_side);
13843 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13844 player->index_bit, dig_side);
13845 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13846 player->index_bit, dig_side);
13848 return MP_NO_ACTION;
13851 player->push_delay = -1;
13853 if (is_player) /* function can also be called by EL_PENGUIN */
13855 if (Feld[x][y] != element) /* really digged/collected something */
13857 player->is_collecting = !player->is_digging;
13858 player->is_active = TRUE;
13865 static boolean DigFieldByCE(int x, int y, int digging_element)
13867 int element = Feld[x][y];
13869 if (!IS_FREE(x, y))
13871 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13872 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13875 /* no element can dig solid indestructible elements */
13876 if (IS_INDESTRUCTIBLE(element) &&
13877 !IS_DIGGABLE(element) &&
13878 !IS_COLLECTIBLE(element))
13881 if (AmoebaNr[x][y] &&
13882 (element == EL_AMOEBA_FULL ||
13883 element == EL_BD_AMOEBA ||
13884 element == EL_AMOEBA_GROWING))
13886 AmoebaCnt[AmoebaNr[x][y]]--;
13887 AmoebaCnt2[AmoebaNr[x][y]]--;
13890 if (IS_MOVING(x, y))
13891 RemoveMovingField(x, y);
13895 TEST_DrawLevelField(x, y);
13898 /* if digged element was about to explode, prevent the explosion */
13899 ExplodeField[x][y] = EX_TYPE_NONE;
13901 PlayLevelSoundAction(x, y, action);
13904 Store[x][y] = EL_EMPTY;
13906 /* this makes it possible to leave the removed element again */
13907 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13908 Store[x][y] = element;
13913 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13915 int jx = player->jx, jy = player->jy;
13916 int x = jx + dx, y = jy + dy;
13917 int snap_direction = (dx == -1 ? MV_LEFT :
13918 dx == +1 ? MV_RIGHT :
13920 dy == +1 ? MV_DOWN : MV_NONE);
13921 boolean can_continue_snapping = (level.continuous_snapping &&
13922 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13924 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13927 if (!player->active || !IN_LEV_FIELD(x, y))
13935 if (player->MovPos == 0)
13936 player->is_pushing = FALSE;
13938 player->is_snapping = FALSE;
13940 if (player->MovPos == 0)
13942 player->is_moving = FALSE;
13943 player->is_digging = FALSE;
13944 player->is_collecting = FALSE;
13950 /* prevent snapping with already pressed snap key when not allowed */
13951 if (player->is_snapping && !can_continue_snapping)
13954 player->MovDir = snap_direction;
13956 if (player->MovPos == 0)
13958 player->is_moving = FALSE;
13959 player->is_digging = FALSE;
13960 player->is_collecting = FALSE;
13963 player->is_dropping = FALSE;
13964 player->is_dropping_pressed = FALSE;
13965 player->drop_pressed_delay = 0;
13967 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13970 player->is_snapping = TRUE;
13971 player->is_active = TRUE;
13973 if (player->MovPos == 0)
13975 player->is_moving = FALSE;
13976 player->is_digging = FALSE;
13977 player->is_collecting = FALSE;
13980 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13981 TEST_DrawLevelField(player->last_jx, player->last_jy);
13983 TEST_DrawLevelField(x, y);
13988 static boolean DropElement(struct PlayerInfo *player)
13990 int old_element, new_element;
13991 int dropx = player->jx, dropy = player->jy;
13992 int drop_direction = player->MovDir;
13993 int drop_side = drop_direction;
13994 int drop_element = get_next_dropped_element(player);
13996 player->is_dropping_pressed = TRUE;
13998 /* do not drop an element on top of another element; when holding drop key
13999 pressed without moving, dropped element must move away before the next
14000 element can be dropped (this is especially important if the next element
14001 is dynamite, which can be placed on background for historical reasons) */
14002 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14005 if (IS_THROWABLE(drop_element))
14007 dropx += GET_DX_FROM_DIR(drop_direction);
14008 dropy += GET_DY_FROM_DIR(drop_direction);
14010 if (!IN_LEV_FIELD(dropx, dropy))
14014 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14015 new_element = drop_element; /* default: no change when dropping */
14017 /* check if player is active, not moving and ready to drop */
14018 if (!player->active || player->MovPos || player->drop_delay > 0)
14021 /* check if player has anything that can be dropped */
14022 if (new_element == EL_UNDEFINED)
14025 /* check if drop key was pressed long enough for EM style dynamite */
14026 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14029 /* check if anything can be dropped at the current position */
14030 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14033 /* collected custom elements can only be dropped on empty fields */
14034 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14037 if (old_element != EL_EMPTY)
14038 Back[dropx][dropy] = old_element; /* store old element on this field */
14040 ResetGfxAnimation(dropx, dropy);
14041 ResetRandomAnimationValue(dropx, dropy);
14043 if (player->inventory_size > 0 ||
14044 player->inventory_infinite_element != EL_UNDEFINED)
14046 if (player->inventory_size > 0)
14048 player->inventory_size--;
14050 DrawGameDoorValues();
14052 if (new_element == EL_DYNAMITE)
14053 new_element = EL_DYNAMITE_ACTIVE;
14054 else if (new_element == EL_EM_DYNAMITE)
14055 new_element = EL_EM_DYNAMITE_ACTIVE;
14056 else if (new_element == EL_SP_DISK_RED)
14057 new_element = EL_SP_DISK_RED_ACTIVE;
14060 Feld[dropx][dropy] = new_element;
14062 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14063 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14064 el2img(Feld[dropx][dropy]), 0);
14066 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14068 /* needed if previous element just changed to "empty" in the last frame */
14069 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14071 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14072 player->index_bit, drop_side);
14073 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14075 player->index_bit, drop_side);
14077 TestIfElementTouchesCustomElement(dropx, dropy);
14079 else /* player is dropping a dyna bomb */
14081 player->dynabombs_left--;
14083 Feld[dropx][dropy] = new_element;
14085 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14086 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14087 el2img(Feld[dropx][dropy]), 0);
14089 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14092 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14093 InitField_WithBug1(dropx, dropy, FALSE);
14095 new_element = Feld[dropx][dropy]; /* element might have changed */
14097 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14098 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14100 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14101 MovDir[dropx][dropy] = drop_direction;
14103 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14105 /* do not cause impact style collision by dropping elements that can fall */
14106 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14109 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14110 player->is_dropping = TRUE;
14112 player->drop_pressed_delay = 0;
14113 player->is_dropping_pressed = FALSE;
14115 player->drop_x = dropx;
14116 player->drop_y = dropy;
14121 /* ------------------------------------------------------------------------- */
14122 /* game sound playing functions */
14123 /* ------------------------------------------------------------------------- */
14125 static int *loop_sound_frame = NULL;
14126 static int *loop_sound_volume = NULL;
14128 void InitPlayLevelSound()
14130 int num_sounds = getSoundListSize();
14132 checked_free(loop_sound_frame);
14133 checked_free(loop_sound_volume);
14135 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14136 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14139 static void PlayLevelSound(int x, int y, int nr)
14141 int sx = SCREENX(x), sy = SCREENY(y);
14142 int volume, stereo_position;
14143 int max_distance = 8;
14144 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14146 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14147 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14150 if (!IN_LEV_FIELD(x, y) ||
14151 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14152 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14155 volume = SOUND_MAX_VOLUME;
14157 if (!IN_SCR_FIELD(sx, sy))
14159 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14160 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14162 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14165 stereo_position = (SOUND_MAX_LEFT +
14166 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14167 (SCR_FIELDX + 2 * max_distance));
14169 if (IS_LOOP_SOUND(nr))
14171 /* This assures that quieter loop sounds do not overwrite louder ones,
14172 while restarting sound volume comparison with each new game frame. */
14174 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14177 loop_sound_volume[nr] = volume;
14178 loop_sound_frame[nr] = FrameCounter;
14181 PlaySoundExt(nr, volume, stereo_position, type);
14184 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14186 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14187 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14188 y < LEVELY(BY1) ? LEVELY(BY1) :
14189 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14193 static void PlayLevelSoundAction(int x, int y, int action)
14195 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14198 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14200 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14202 if (sound_effect != SND_UNDEFINED)
14203 PlayLevelSound(x, y, sound_effect);
14206 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14209 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14211 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14212 PlayLevelSound(x, y, sound_effect);
14215 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14217 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14219 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14220 PlayLevelSound(x, y, sound_effect);
14223 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14225 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14227 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14228 StopSound(sound_effect);
14231 static void PlayLevelMusic()
14233 if (levelset.music[level_nr] != MUS_UNDEFINED)
14234 PlayMusic(levelset.music[level_nr]); /* from config file */
14236 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14239 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14241 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14242 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14243 int x = xx - 1 - offset;
14244 int y = yy - 1 - offset;
14249 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14253 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14257 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14261 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14265 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14269 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14273 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14276 case SAMPLE_android_clone:
14277 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14280 case SAMPLE_android_move:
14281 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14284 case SAMPLE_spring:
14285 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14289 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14293 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14296 case SAMPLE_eater_eat:
14297 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14301 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14304 case SAMPLE_collect:
14305 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14308 case SAMPLE_diamond:
14309 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14312 case SAMPLE_squash:
14313 /* !!! CHECK THIS !!! */
14315 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14317 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14321 case SAMPLE_wonderfall:
14322 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14326 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14330 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14334 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14338 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14342 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14346 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14349 case SAMPLE_wonder:
14350 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14354 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14357 case SAMPLE_exit_open:
14358 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14361 case SAMPLE_exit_leave:
14362 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14365 case SAMPLE_dynamite:
14366 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14370 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14374 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14378 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14382 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14386 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14390 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14394 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14399 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14401 int element = map_element_SP_to_RND(element_sp);
14402 int action = map_action_SP_to_RND(action_sp);
14403 int offset = (setup.sp_show_border_elements ? 0 : 1);
14404 int x = xx - offset;
14405 int y = yy - offset;
14407 PlayLevelSoundElementAction(x, y, element, action);
14410 void RaiseScore(int value)
14412 local_player->score += value;
14414 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14416 DisplayGameControlValues();
14419 void RaiseScoreElement(int element)
14424 case EL_BD_DIAMOND:
14425 case EL_EMERALD_YELLOW:
14426 case EL_EMERALD_RED:
14427 case EL_EMERALD_PURPLE:
14428 case EL_SP_INFOTRON:
14429 RaiseScore(level.score[SC_EMERALD]);
14432 RaiseScore(level.score[SC_DIAMOND]);
14435 RaiseScore(level.score[SC_CRYSTAL]);
14438 RaiseScore(level.score[SC_PEARL]);
14441 case EL_BD_BUTTERFLY:
14442 case EL_SP_ELECTRON:
14443 RaiseScore(level.score[SC_BUG]);
14446 case EL_BD_FIREFLY:
14447 case EL_SP_SNIKSNAK:
14448 RaiseScore(level.score[SC_SPACESHIP]);
14451 case EL_DARK_YAMYAM:
14452 RaiseScore(level.score[SC_YAMYAM]);
14455 RaiseScore(level.score[SC_ROBOT]);
14458 RaiseScore(level.score[SC_PACMAN]);
14461 RaiseScore(level.score[SC_NUT]);
14464 case EL_EM_DYNAMITE:
14465 case EL_SP_DISK_RED:
14466 case EL_DYNABOMB_INCREASE_NUMBER:
14467 case EL_DYNABOMB_INCREASE_SIZE:
14468 case EL_DYNABOMB_INCREASE_POWER:
14469 RaiseScore(level.score[SC_DYNAMITE]);
14471 case EL_SHIELD_NORMAL:
14472 case EL_SHIELD_DEADLY:
14473 RaiseScore(level.score[SC_SHIELD]);
14475 case EL_EXTRA_TIME:
14476 RaiseScore(level.extra_time_score);
14490 case EL_DC_KEY_WHITE:
14491 RaiseScore(level.score[SC_KEY]);
14494 RaiseScore(element_info[element].collect_score);
14499 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14501 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14503 /* closing door required in case of envelope style request dialogs */
14505 CloseDoor(DOOR_CLOSE_1);
14507 #if defined(NETWORK_AVALIABLE)
14508 if (options.network)
14509 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14514 FadeSkipNextFadeIn();
14516 SetGameStatus(GAME_MODE_MAIN);
14521 else /* continue playing the game */
14523 if (tape.playing && tape.deactivate_display)
14524 TapeDeactivateDisplayOff(TRUE);
14526 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14528 if (tape.playing && tape.deactivate_display)
14529 TapeDeactivateDisplayOn();
14533 void RequestQuitGame(boolean ask_if_really_quit)
14535 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14536 boolean skip_request = AllPlayersGone || quick_quit;
14538 RequestQuitGameExt(skip_request, quick_quit,
14539 "Do you really want to quit the game?");
14543 /* ------------------------------------------------------------------------- */
14544 /* random generator functions */
14545 /* ------------------------------------------------------------------------- */
14547 unsigned int InitEngineRandom_RND(int seed)
14549 game.num_random_calls = 0;
14551 return InitEngineRandom(seed);
14554 unsigned int RND(int max)
14558 game.num_random_calls++;
14560 return GetEngineRandom(max);
14567 /* ------------------------------------------------------------------------- */
14568 /* game engine snapshot handling functions */
14569 /* ------------------------------------------------------------------------- */
14571 struct EngineSnapshotInfo
14573 /* runtime values for custom element collect score */
14574 int collect_score[NUM_CUSTOM_ELEMENTS];
14576 /* runtime values for group element choice position */
14577 int choice_pos[NUM_GROUP_ELEMENTS];
14579 /* runtime values for belt position animations */
14580 int belt_graphic[4][NUM_BELT_PARTS];
14581 int belt_anim_mode[4][NUM_BELT_PARTS];
14584 static struct EngineSnapshotInfo engine_snapshot_rnd;
14585 static char *snapshot_level_identifier = NULL;
14586 static int snapshot_level_nr = -1;
14588 static void SaveEngineSnapshotValues_RND()
14590 static int belt_base_active_element[4] =
14592 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14593 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14594 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14595 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14599 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14601 int element = EL_CUSTOM_START + i;
14603 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14606 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14608 int element = EL_GROUP_START + i;
14610 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14613 for (i = 0; i < 4; i++)
14615 for (j = 0; j < NUM_BELT_PARTS; j++)
14617 int element = belt_base_active_element[i] + j;
14618 int graphic = el2img(element);
14619 int anim_mode = graphic_info[graphic].anim_mode;
14621 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14622 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14627 static void LoadEngineSnapshotValues_RND()
14629 unsigned int num_random_calls = game.num_random_calls;
14632 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14634 int element = EL_CUSTOM_START + i;
14636 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14639 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14641 int element = EL_GROUP_START + i;
14643 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14646 for (i = 0; i < 4; i++)
14648 for (j = 0; j < NUM_BELT_PARTS; j++)
14650 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14651 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14653 graphic_info[graphic].anim_mode = anim_mode;
14657 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14659 InitRND(tape.random_seed);
14660 for (i = 0; i < num_random_calls; i++)
14664 if (game.num_random_calls != num_random_calls)
14666 Error(ERR_INFO, "number of random calls out of sync");
14667 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14668 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14669 Error(ERR_EXIT, "this should not happen -- please debug");
14673 void FreeEngineSnapshotSingle()
14675 FreeSnapshotSingle();
14677 setString(&snapshot_level_identifier, NULL);
14678 snapshot_level_nr = -1;
14681 void FreeEngineSnapshotList()
14683 FreeSnapshotList();
14686 ListNode *SaveEngineSnapshotBuffers()
14688 ListNode *buffers = NULL;
14690 /* copy some special values to a structure better suited for the snapshot */
14692 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14693 SaveEngineSnapshotValues_RND();
14694 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14695 SaveEngineSnapshotValues_EM();
14696 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14697 SaveEngineSnapshotValues_SP(&buffers);
14699 /* save values stored in special snapshot structure */
14701 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14702 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14703 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14704 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14705 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14706 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14708 /* save further RND engine values */
14710 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14711 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14712 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14714 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14715 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14716 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14717 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14719 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14720 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14721 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14722 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14723 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14725 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14726 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14727 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14729 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14731 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14733 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14734 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14736 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14737 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14738 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14739 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14740 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14741 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14742 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14743 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14744 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14745 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14746 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14747 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14748 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14749 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14750 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14751 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14752 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14753 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14755 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14756 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14758 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14759 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14760 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14762 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14763 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14765 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14766 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14767 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14768 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14769 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14771 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14772 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14775 ListNode *node = engine_snapshot_list_rnd;
14778 while (node != NULL)
14780 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14785 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14791 void SaveEngineSnapshotSingle()
14793 ListNode *buffers = SaveEngineSnapshotBuffers();
14795 /* finally save all snapshot buffers to single snapshot */
14796 SaveSnapshotSingle(buffers);
14798 /* save level identification information */
14799 setString(&snapshot_level_identifier, leveldir_current->identifier);
14800 snapshot_level_nr = level_nr;
14803 boolean CheckSaveEngineSnapshotToList()
14805 boolean save_snapshot =
14806 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14807 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14808 game.snapshot.changed_action) ||
14809 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14810 game.snapshot.collected_item));
14812 game.snapshot.changed_action = FALSE;
14813 game.snapshot.collected_item = FALSE;
14814 game.snapshot.save_snapshot = save_snapshot;
14816 return save_snapshot;
14819 void SaveEngineSnapshotToList()
14821 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14825 ListNode *buffers = SaveEngineSnapshotBuffers();
14827 /* finally save all snapshot buffers to snapshot list */
14828 SaveSnapshotToList(buffers);
14831 void SaveEngineSnapshotToListInitial()
14833 FreeEngineSnapshotList();
14835 SaveEngineSnapshotToList();
14838 void LoadEngineSnapshotValues()
14840 /* restore special values from snapshot structure */
14842 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14843 LoadEngineSnapshotValues_RND();
14844 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14845 LoadEngineSnapshotValues_EM();
14846 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14847 LoadEngineSnapshotValues_SP();
14850 void LoadEngineSnapshotSingle()
14852 LoadSnapshotSingle();
14854 LoadEngineSnapshotValues();
14857 void LoadEngineSnapshot_Undo(int steps)
14859 LoadSnapshotFromList_Older(steps);
14861 LoadEngineSnapshotValues();
14864 void LoadEngineSnapshot_Redo(int steps)
14866 LoadSnapshotFromList_Newer(steps);
14868 LoadEngineSnapshotValues();
14871 boolean CheckEngineSnapshotSingle()
14873 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14874 snapshot_level_nr == level_nr);
14877 boolean CheckEngineSnapshotList()
14879 return CheckSnapshotList();
14883 /* ---------- new game button stuff ---------------------------------------- */
14891 } gamebutton_info[NUM_GAME_BUTTONS] =
14894 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
14895 GAME_CTRL_ID_STOP, "stop game"
14898 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
14899 GAME_CTRL_ID_PAUSE, "pause game"
14902 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
14903 GAME_CTRL_ID_PLAY, "play game"
14906 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
14907 GAME_CTRL_ID_UNDO, "undo step"
14910 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
14911 GAME_CTRL_ID_REDO, "redo step"
14914 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
14915 GAME_CTRL_ID_SAVE, "save game"
14918 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
14919 GAME_CTRL_ID_PAUSE2, "pause game"
14922 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
14923 GAME_CTRL_ID_LOAD, "load game"
14926 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
14927 SOUND_CTRL_ID_MUSIC, "background music on/off"
14930 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
14931 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14934 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
14935 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14939 void CreateGameButtons()
14943 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14945 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14946 struct XY *pos = gamebutton_info[i].pos;
14947 struct GadgetInfo *gi;
14950 unsigned int event_mask;
14951 int base_x = (tape.show_game_buttons ? VX : DX);
14952 int base_y = (tape.show_game_buttons ? VY : DY);
14953 int gd_x = gfx->src_x;
14954 int gd_y = gfx->src_y;
14955 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14956 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14957 int gd_xa = gfx->src_x + gfx->active_xoffset;
14958 int gd_ya = gfx->src_y + gfx->active_yoffset;
14959 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14960 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14963 if (gfx->bitmap == NULL)
14965 game_gadget[id] = NULL;
14970 if (id == GAME_CTRL_ID_STOP ||
14971 id == GAME_CTRL_ID_PLAY ||
14972 id == GAME_CTRL_ID_SAVE ||
14973 id == GAME_CTRL_ID_LOAD)
14975 button_type = GD_TYPE_NORMAL_BUTTON;
14977 event_mask = GD_EVENT_RELEASED;
14979 else if (id == GAME_CTRL_ID_UNDO ||
14980 id == GAME_CTRL_ID_REDO)
14982 button_type = GD_TYPE_NORMAL_BUTTON;
14984 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14988 button_type = GD_TYPE_CHECK_BUTTON;
14990 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14991 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14992 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14993 event_mask = GD_EVENT_PRESSED;
14996 gi = CreateGadget(GDI_CUSTOM_ID, id,
14997 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14998 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14999 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15000 GDI_WIDTH, gfx->width,
15001 GDI_HEIGHT, gfx->height,
15002 GDI_TYPE, button_type,
15003 GDI_STATE, GD_BUTTON_UNPRESSED,
15004 GDI_CHECKED, checked,
15005 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15006 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15007 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15008 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15009 GDI_DIRECT_DRAW, FALSE,
15010 GDI_EVENT_MASK, event_mask,
15011 GDI_CALLBACK_ACTION, HandleGameButtons,
15015 Error(ERR_EXIT, "cannot create gadget");
15017 game_gadget[id] = gi;
15021 void FreeGameButtons()
15025 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15026 FreeGadget(game_gadget[i]);
15029 static void UnmapGameButtonsAtSamePosition(int id)
15033 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15035 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15036 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15037 UnmapGadget(game_gadget[i]);
15040 static void UnmapGameButtonsAtSamePosition_All()
15042 if (setup.show_snapshot_buttons)
15044 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15045 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15046 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15050 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15051 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15052 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15056 static void MapGameButtonsAtSamePosition(int id)
15060 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15062 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15063 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15064 MapGadget(game_gadget[i]);
15066 UnmapGameButtonsAtSamePosition_All();
15069 void MapUndoRedoButtons()
15071 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15072 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15074 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15075 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15077 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15080 void UnmapUndoRedoButtons()
15082 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15083 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15085 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15086 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15088 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15091 void MapGameButtons()
15095 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15096 if (i != GAME_CTRL_ID_UNDO &&
15097 i != GAME_CTRL_ID_REDO)
15098 MapGadget(game_gadget[i]);
15100 UnmapGameButtonsAtSamePosition_All();
15102 RedrawGameButtons();
15105 void UnmapGameButtons()
15109 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15110 UnmapGadget(game_gadget[i]);
15113 void RedrawGameButtons()
15117 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15118 RedrawGadget(game_gadget[i]);
15120 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15121 redraw_mask &= ~REDRAW_ALL;
15124 void GameUndoRedoExt()
15126 ClearPlayerAction();
15128 tape.pausing = TRUE;
15131 UpdateAndDisplayGameControlValues();
15133 DrawCompleteVideoDisplay();
15134 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15135 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15136 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15141 void GameUndo(int steps)
15143 if (!CheckEngineSnapshotList())
15146 LoadEngineSnapshot_Undo(steps);
15151 void GameRedo(int steps)
15153 if (!CheckEngineSnapshotList())
15156 LoadEngineSnapshot_Redo(steps);
15161 static void HandleGameButtonsExt(int id, int button)
15163 static boolean game_undo_executed = FALSE;
15164 int steps = BUTTON_STEPSIZE(button);
15165 boolean handle_game_buttons =
15166 (game_status == GAME_MODE_PLAYING ||
15167 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15169 if (!handle_game_buttons)
15174 case GAME_CTRL_ID_STOP:
15175 if (game_status == GAME_MODE_MAIN)
15181 RequestQuitGame(TRUE);
15185 case GAME_CTRL_ID_PAUSE:
15186 case GAME_CTRL_ID_PAUSE2:
15187 if (options.network && game_status == GAME_MODE_PLAYING)
15189 #if defined(NETWORK_AVALIABLE)
15191 SendToServer_ContinuePlaying();
15193 SendToServer_PausePlaying();
15197 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15199 game_undo_executed = FALSE;
15203 case GAME_CTRL_ID_PLAY:
15204 if (game_status == GAME_MODE_MAIN)
15206 StartGameActions(options.network, setup.autorecord, level.random_seed);
15208 else if (tape.pausing)
15210 #if defined(NETWORK_AVALIABLE)
15211 if (options.network)
15212 SendToServer_ContinuePlaying();
15215 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15219 case GAME_CTRL_ID_UNDO:
15220 // Important: When using "save snapshot when collecting an item" mode,
15221 // load last (current) snapshot for first "undo" after pressing "pause"
15222 // (else the last-but-one snapshot would be loaded, because the snapshot
15223 // pointer already points to the last snapshot when pressing "pause",
15224 // which is fine for "every step/move" mode, but not for "every collect")
15225 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15226 !game_undo_executed)
15229 game_undo_executed = TRUE;
15234 case GAME_CTRL_ID_REDO:
15238 case GAME_CTRL_ID_SAVE:
15242 case GAME_CTRL_ID_LOAD:
15246 case SOUND_CTRL_ID_MUSIC:
15247 if (setup.sound_music)
15249 setup.sound_music = FALSE;
15253 else if (audio.music_available)
15255 setup.sound = setup.sound_music = TRUE;
15257 SetAudioMode(setup.sound);
15263 case SOUND_CTRL_ID_LOOPS:
15264 if (setup.sound_loops)
15265 setup.sound_loops = FALSE;
15266 else if (audio.loops_available)
15268 setup.sound = setup.sound_loops = TRUE;
15270 SetAudioMode(setup.sound);
15274 case SOUND_CTRL_ID_SIMPLE:
15275 if (setup.sound_simple)
15276 setup.sound_simple = FALSE;
15277 else if (audio.sound_available)
15279 setup.sound = setup.sound_simple = TRUE;
15281 SetAudioMode(setup.sound);
15290 static void HandleGameButtons(struct GadgetInfo *gi)
15292 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15295 void HandleSoundButtonKeys(Key key)
15298 if (key == setup.shortcut.sound_simple)
15299 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15300 else if (key == setup.shortcut.sound_loops)
15301 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15302 else if (key == setup.shortcut.sound_music)
15303 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);