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 scroll positions */
831 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
832 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
834 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN (1)
841 #define MOVE_STEPSIZE_MAX (TILEX)
843 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
846 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
848 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
849 RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
851 RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
853 RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
855 (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
857 RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
860 RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
862 RND((c)->delay_random))
865 #define GET_VALID_RUNTIME_ELEMENT(e) \
866 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
868 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
869 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
870 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
871 (be) + (e) - EL_SELF)
873 #define GET_PLAYER_FROM_BITS(p) \
874 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
877 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
878 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
879 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
880 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
881 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
882 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
883 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
884 RESOLVED_REFERENCE_ELEMENT(be, e) : \
887 #define CAN_GROW_INTO(e) \
888 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
891 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
895 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
896 (CAN_MOVE_INTO_ACID(e) && \
897 Feld[x][y] == EL_ACID) || \
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
901 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
902 (CAN_MOVE_INTO_ACID(e) && \
903 Feld[x][y] == EL_ACID) || \
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
907 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
909 (CAN_MOVE_INTO_ACID(e) && \
910 Feld[x][y] == EL_ACID) || \
911 (DONT_COLLIDE_WITH(e) && \
913 !PLAYER_ENEMY_PROTECTED(x, y))))
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
916 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
918 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
919 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
922 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
924 #define ANDROID_CAN_CLONE_FIELD(x, y) \
925 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
929 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
932 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
935 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
938 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
940 #define PIG_CAN_ENTER_FIELD(e, x, y) \
941 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
944 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945 Feld[x][y] == EL_EM_EXIT_OPEN || \
946 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948 IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
950 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
953 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
955 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
956 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
959 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
960 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
962 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
964 #define CE_ENTER_FIELD_COND(e, x, y) \
965 (!IS_PLAYER(x, y) && \
966 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
969 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
974 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP 0
981 #define GAME_CTRL_ID_PAUSE 1
982 #define GAME_CTRL_ID_PLAY 2
983 #define GAME_CTRL_ID_UNDO 3
984 #define GAME_CTRL_ID_REDO 4
985 #define GAME_CTRL_ID_SAVE 5
986 #define GAME_CTRL_ID_PAUSE2 6
987 #define GAME_CTRL_ID_LOAD 7
988 #define SOUND_CTRL_ID_MUSIC 8
989 #define SOUND_CTRL_ID_LOOPS 9
990 #define SOUND_CTRL_ID_SIMPLE 10
992 #define NUM_GAME_BUTTONS 11
995 /* forward declaration for internal use */
997 static void CreateField(int, int, int);
999 static void ResetGfxAnimation(int, int);
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev) \
1031 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1033 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1035 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1037 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev) \
1041 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1043 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1045 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1056 static void HandleGameButtons(struct GadgetInfo *);
1058 int AmoebeNachbarNr(int, int);
1059 void AmoebeUmwandeln(int, int);
1060 void ContinueMoving(int, int);
1061 void Bang(int, int);
1062 void InitMovDir(int, int);
1063 void InitAmoebaNr(int, int);
1064 int NewHiScore(void);
1066 void TestIfGoodThingHitsBadThing(int, int, int);
1067 void TestIfBadThingHitsGoodThing(int, int, int);
1068 void TestIfPlayerTouchesBadThing(int, int);
1069 void TestIfPlayerRunsIntoBadThing(int, int, int);
1070 void TestIfBadThingTouchesPlayer(int, int);
1071 void TestIfBadThingRunsIntoPlayer(int, int, int);
1072 void TestIfFriendTouchesBadThing(int, int);
1073 void TestIfBadThingTouchesFriend(int, int);
1074 void TestIfBadThingTouchesOtherBadThing(int, int);
1075 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1077 void KillPlayer(struct PlayerInfo *);
1078 void BuryPlayer(struct PlayerInfo *);
1079 void RemovePlayer(struct PlayerInfo *);
1081 static int getInvisibleActiveFromInvisibleElement(int);
1082 static int getInvisibleFromInvisibleActiveElement(int);
1084 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1086 /* for detection of endless loops, caused by custom element programming */
1087 /* (using maximal playfield width x 10 is just a rough approximation) */
1088 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1090 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1092 if (recursion_loop_detected) \
1095 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1097 recursion_loop_detected = TRUE; \
1098 recursion_loop_element = (e); \
1101 recursion_loop_depth++; \
1104 #define RECURSION_LOOP_DETECTION_END() \
1106 recursion_loop_depth--; \
1109 static int recursion_loop_depth;
1110 static boolean recursion_loop_detected;
1111 static boolean recursion_loop_element;
1113 static int map_player_action[MAX_PLAYERS];
1116 /* ------------------------------------------------------------------------- */
1117 /* definition of elements that automatically change to other elements after */
1118 /* a specified time, eventually calling a function when changing */
1119 /* ------------------------------------------------------------------------- */
1121 /* forward declaration for changer functions */
1122 static void InitBuggyBase(int, int);
1123 static void WarnBuggyBase(int, int);
1125 static void InitTrap(int, int);
1126 static void ActivateTrap(int, int);
1127 static void ChangeActiveTrap(int, int);
1129 static void InitRobotWheel(int, int);
1130 static void RunRobotWheel(int, int);
1131 static void StopRobotWheel(int, int);
1133 static void InitTimegateWheel(int, int);
1134 static void RunTimegateWheel(int, int);
1136 static void InitMagicBallDelay(int, int);
1137 static void ActivateMagicBall(int, int);
1139 struct ChangingElementInfo
1144 void (*pre_change_function)(int x, int y);
1145 void (*change_function)(int x, int y);
1146 void (*post_change_function)(int x, int y);
1149 static struct ChangingElementInfo change_delay_list[] =
1184 EL_STEEL_EXIT_OPENING,
1192 EL_STEEL_EXIT_CLOSING,
1193 EL_STEEL_EXIT_CLOSED,
1216 EL_EM_STEEL_EXIT_OPENING,
1217 EL_EM_STEEL_EXIT_OPEN,
1224 EL_EM_STEEL_EXIT_CLOSING,
1248 EL_SWITCHGATE_OPENING,
1256 EL_SWITCHGATE_CLOSING,
1257 EL_SWITCHGATE_CLOSED,
1264 EL_TIMEGATE_OPENING,
1272 EL_TIMEGATE_CLOSING,
1281 EL_ACID_SPLASH_LEFT,
1289 EL_ACID_SPLASH_RIGHT,
1298 EL_SP_BUGGY_BASE_ACTIVATING,
1305 EL_SP_BUGGY_BASE_ACTIVATING,
1306 EL_SP_BUGGY_BASE_ACTIVE,
1313 EL_SP_BUGGY_BASE_ACTIVE,
1337 EL_ROBOT_WHEEL_ACTIVE,
1345 EL_TIMEGATE_SWITCH_ACTIVE,
1353 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1354 EL_DC_TIMEGATE_SWITCH,
1361 EL_EMC_MAGIC_BALL_ACTIVE,
1362 EL_EMC_MAGIC_BALL_ACTIVE,
1369 EL_EMC_SPRING_BUMPER_ACTIVE,
1370 EL_EMC_SPRING_BUMPER,
1377 EL_DIAGONAL_SHRINKING,
1385 EL_DIAGONAL_GROWING,
1406 int push_delay_fixed, push_delay_random;
1410 { EL_SPRING, 0, 0 },
1411 { EL_BALLOON, 0, 0 },
1413 { EL_SOKOBAN_OBJECT, 2, 0 },
1414 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1415 { EL_SATELLITE, 2, 0 },
1416 { EL_SP_DISK_YELLOW, 2, 0 },
1418 { EL_UNDEFINED, 0, 0 },
1426 move_stepsize_list[] =
1428 { EL_AMOEBA_DROP, 2 },
1429 { EL_AMOEBA_DROPPING, 2 },
1430 { EL_QUICKSAND_FILLING, 1 },
1431 { EL_QUICKSAND_EMPTYING, 1 },
1432 { EL_QUICKSAND_FAST_FILLING, 2 },
1433 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1434 { EL_MAGIC_WALL_FILLING, 2 },
1435 { EL_MAGIC_WALL_EMPTYING, 2 },
1436 { EL_BD_MAGIC_WALL_FILLING, 2 },
1437 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1438 { EL_DC_MAGIC_WALL_FILLING, 2 },
1439 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1441 { EL_UNDEFINED, 0 },
1449 collect_count_list[] =
1452 { EL_BD_DIAMOND, 1 },
1453 { EL_EMERALD_YELLOW, 1 },
1454 { EL_EMERALD_RED, 1 },
1455 { EL_EMERALD_PURPLE, 1 },
1457 { EL_SP_INFOTRON, 1 },
1461 { EL_UNDEFINED, 0 },
1469 access_direction_list[] =
1471 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1472 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1473 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1474 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1475 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1476 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1477 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1478 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1479 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1480 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1481 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1483 { EL_SP_PORT_LEFT, MV_RIGHT },
1484 { EL_SP_PORT_RIGHT, MV_LEFT },
1485 { EL_SP_PORT_UP, MV_DOWN },
1486 { EL_SP_PORT_DOWN, MV_UP },
1487 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1488 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1489 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1490 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1491 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1492 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1493 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1494 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1495 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1496 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1497 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1498 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1499 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1500 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1501 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1503 { EL_UNDEFINED, MV_NONE }
1506 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1508 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1509 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1510 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1511 IS_JUST_CHANGING(x, y))
1513 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1515 /* static variables for playfield scan mode (scanning forward or backward) */
1516 static int playfield_scan_start_x = 0;
1517 static int playfield_scan_start_y = 0;
1518 static int playfield_scan_delta_x = 1;
1519 static int playfield_scan_delta_y = 1;
1521 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1522 (y) >= 0 && (y) <= lev_fieldy - 1; \
1523 (y) += playfield_scan_delta_y) \
1524 for ((x) = playfield_scan_start_x; \
1525 (x) >= 0 && (x) <= lev_fieldx - 1; \
1526 (x) += playfield_scan_delta_x)
1529 void DEBUG_SetMaximumDynamite()
1533 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1534 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1535 local_player->inventory_element[local_player->inventory_size++] =
1540 static void InitPlayfieldScanModeVars()
1542 if (game.use_reverse_scan_direction)
1544 playfield_scan_start_x = lev_fieldx - 1;
1545 playfield_scan_start_y = lev_fieldy - 1;
1547 playfield_scan_delta_x = -1;
1548 playfield_scan_delta_y = -1;
1552 playfield_scan_start_x = 0;
1553 playfield_scan_start_y = 0;
1555 playfield_scan_delta_x = 1;
1556 playfield_scan_delta_y = 1;
1560 static void InitPlayfieldScanMode(int mode)
1562 game.use_reverse_scan_direction =
1563 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1565 InitPlayfieldScanModeVars();
1568 static int get_move_delay_from_stepsize(int move_stepsize)
1571 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1573 /* make sure that stepsize value is always a power of 2 */
1574 move_stepsize = (1 << log_2(move_stepsize));
1576 return TILEX / move_stepsize;
1579 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1582 int player_nr = player->index_nr;
1583 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1584 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1586 /* do no immediately change move delay -- the player might just be moving */
1587 player->move_delay_value_next = move_delay;
1589 /* information if player can move must be set separately */
1590 player->cannot_move = cannot_move;
1594 player->move_delay = game.initial_move_delay[player_nr];
1595 player->move_delay_value = game.initial_move_delay_value[player_nr];
1597 player->move_delay_value_next = -1;
1599 player->move_delay_reset_counter = 0;
1603 void GetPlayerConfig()
1605 GameFrameDelay = setup.game_frame_delay;
1607 if (!audio.sound_available)
1608 setup.sound_simple = FALSE;
1610 if (!audio.loops_available)
1611 setup.sound_loops = FALSE;
1613 if (!audio.music_available)
1614 setup.sound_music = FALSE;
1616 if (!video.fullscreen_available)
1617 setup.fullscreen = FALSE;
1619 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1621 SetAudioMode(setup.sound);
1624 int GetElementFromGroupElement(int element)
1626 if (IS_GROUP_ELEMENT(element))
1628 struct ElementGroupInfo *group = element_info[element].group;
1629 int last_anim_random_frame = gfx.anim_random_frame;
1632 if (group->choice_mode == ANIM_RANDOM)
1633 gfx.anim_random_frame = RND(group->num_elements_resolved);
1635 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1636 group->choice_mode, 0,
1639 if (group->choice_mode == ANIM_RANDOM)
1640 gfx.anim_random_frame = last_anim_random_frame;
1642 group->choice_pos++;
1644 element = group->element_resolved[element_pos];
1650 static void InitPlayerField(int x, int y, int element, boolean init_game)
1652 if (element == EL_SP_MURPHY)
1656 if (stored_player[0].present)
1658 Feld[x][y] = EL_SP_MURPHY_CLONE;
1664 stored_player[0].initial_element = element;
1665 stored_player[0].use_murphy = TRUE;
1667 if (!level.use_artwork_element[0])
1668 stored_player[0].artwork_element = EL_SP_MURPHY;
1671 Feld[x][y] = EL_PLAYER_1;
1677 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1678 int jx = player->jx, jy = player->jy;
1680 player->present = TRUE;
1682 player->block_last_field = (element == EL_SP_MURPHY ?
1683 level.sp_block_last_field :
1684 level.block_last_field);
1686 /* ---------- initialize player's last field block delay --------------- */
1688 /* always start with reliable default value (no adjustment needed) */
1689 player->block_delay_adjustment = 0;
1691 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1692 if (player->block_last_field && element == EL_SP_MURPHY)
1693 player->block_delay_adjustment = 1;
1695 /* special case 2: in game engines before 3.1.1, blocking was different */
1696 if (game.use_block_last_field_bug)
1697 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1699 if (!options.network || player->connected)
1701 player->active = TRUE;
1703 /* remove potentially duplicate players */
1704 if (StorePlayer[jx][jy] == Feld[x][y])
1705 StorePlayer[jx][jy] = 0;
1707 StorePlayer[x][y] = Feld[x][y];
1709 #if DEBUG_INIT_PLAYER
1712 printf("- player element %d activated", player->element_nr);
1713 printf(" (local player is %d and currently %s)\n",
1714 local_player->element_nr,
1715 local_player->active ? "active" : "not active");
1720 Feld[x][y] = EL_EMPTY;
1722 player->jx = player->last_jx = x;
1723 player->jy = player->last_jy = y;
1728 int player_nr = GET_PLAYER_NR(element);
1729 struct PlayerInfo *player = &stored_player[player_nr];
1731 if (player->active && player->killed)
1732 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1736 static void InitField(int x, int y, boolean init_game)
1738 int element = Feld[x][y];
1747 InitPlayerField(x, y, element, init_game);
1750 case EL_SOKOBAN_FIELD_PLAYER:
1751 element = Feld[x][y] = EL_PLAYER_1;
1752 InitField(x, y, init_game);
1754 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1755 InitField(x, y, init_game);
1758 case EL_SOKOBAN_FIELD_EMPTY:
1759 local_player->sokobanfields_still_needed++;
1763 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1764 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1765 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1766 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1767 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1768 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1769 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1770 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1771 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1772 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1781 case EL_SPACESHIP_RIGHT:
1782 case EL_SPACESHIP_UP:
1783 case EL_SPACESHIP_LEFT:
1784 case EL_SPACESHIP_DOWN:
1785 case EL_BD_BUTTERFLY:
1786 case EL_BD_BUTTERFLY_RIGHT:
1787 case EL_BD_BUTTERFLY_UP:
1788 case EL_BD_BUTTERFLY_LEFT:
1789 case EL_BD_BUTTERFLY_DOWN:
1791 case EL_BD_FIREFLY_RIGHT:
1792 case EL_BD_FIREFLY_UP:
1793 case EL_BD_FIREFLY_LEFT:
1794 case EL_BD_FIREFLY_DOWN:
1795 case EL_PACMAN_RIGHT:
1797 case EL_PACMAN_LEFT:
1798 case EL_PACMAN_DOWN:
1800 case EL_YAMYAM_LEFT:
1801 case EL_YAMYAM_RIGHT:
1803 case EL_YAMYAM_DOWN:
1804 case EL_DARK_YAMYAM:
1807 case EL_SP_SNIKSNAK:
1808 case EL_SP_ELECTRON:
1817 case EL_AMOEBA_FULL:
1822 case EL_AMOEBA_DROP:
1823 if (y == lev_fieldy - 1)
1825 Feld[x][y] = EL_AMOEBA_GROWING;
1826 Store[x][y] = EL_AMOEBA_WET;
1830 case EL_DYNAMITE_ACTIVE:
1831 case EL_SP_DISK_RED_ACTIVE:
1832 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1833 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1834 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1835 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1836 MovDelay[x][y] = 96;
1839 case EL_EM_DYNAMITE_ACTIVE:
1840 MovDelay[x][y] = 32;
1844 local_player->lights_still_needed++;
1848 local_player->friends_still_needed++;
1853 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1856 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1857 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1858 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1859 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1860 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1861 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1862 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1863 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1864 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1865 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1866 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1867 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1870 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1871 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1872 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1874 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1876 game.belt_dir[belt_nr] = belt_dir;
1877 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1879 else /* more than one switch -- set it like the first switch */
1881 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1886 case EL_LIGHT_SWITCH_ACTIVE:
1888 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1891 case EL_INVISIBLE_STEELWALL:
1892 case EL_INVISIBLE_WALL:
1893 case EL_INVISIBLE_SAND:
1894 if (game.light_time_left > 0 ||
1895 game.lenses_time_left > 0)
1896 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1899 case EL_EMC_MAGIC_BALL:
1900 if (game.ball_state)
1901 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1904 case EL_EMC_MAGIC_BALL_SWITCH:
1905 if (game.ball_state)
1906 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1909 case EL_TRIGGER_PLAYER:
1910 case EL_TRIGGER_ELEMENT:
1911 case EL_TRIGGER_CE_VALUE:
1912 case EL_TRIGGER_CE_SCORE:
1914 case EL_ANY_ELEMENT:
1915 case EL_CURRENT_CE_VALUE:
1916 case EL_CURRENT_CE_SCORE:
1933 /* reference elements should not be used on the playfield */
1934 Feld[x][y] = EL_EMPTY;
1938 if (IS_CUSTOM_ELEMENT(element))
1940 if (CAN_MOVE(element))
1943 if (!element_info[element].use_last_ce_value || init_game)
1944 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1946 else if (IS_GROUP_ELEMENT(element))
1948 Feld[x][y] = GetElementFromGroupElement(element);
1950 InitField(x, y, init_game);
1957 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1960 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1962 InitField(x, y, init_game);
1964 /* not needed to call InitMovDir() -- already done by InitField()! */
1965 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1966 CAN_MOVE(Feld[x][y]))
1970 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1972 int old_element = Feld[x][y];
1974 InitField(x, y, init_game);
1976 /* not needed to call InitMovDir() -- already done by InitField()! */
1977 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1978 CAN_MOVE(old_element) &&
1979 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1982 /* this case is in fact a combination of not less than three bugs:
1983 first, it calls InitMovDir() for elements that can move, although this is
1984 already done by InitField(); then, it checks the element that was at this
1985 field _before_ the call to InitField() (which can change it); lastly, it
1986 was not called for "mole with direction" elements, which were treated as
1987 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1991 static int get_key_element_from_nr(int key_nr)
1993 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1994 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1995 EL_EM_KEY_1 : EL_KEY_1);
1997 return key_base_element + key_nr;
2000 static int get_next_dropped_element(struct PlayerInfo *player)
2002 return (player->inventory_size > 0 ?
2003 player->inventory_element[player->inventory_size - 1] :
2004 player->inventory_infinite_element != EL_UNDEFINED ?
2005 player->inventory_infinite_element :
2006 player->dynabombs_left > 0 ?
2007 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2011 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2013 /* pos >= 0: get element from bottom of the stack;
2014 pos < 0: get element from top of the stack */
2018 int min_inventory_size = -pos;
2019 int inventory_pos = player->inventory_size - min_inventory_size;
2020 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2022 return (player->inventory_size >= min_inventory_size ?
2023 player->inventory_element[inventory_pos] :
2024 player->inventory_infinite_element != EL_UNDEFINED ?
2025 player->inventory_infinite_element :
2026 player->dynabombs_left >= min_dynabombs_left ?
2027 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032 int min_dynabombs_left = pos + 1;
2033 int min_inventory_size = pos + 1 - player->dynabombs_left;
2034 int inventory_pos = pos - player->dynabombs_left;
2036 return (player->inventory_infinite_element != EL_UNDEFINED ?
2037 player->inventory_infinite_element :
2038 player->dynabombs_left >= min_dynabombs_left ?
2039 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2040 player->inventory_size >= min_inventory_size ?
2041 player->inventory_element[inventory_pos] :
2046 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2048 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2049 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2052 if (gpo1->sort_priority != gpo2->sort_priority)
2053 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2055 compare_result = gpo1->nr - gpo2->nr;
2057 return compare_result;
2060 int getPlayerInventorySize(int player_nr)
2062 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2063 return level.native_em_level->ply[player_nr]->dynamite;
2064 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2065 return level.native_sp_level->game_sp->red_disk_count;
2067 return stored_player[player_nr].inventory_size;
2070 void InitGameControlValues()
2074 for (i = 0; game_panel_controls[i].nr != -1; i++)
2076 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2077 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2078 struct TextPosInfo *pos = gpc->pos;
2080 int type = gpc->type;
2084 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2085 Error(ERR_EXIT, "this should not happen -- please debug");
2088 /* force update of game controls after initialization */
2089 gpc->value = gpc->last_value = -1;
2090 gpc->frame = gpc->last_frame = -1;
2091 gpc->gfx_frame = -1;
2093 /* determine panel value width for later calculation of alignment */
2094 if (type == TYPE_INTEGER || type == TYPE_STRING)
2096 pos->width = pos->size * getFontWidth(pos->font);
2097 pos->height = getFontHeight(pos->font);
2099 else if (type == TYPE_ELEMENT)
2101 pos->width = pos->size;
2102 pos->height = pos->size;
2105 /* fill structure for game panel draw order */
2107 gpo->sort_priority = pos->sort_priority;
2110 /* sort game panel controls according to sort_priority and control number */
2111 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2112 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2115 void UpdatePlayfieldElementCount()
2117 boolean use_element_count = FALSE;
2120 /* first check if it is needed at all to calculate playfield element count */
2121 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2122 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2123 use_element_count = TRUE;
2125 if (!use_element_count)
2128 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2129 element_info[i].element_count = 0;
2131 SCAN_PLAYFIELD(x, y)
2133 element_info[Feld[x][y]].element_count++;
2136 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2137 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2138 if (IS_IN_GROUP(j, i))
2139 element_info[EL_GROUP_START + i].element_count +=
2140 element_info[j].element_count;
2143 void UpdateGameControlValues()
2146 int time = (local_player->LevelSolved ?
2147 local_player->LevelSolved_CountingTime :
2148 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149 level.native_em_level->lev->time :
2150 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151 level.native_sp_level->game_sp->time_played :
2152 game.no_time_limit ? TimePlayed : TimeLeft);
2153 int score = (local_player->LevelSolved ?
2154 local_player->LevelSolved_CountingScore :
2155 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2156 level.native_em_level->lev->score :
2157 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2158 level.native_sp_level->game_sp->score :
2159 local_player->score);
2160 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2161 level.native_em_level->lev->required :
2162 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2163 level.native_sp_level->game_sp->infotrons_still_needed :
2164 local_player->gems_still_needed);
2165 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2166 level.native_em_level->lev->required > 0 :
2167 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2168 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2169 local_player->gems_still_needed > 0 ||
2170 local_player->sokobanfields_still_needed > 0 ||
2171 local_player->lights_still_needed > 0);
2173 UpdatePlayfieldElementCount();
2175 /* update game panel control values */
2177 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2178 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2180 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2181 for (i = 0; i < MAX_NUM_KEYS; i++)
2182 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2183 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2184 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2186 if (game.centered_player_nr == -1)
2188 for (i = 0; i < MAX_PLAYERS; i++)
2190 /* only one player in Supaplex game engine */
2191 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2194 for (k = 0; k < MAX_NUM_KEYS; k++)
2196 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2198 if (level.native_em_level->ply[i]->keys & (1 << k))
2199 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2200 get_key_element_from_nr(k);
2202 else if (stored_player[i].key[k])
2203 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2204 get_key_element_from_nr(k);
2207 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2208 getPlayerInventorySize(i);
2210 if (stored_player[i].num_white_keys > 0)
2211 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2214 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2215 stored_player[i].num_white_keys;
2220 int player_nr = game.centered_player_nr;
2222 for (k = 0; k < MAX_NUM_KEYS; k++)
2224 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2227 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2228 get_key_element_from_nr(k);
2230 else if (stored_player[player_nr].key[k])
2231 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2232 get_key_element_from_nr(k);
2235 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2236 getPlayerInventorySize(player_nr);
2238 if (stored_player[player_nr].num_white_keys > 0)
2239 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2241 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2242 stored_player[player_nr].num_white_keys;
2245 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2247 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2248 get_inventory_element_from_pos(local_player, i);
2249 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2250 get_inventory_element_from_pos(local_player, -i - 1);
2253 game_panel_controls[GAME_PANEL_SCORE].value = score;
2254 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2256 game_panel_controls[GAME_PANEL_TIME].value = time;
2258 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2259 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2260 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2262 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2264 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2265 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2267 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2268 local_player->shield_normal_time_left;
2269 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2270 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2272 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2273 local_player->shield_deadly_time_left;
2275 game_panel_controls[GAME_PANEL_EXIT].value =
2276 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2278 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2279 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2280 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2281 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2282 EL_EMC_MAGIC_BALL_SWITCH);
2284 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2285 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2286 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2287 game.light_time_left;
2289 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2290 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2291 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2292 game.timegate_time_left;
2294 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2295 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2297 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2298 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2299 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2300 game.lenses_time_left;
2302 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2303 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2304 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2305 game.magnify_time_left;
2307 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2308 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2309 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2310 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2311 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2312 EL_BALLOON_SWITCH_NONE);
2314 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2315 local_player->dynabomb_count;
2316 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2317 local_player->dynabomb_size;
2318 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2319 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2321 game_panel_controls[GAME_PANEL_PENGUINS].value =
2322 local_player->friends_still_needed;
2324 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2325 local_player->sokobanfields_still_needed;
2326 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2327 local_player->sokobanfields_still_needed;
2329 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2330 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2332 for (i = 0; i < NUM_BELTS; i++)
2334 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2335 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2336 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2337 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2338 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2341 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2342 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2343 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2344 game.magic_wall_time_left;
2346 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2347 local_player->gravity;
2349 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2350 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2354 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2355 game.panel.element[i].id : EL_UNDEFINED);
2357 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2358 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2359 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2360 element_info[game.panel.element_count[i].id].element_count : 0);
2362 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2364 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2365 element_info[game.panel.ce_score[i].id].collect_score : 0);
2367 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2368 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2369 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2370 element_info[game.panel.ce_score_element[i].id].collect_score :
2373 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2374 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2375 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377 /* update game panel control frames */
2379 for (i = 0; game_panel_controls[i].nr != -1; i++)
2381 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383 if (gpc->type == TYPE_ELEMENT)
2385 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2387 int last_anim_random_frame = gfx.anim_random_frame;
2388 int element = gpc->value;
2389 int graphic = el2panelimg(element);
2391 if (gpc->value != gpc->last_value)
2394 gpc->gfx_random = INIT_GFX_RANDOM();
2400 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2401 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2402 gpc->gfx_random = INIT_GFX_RANDOM();
2405 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2406 gfx.anim_random_frame = gpc->gfx_random;
2408 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2409 gpc->gfx_frame = element_info[element].collect_score;
2411 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2414 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2415 gfx.anim_random_frame = last_anim_random_frame;
2421 void DisplayGameControlValues()
2423 boolean redraw_panel = FALSE;
2426 for (i = 0; game_panel_controls[i].nr != -1; i++)
2428 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2430 if (PANEL_DEACTIVATED(gpc->pos))
2433 if (gpc->value == gpc->last_value &&
2434 gpc->frame == gpc->last_frame)
2437 redraw_panel = TRUE;
2443 /* copy default game door content to main double buffer */
2445 /* !!! CHECK AGAIN !!! */
2446 SetPanelBackground();
2447 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2448 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2450 /* redraw game control buttons */
2451 RedrawGameButtons();
2453 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2455 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457 int nr = game_panel_order[i].nr;
2458 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2459 struct TextPosInfo *pos = gpc->pos;
2460 int type = gpc->type;
2461 int value = gpc->value;
2462 int frame = gpc->frame;
2463 int size = pos->size;
2464 int font = pos->font;
2465 boolean draw_masked = pos->draw_masked;
2466 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2468 if (PANEL_DEACTIVATED(pos))
2471 gpc->last_value = value;
2472 gpc->last_frame = frame;
2474 if (type == TYPE_INTEGER)
2476 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2477 nr == GAME_PANEL_TIME)
2479 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2481 if (use_dynamic_size) /* use dynamic number of digits */
2483 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2484 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2485 int size2 = size1 + 1;
2486 int font1 = pos->font;
2487 int font2 = pos->font_alt;
2489 size = (value < value_change ? size1 : size2);
2490 font = (value < value_change ? font1 : font2);
2494 /* correct text size if "digits" is zero or less */
2496 size = strlen(int2str(value, size));
2498 /* dynamically correct text alignment */
2499 pos->width = size * getFontWidth(font);
2501 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2502 int2str(value, size), font, mask_mode);
2504 else if (type == TYPE_ELEMENT)
2506 int element, graphic;
2510 int dst_x = PANEL_XPOS(pos);
2511 int dst_y = PANEL_YPOS(pos);
2513 if (value != EL_UNDEFINED && value != EL_EMPTY)
2516 graphic = el2panelimg(value);
2518 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2520 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2523 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2526 width = graphic_info[graphic].width * size / TILESIZE;
2527 height = graphic_info[graphic].height * size / TILESIZE;
2530 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2533 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2537 else if (type == TYPE_STRING)
2539 boolean active = (value != 0);
2540 char *state_normal = "off";
2541 char *state_active = "on";
2542 char *state = (active ? state_active : state_normal);
2543 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2544 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2545 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2546 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2548 if (nr == GAME_PANEL_GRAVITY_STATE)
2550 int font1 = pos->font; /* (used for normal state) */
2551 int font2 = pos->font_alt; /* (used for active state) */
2553 font = (active ? font2 : font1);
2562 /* don't truncate output if "chars" is zero or less */
2565 /* dynamically correct text alignment */
2566 pos->width = size * getFontWidth(font);
2569 s_cut = getStringCopyN(s, size);
2571 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2572 s_cut, font, mask_mode);
2578 redraw_mask |= REDRAW_DOOR_1;
2581 SetGameStatus(GAME_MODE_PLAYING);
2584 void UpdateAndDisplayGameControlValues()
2586 if (tape.deactivate_display)
2589 UpdateGameControlValues();
2590 DisplayGameControlValues();
2593 void UpdateGameDoorValues()
2595 UpdateGameControlValues();
2598 void DrawGameDoorValues()
2600 DisplayGameControlValues();
2605 =============================================================================
2607 -----------------------------------------------------------------------------
2608 initialize game engine due to level / tape version number
2609 =============================================================================
2612 static void InitGameEngine()
2614 int i, j, k, l, x, y;
2616 /* set game engine from tape file when re-playing, else from level file */
2617 game.engine_version = (tape.playing ? tape.engine_version :
2618 level.game_version);
2620 /* set single or multi-player game mode (needed for re-playing tapes) */
2621 game.team_mode = setup.team_mode;
2625 int num_players = 0;
2627 for (i = 0; i < MAX_PLAYERS; i++)
2628 if (tape.player_participates[i])
2631 /* multi-player tapes contain input data for more than one player */
2632 game.team_mode = (num_players > 1);
2635 /* ---------------------------------------------------------------------- */
2636 /* set flags for bugs and changes according to active game engine version */
2637 /* ---------------------------------------------------------------------- */
2640 Summary of bugfix/change:
2641 Fixed handling for custom elements that change when pushed by the player.
2643 Fixed/changed in version:
2647 Before 3.1.0, custom elements that "change when pushing" changed directly
2648 after the player started pushing them (until then handled in "DigField()").
2649 Since 3.1.0, these custom elements are not changed until the "pushing"
2650 move of the element is finished (now handled in "ContinueMoving()").
2652 Affected levels/tapes:
2653 The first condition is generally needed for all levels/tapes before version
2654 3.1.0, which might use the old behaviour before it was changed; known tapes
2655 that are affected are some tapes from the level set "Walpurgis Gardens" by
2657 The second condition is an exception from the above case and is needed for
2658 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2659 above (including some development versions of 3.1.0), but before it was
2660 known that this change would break tapes like the above and was fixed in
2661 3.1.1, so that the changed behaviour was active although the engine version
2662 while recording maybe was before 3.1.0. There is at least one tape that is
2663 affected by this exception, which is the tape for the one-level set "Bug
2664 Machine" by Juergen Bonhagen.
2667 game.use_change_when_pushing_bug =
2668 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2670 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2671 tape.game_version < VERSION_IDENT(3,1,1,0)));
2674 Summary of bugfix/change:
2675 Fixed handling for blocking the field the player leaves when moving.
2677 Fixed/changed in version:
2681 Before 3.1.1, when "block last field when moving" was enabled, the field
2682 the player is leaving when moving was blocked for the time of the move,
2683 and was directly unblocked afterwards. This resulted in the last field
2684 being blocked for exactly one less than the number of frames of one player
2685 move. Additionally, even when blocking was disabled, the last field was
2686 blocked for exactly one frame.
2687 Since 3.1.1, due to changes in player movement handling, the last field
2688 is not blocked at all when blocking is disabled. When blocking is enabled,
2689 the last field is blocked for exactly the number of frames of one player
2690 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2691 last field is blocked for exactly one more than the number of frames of
2694 Affected levels/tapes:
2695 (!!! yet to be determined -- probably many !!!)
2698 game.use_block_last_field_bug =
2699 (game.engine_version < VERSION_IDENT(3,1,1,0));
2701 game_em.use_single_button =
2702 (game.engine_version > VERSION_IDENT(4,0,0,2));
2704 /* ---------------------------------------------------------------------- */
2706 /* set maximal allowed number of custom element changes per game frame */
2707 game.max_num_changes_per_frame = 1;
2709 /* default scan direction: scan playfield from top/left to bottom/right */
2710 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2712 /* dynamically adjust element properties according to game engine version */
2713 InitElementPropertiesEngine(game.engine_version);
2716 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2717 printf(" tape version == %06d [%s] [file: %06d]\n",
2718 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2720 printf(" => game.engine_version == %06d\n", game.engine_version);
2723 /* ---------- initialize player's initial move delay --------------------- */
2725 /* dynamically adjust player properties according to level information */
2726 for (i = 0; i < MAX_PLAYERS; i++)
2727 game.initial_move_delay_value[i] =
2728 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2730 /* dynamically adjust player properties according to game engine version */
2731 for (i = 0; i < MAX_PLAYERS; i++)
2732 game.initial_move_delay[i] =
2733 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2734 game.initial_move_delay_value[i] : 0);
2736 /* ---------- initialize player's initial push delay --------------------- */
2738 /* dynamically adjust player properties according to game engine version */
2739 game.initial_push_delay_value =
2740 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2742 /* ---------- initialize changing elements ------------------------------- */
2744 /* initialize changing elements information */
2745 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2747 struct ElementInfo *ei = &element_info[i];
2749 /* this pointer might have been changed in the level editor */
2750 ei->change = &ei->change_page[0];
2752 if (!IS_CUSTOM_ELEMENT(i))
2754 ei->change->target_element = EL_EMPTY_SPACE;
2755 ei->change->delay_fixed = 0;
2756 ei->change->delay_random = 0;
2757 ei->change->delay_frames = 1;
2760 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2762 ei->has_change_event[j] = FALSE;
2764 ei->event_page_nr[j] = 0;
2765 ei->event_page[j] = &ei->change_page[0];
2769 /* add changing elements from pre-defined list */
2770 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2772 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2773 struct ElementInfo *ei = &element_info[ch_delay->element];
2775 ei->change->target_element = ch_delay->target_element;
2776 ei->change->delay_fixed = ch_delay->change_delay;
2778 ei->change->pre_change_function = ch_delay->pre_change_function;
2779 ei->change->change_function = ch_delay->change_function;
2780 ei->change->post_change_function = ch_delay->post_change_function;
2782 ei->change->can_change = TRUE;
2783 ei->change->can_change_or_has_action = TRUE;
2785 ei->has_change_event[CE_DELAY] = TRUE;
2787 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2788 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2791 /* ---------- initialize internal run-time variables --------------------- */
2793 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2795 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2797 for (j = 0; j < ei->num_change_pages; j++)
2799 ei->change_page[j].can_change_or_has_action =
2800 (ei->change_page[j].can_change |
2801 ei->change_page[j].has_action);
2805 /* add change events from custom element configuration */
2806 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2808 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2810 for (j = 0; j < ei->num_change_pages; j++)
2812 if (!ei->change_page[j].can_change_or_has_action)
2815 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2817 /* only add event page for the first page found with this event */
2818 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2820 ei->has_change_event[k] = TRUE;
2822 ei->event_page_nr[k] = j;
2823 ei->event_page[k] = &ei->change_page[j];
2829 /* ---------- initialize reference elements in change conditions --------- */
2831 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2833 int element = EL_CUSTOM_START + i;
2834 struct ElementInfo *ei = &element_info[element];
2836 for (j = 0; j < ei->num_change_pages; j++)
2838 int trigger_element = ei->change_page[j].initial_trigger_element;
2840 if (trigger_element >= EL_PREV_CE_8 &&
2841 trigger_element <= EL_NEXT_CE_8)
2842 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2844 ei->change_page[j].trigger_element = trigger_element;
2848 /* ---------- initialize run-time trigger player and element ------------- */
2850 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2852 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2854 for (j = 0; j < ei->num_change_pages; j++)
2856 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2857 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2858 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2859 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2860 ei->change_page[j].actual_trigger_ce_value = 0;
2861 ei->change_page[j].actual_trigger_ce_score = 0;
2865 /* ---------- initialize trigger events ---------------------------------- */
2867 /* initialize trigger events information */
2868 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2869 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2870 trigger_events[i][j] = FALSE;
2872 /* add trigger events from element change event properties */
2873 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2875 struct ElementInfo *ei = &element_info[i];
2877 for (j = 0; j < ei->num_change_pages; j++)
2879 if (!ei->change_page[j].can_change_or_has_action)
2882 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2884 int trigger_element = ei->change_page[j].trigger_element;
2886 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2888 if (ei->change_page[j].has_event[k])
2890 if (IS_GROUP_ELEMENT(trigger_element))
2892 struct ElementGroupInfo *group =
2893 element_info[trigger_element].group;
2895 for (l = 0; l < group->num_elements_resolved; l++)
2896 trigger_events[group->element_resolved[l]][k] = TRUE;
2898 else if (trigger_element == EL_ANY_ELEMENT)
2899 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2900 trigger_events[l][k] = TRUE;
2902 trigger_events[trigger_element][k] = TRUE;
2909 /* ---------- initialize push delay -------------------------------------- */
2911 /* initialize push delay values to default */
2912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2914 if (!IS_CUSTOM_ELEMENT(i))
2916 /* set default push delay values (corrected since version 3.0.7-1) */
2917 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2919 element_info[i].push_delay_fixed = 2;
2920 element_info[i].push_delay_random = 8;
2924 element_info[i].push_delay_fixed = 8;
2925 element_info[i].push_delay_random = 8;
2930 /* set push delay value for certain elements from pre-defined list */
2931 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2933 int e = push_delay_list[i].element;
2935 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2936 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2939 /* set push delay value for Supaplex elements for newer engine versions */
2940 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2942 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2944 if (IS_SP_ELEMENT(i))
2946 /* set SP push delay to just enough to push under a falling zonk */
2947 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2949 element_info[i].push_delay_fixed = delay;
2950 element_info[i].push_delay_random = 0;
2955 /* ---------- initialize move stepsize ----------------------------------- */
2957 /* initialize move stepsize values to default */
2958 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2959 if (!IS_CUSTOM_ELEMENT(i))
2960 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2962 /* set move stepsize value for certain elements from pre-defined list */
2963 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2965 int e = move_stepsize_list[i].element;
2967 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2970 /* ---------- initialize collect score ----------------------------------- */
2972 /* initialize collect score values for custom elements from initial value */
2973 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974 if (IS_CUSTOM_ELEMENT(i))
2975 element_info[i].collect_score = element_info[i].collect_score_initial;
2977 /* ---------- initialize collect count ----------------------------------- */
2979 /* initialize collect count values for non-custom elements */
2980 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2981 if (!IS_CUSTOM_ELEMENT(i))
2982 element_info[i].collect_count_initial = 0;
2984 /* add collect count values for all elements from pre-defined list */
2985 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2986 element_info[collect_count_list[i].element].collect_count_initial =
2987 collect_count_list[i].count;
2989 /* ---------- initialize access direction -------------------------------- */
2991 /* initialize access direction values to default (access from every side) */
2992 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993 if (!IS_CUSTOM_ELEMENT(i))
2994 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2996 /* set access direction value for certain elements from pre-defined list */
2997 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2998 element_info[access_direction_list[i].element].access_direction =
2999 access_direction_list[i].direction;
3001 /* ---------- initialize explosion content ------------------------------- */
3002 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3004 if (IS_CUSTOM_ELEMENT(i))
3007 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3009 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3011 element_info[i].content.e[x][y] =
3012 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3013 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3014 i == EL_PLAYER_3 ? EL_EMERALD :
3015 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3016 i == EL_MOLE ? EL_EMERALD_RED :
3017 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3018 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3019 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3020 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3021 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3022 i == EL_WALL_EMERALD ? EL_EMERALD :
3023 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3024 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3025 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3026 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3027 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3028 i == EL_WALL_PEARL ? EL_PEARL :
3029 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3034 /* ---------- initialize recursion detection ------------------------------ */
3035 recursion_loop_depth = 0;
3036 recursion_loop_detected = FALSE;
3037 recursion_loop_element = EL_UNDEFINED;
3039 /* ---------- initialize graphics engine ---------------------------------- */
3040 game.scroll_delay_value =
3041 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3042 setup.scroll_delay ? setup.scroll_delay_value : 0);
3043 game.scroll_delay_value =
3044 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3046 /* ---------- initialize game engine snapshots ---------------------------- */
3047 for (i = 0; i < MAX_PLAYERS; i++)
3048 game.snapshot.last_action[i] = 0;
3049 game.snapshot.changed_action = FALSE;
3050 game.snapshot.collected_item = FALSE;
3051 game.snapshot.mode =
3052 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3053 SNAPSHOT_MODE_EVERY_STEP :
3054 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3055 SNAPSHOT_MODE_EVERY_MOVE :
3056 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3057 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3058 game.snapshot.save_snapshot = FALSE;
3061 int get_num_special_action(int element, int action_first, int action_last)
3063 int num_special_action = 0;
3066 for (i = action_first; i <= action_last; i++)
3068 boolean found = FALSE;
3070 for (j = 0; j < NUM_DIRECTIONS; j++)
3071 if (el_act_dir2img(element, i, j) !=
3072 el_act_dir2img(element, ACTION_DEFAULT, j))
3076 num_special_action++;
3081 return num_special_action;
3086 =============================================================================
3088 -----------------------------------------------------------------------------
3089 initialize and start new game
3090 =============================================================================
3095 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3096 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3097 int fade_mask = REDRAW_FIELD;
3099 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3100 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3101 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3102 int initial_move_dir = MV_DOWN;
3105 // required here to update video display before fading (FIX THIS)
3106 DrawMaskedBorder(REDRAW_DOOR_2);
3108 if (!game.restart_level)
3109 CloseDoor(DOOR_CLOSE_1);
3111 SetGameStatus(GAME_MODE_PLAYING);
3113 if (level_editor_test_game)
3114 FadeSkipNextFadeIn();
3116 FadeSetEnterScreen();
3118 if (CheckIfGlobalBorderHasChanged())
3119 fade_mask = REDRAW_ALL;
3121 FadeSoundsAndMusic();
3123 ExpireSoundLoops(TRUE);
3127 /* needed if different viewport properties defined for playing */
3128 ChangeViewportPropertiesIfNeeded();
3132 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3134 DrawCompleteVideoDisplay();
3137 InitGameControlValues();
3139 /* don't play tapes over network */
3140 network_playing = (options.network && !tape.playing);
3142 for (i = 0; i < MAX_PLAYERS; i++)
3144 struct PlayerInfo *player = &stored_player[i];
3146 player->index_nr = i;
3147 player->index_bit = (1 << i);
3148 player->element_nr = EL_PLAYER_1 + i;
3150 player->present = FALSE;
3151 player->active = FALSE;
3152 player->mapped = FALSE;
3154 player->killed = FALSE;
3155 player->reanimated = FALSE;
3158 player->effective_action = 0;
3159 player->programmed_action = 0;
3162 player->score_final = 0;
3164 player->gems_still_needed = level.gems_needed;
3165 player->sokobanfields_still_needed = 0;
3166 player->lights_still_needed = 0;
3167 player->friends_still_needed = 0;
3169 for (j = 0; j < MAX_NUM_KEYS; j++)
3170 player->key[j] = FALSE;
3172 player->num_white_keys = 0;
3174 player->dynabomb_count = 0;
3175 player->dynabomb_size = 1;
3176 player->dynabombs_left = 0;
3177 player->dynabomb_xl = FALSE;
3179 player->MovDir = initial_move_dir;
3182 player->GfxDir = initial_move_dir;
3183 player->GfxAction = ACTION_DEFAULT;
3185 player->StepFrame = 0;
3187 player->initial_element = player->element_nr;
3188 player->artwork_element =
3189 (level.use_artwork_element[i] ? level.artwork_element[i] :
3190 player->element_nr);
3191 player->use_murphy = FALSE;
3193 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3194 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3196 player->gravity = level.initial_player_gravity[i];
3198 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3200 player->actual_frame_counter = 0;
3202 player->step_counter = 0;
3204 player->last_move_dir = initial_move_dir;
3206 player->is_active = FALSE;
3208 player->is_waiting = FALSE;
3209 player->is_moving = FALSE;
3210 player->is_auto_moving = FALSE;
3211 player->is_digging = FALSE;
3212 player->is_snapping = FALSE;
3213 player->is_collecting = FALSE;
3214 player->is_pushing = FALSE;
3215 player->is_switching = FALSE;
3216 player->is_dropping = FALSE;
3217 player->is_dropping_pressed = FALSE;
3219 player->is_bored = FALSE;
3220 player->is_sleeping = FALSE;
3222 player->was_waiting = TRUE;
3223 player->was_moving = FALSE;
3224 player->was_snapping = FALSE;
3225 player->was_dropping = FALSE;
3227 player->force_dropping = FALSE;
3229 player->frame_counter_bored = -1;
3230 player->frame_counter_sleeping = -1;
3232 player->anim_delay_counter = 0;
3233 player->post_delay_counter = 0;
3235 player->dir_waiting = initial_move_dir;
3236 player->action_waiting = ACTION_DEFAULT;
3237 player->last_action_waiting = ACTION_DEFAULT;
3238 player->special_action_bored = ACTION_DEFAULT;
3239 player->special_action_sleeping = ACTION_DEFAULT;
3241 player->switch_x = -1;
3242 player->switch_y = -1;
3244 player->drop_x = -1;
3245 player->drop_y = -1;
3247 player->show_envelope = 0;
3249 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3251 player->push_delay = -1; /* initialized when pushing starts */
3252 player->push_delay_value = game.initial_push_delay_value;
3254 player->drop_delay = 0;
3255 player->drop_pressed_delay = 0;
3257 player->last_jx = -1;
3258 player->last_jy = -1;
3262 player->shield_normal_time_left = 0;
3263 player->shield_deadly_time_left = 0;
3265 player->inventory_infinite_element = EL_UNDEFINED;
3266 player->inventory_size = 0;
3268 if (level.use_initial_inventory[i])
3270 for (j = 0; j < level.initial_inventory_size[i]; j++)
3272 int element = level.initial_inventory_content[i][j];
3273 int collect_count = element_info[element].collect_count_initial;
3276 if (!IS_CUSTOM_ELEMENT(element))
3279 if (collect_count == 0)
3280 player->inventory_infinite_element = element;
3282 for (k = 0; k < collect_count; k++)
3283 if (player->inventory_size < MAX_INVENTORY_SIZE)
3284 player->inventory_element[player->inventory_size++] = element;
3288 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3289 SnapField(player, 0, 0);
3291 player->LevelSolved = FALSE;
3292 player->GameOver = FALSE;
3294 player->LevelSolved_GameWon = FALSE;
3295 player->LevelSolved_GameEnd = FALSE;
3296 player->LevelSolved_PanelOff = FALSE;
3297 player->LevelSolved_SaveTape = FALSE;
3298 player->LevelSolved_SaveScore = FALSE;
3299 player->LevelSolved_CountingTime = 0;
3300 player->LevelSolved_CountingScore = 0;
3302 map_player_action[i] = i;
3305 network_player_action_received = FALSE;
3307 #if defined(NETWORK_AVALIABLE)
3308 /* initial null action */
3309 if (network_playing)
3310 SendToServer_MovePlayer(MV_NONE);
3319 TimeLeft = level.time;
3322 ScreenMovDir = MV_NONE;
3326 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3328 AllPlayersGone = FALSE;
3330 game.no_time_limit = (level.time == 0);
3332 game.yamyam_content_nr = 0;
3333 game.robot_wheel_active = FALSE;
3334 game.magic_wall_active = FALSE;
3335 game.magic_wall_time_left = 0;
3336 game.light_time_left = 0;
3337 game.timegate_time_left = 0;
3338 game.switchgate_pos = 0;
3339 game.wind_direction = level.wind_direction_initial;
3341 game.lenses_time_left = 0;
3342 game.magnify_time_left = 0;
3344 game.ball_state = level.ball_state_initial;
3345 game.ball_content_nr = 0;
3347 game.envelope_active = FALSE;
3349 /* set focus to local player for network games, else to all players */
3350 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3351 game.centered_player_nr_next = game.centered_player_nr;
3352 game.set_centered_player = FALSE;
3354 if (network_playing && tape.recording)
3356 /* store client dependent player focus when recording network games */
3357 tape.centered_player_nr_next = game.centered_player_nr_next;
3358 tape.set_centered_player = TRUE;
3361 for (i = 0; i < NUM_BELTS; i++)
3363 game.belt_dir[i] = MV_NONE;
3364 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3367 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3368 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3370 #if DEBUG_INIT_PLAYER
3373 printf("Player status at level initialization:\n");
3377 SCAN_PLAYFIELD(x, y)
3379 Feld[x][y] = level.field[x][y];
3380 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3381 ChangeDelay[x][y] = 0;
3382 ChangePage[x][y] = -1;
3383 CustomValue[x][y] = 0; /* initialized in InitField() */
3384 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3386 WasJustMoving[x][y] = 0;
3387 WasJustFalling[x][y] = 0;
3388 CheckCollision[x][y] = 0;
3389 CheckImpact[x][y] = 0;
3391 Pushed[x][y] = FALSE;
3393 ChangeCount[x][y] = 0;
3394 ChangeEvent[x][y] = -1;
3396 ExplodePhase[x][y] = 0;
3397 ExplodeDelay[x][y] = 0;
3398 ExplodeField[x][y] = EX_TYPE_NONE;
3400 RunnerVisit[x][y] = 0;
3401 PlayerVisit[x][y] = 0;
3404 GfxRandom[x][y] = INIT_GFX_RANDOM();
3405 GfxElement[x][y] = EL_UNDEFINED;
3406 GfxAction[x][y] = ACTION_DEFAULT;
3407 GfxDir[x][y] = MV_NONE;
3408 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3411 SCAN_PLAYFIELD(x, y)
3413 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3415 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3417 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3420 InitField(x, y, TRUE);
3422 ResetGfxAnimation(x, y);
3427 for (i = 0; i < MAX_PLAYERS; i++)
3429 struct PlayerInfo *player = &stored_player[i];
3431 /* set number of special actions for bored and sleeping animation */
3432 player->num_special_action_bored =
3433 get_num_special_action(player->artwork_element,
3434 ACTION_BORING_1, ACTION_BORING_LAST);
3435 player->num_special_action_sleeping =
3436 get_num_special_action(player->artwork_element,
3437 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3440 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3441 emulate_sb ? EMU_SOKOBAN :
3442 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3444 /* initialize type of slippery elements */
3445 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3447 if (!IS_CUSTOM_ELEMENT(i))
3449 /* default: elements slip down either to the left or right randomly */
3450 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3452 /* SP style elements prefer to slip down on the left side */
3453 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3454 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3456 /* BD style elements prefer to slip down on the left side */
3457 if (game.emulation == EMU_BOULDERDASH)
3458 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3462 /* initialize explosion and ignition delay */
3463 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3465 if (!IS_CUSTOM_ELEMENT(i))
3468 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3469 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3470 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3471 int last_phase = (num_phase + 1) * delay;
3472 int half_phase = (num_phase / 2) * delay;
3474 element_info[i].explosion_delay = last_phase - 1;
3475 element_info[i].ignition_delay = half_phase;
3477 if (i == EL_BLACK_ORB)
3478 element_info[i].ignition_delay = 1;
3482 /* correct non-moving belts to start moving left */
3483 for (i = 0; i < NUM_BELTS; i++)
3484 if (game.belt_dir[i] == MV_NONE)
3485 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3487 #if USE_NEW_PLAYER_ASSIGNMENTS
3488 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3489 /* choose default local player */
3490 local_player = &stored_player[0];
3492 for (i = 0; i < MAX_PLAYERS; i++)
3493 stored_player[i].connected = FALSE;
3495 local_player->connected = TRUE;
3496 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3500 for (i = 0; i < MAX_PLAYERS; i++)
3501 stored_player[i].connected = tape.player_participates[i];
3503 else if (game.team_mode && !options.network)
3505 /* try to guess locally connected team mode players (needed for correct
3506 assignment of player figures from level to locally playing players) */
3508 for (i = 0; i < MAX_PLAYERS; i++)
3509 if (setup.input[i].use_joystick ||
3510 setup.input[i].key.left != KSYM_UNDEFINED)
3511 stored_player[i].connected = TRUE;
3514 #if DEBUG_INIT_PLAYER
3517 printf("Player status after level initialization:\n");
3519 for (i = 0; i < MAX_PLAYERS; i++)
3521 struct PlayerInfo *player = &stored_player[i];
3523 printf("- player %d: present == %d, connected == %d, active == %d",
3529 if (local_player == player)
3530 printf(" (local player)");
3537 #if DEBUG_INIT_PLAYER
3539 printf("Reassigning players ...\n");
3542 /* check if any connected player was not found in playfield */
3543 for (i = 0; i < MAX_PLAYERS; i++)
3545 struct PlayerInfo *player = &stored_player[i];
3547 if (player->connected && !player->present)
3549 struct PlayerInfo *field_player = NULL;
3551 #if DEBUG_INIT_PLAYER
3553 printf("- looking for field player for player %d ...\n", i + 1);
3556 /* assign first free player found that is present in the playfield */
3558 /* first try: look for unmapped playfield player that is not connected */
3559 for (j = 0; j < MAX_PLAYERS; j++)
3560 if (field_player == NULL &&
3561 stored_player[j].present &&
3562 !stored_player[j].mapped &&
3563 !stored_player[j].connected)
3564 field_player = &stored_player[j];
3566 /* second try: look for *any* unmapped playfield player */
3567 for (j = 0; j < MAX_PLAYERS; j++)
3568 if (field_player == NULL &&
3569 stored_player[j].present &&
3570 !stored_player[j].mapped)
3571 field_player = &stored_player[j];
3573 if (field_player != NULL)
3575 int jx = field_player->jx, jy = field_player->jy;
3577 #if DEBUG_INIT_PLAYER
3579 printf("- found player %d\n", field_player->index_nr + 1);
3582 player->present = FALSE;
3583 player->active = FALSE;
3585 field_player->present = TRUE;
3586 field_player->active = TRUE;
3589 player->initial_element = field_player->initial_element;
3590 player->artwork_element = field_player->artwork_element;
3592 player->block_last_field = field_player->block_last_field;
3593 player->block_delay_adjustment = field_player->block_delay_adjustment;
3596 StorePlayer[jx][jy] = field_player->element_nr;
3598 field_player->jx = field_player->last_jx = jx;
3599 field_player->jy = field_player->last_jy = jy;
3601 if (local_player == player)
3602 local_player = field_player;
3604 map_player_action[field_player->index_nr] = i;
3606 field_player->mapped = TRUE;
3608 #if DEBUG_INIT_PLAYER
3610 printf("- map_player_action[%d] == %d\n",
3611 field_player->index_nr + 1, i + 1);
3616 if (player->connected && player->present)
3617 player->mapped = TRUE;
3620 #if DEBUG_INIT_PLAYER
3623 printf("Player status after player assignment (first stage):\n");
3625 for (i = 0; i < MAX_PLAYERS; i++)
3627 struct PlayerInfo *player = &stored_player[i];
3629 printf("- player %d: present == %d, connected == %d, active == %d",
3635 if (local_player == player)
3636 printf(" (local player)");
3645 /* check if any connected player was not found in playfield */
3646 for (i = 0; i < MAX_PLAYERS; i++)
3648 struct PlayerInfo *player = &stored_player[i];
3650 if (player->connected && !player->present)
3652 for (j = 0; j < MAX_PLAYERS; j++)
3654 struct PlayerInfo *field_player = &stored_player[j];
3655 int jx = field_player->jx, jy = field_player->jy;
3657 /* assign first free player found that is present in the playfield */
3658 if (field_player->present && !field_player->connected)
3660 player->present = TRUE;
3661 player->active = TRUE;
3663 field_player->present = FALSE;
3664 field_player->active = FALSE;
3666 player->initial_element = field_player->initial_element;
3667 player->artwork_element = field_player->artwork_element;
3669 player->block_last_field = field_player->block_last_field;
3670 player->block_delay_adjustment = field_player->block_delay_adjustment;
3672 StorePlayer[jx][jy] = player->element_nr;
3674 player->jx = player->last_jx = jx;
3675 player->jy = player->last_jy = jy;
3685 printf("::: local_player->present == %d\n", local_player->present);
3690 /* when playing a tape, eliminate all players who do not participate */
3692 #if USE_NEW_PLAYER_ASSIGNMENTS
3694 if (!game.team_mode)
3696 for (i = 0; i < MAX_PLAYERS; i++)
3698 if (stored_player[i].active &&
3699 !tape.player_participates[map_player_action[i]])
3701 struct PlayerInfo *player = &stored_player[i];
3702 int jx = player->jx, jy = player->jy;
3704 #if DEBUG_INIT_PLAYER
3706 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3709 player->active = FALSE;
3710 StorePlayer[jx][jy] = 0;
3711 Feld[jx][jy] = EL_EMPTY;
3718 for (i = 0; i < MAX_PLAYERS; i++)
3720 if (stored_player[i].active &&
3721 !tape.player_participates[i])
3723 struct PlayerInfo *player = &stored_player[i];
3724 int jx = player->jx, jy = player->jy;
3726 player->active = FALSE;
3727 StorePlayer[jx][jy] = 0;
3728 Feld[jx][jy] = EL_EMPTY;
3733 else if (!options.network && !game.team_mode) /* && !tape.playing */
3735 /* when in single player mode, eliminate all but the first active player */
3737 for (i = 0; i < MAX_PLAYERS; i++)
3739 if (stored_player[i].active)
3741 for (j = i + 1; j < MAX_PLAYERS; j++)
3743 if (stored_player[j].active)
3745 struct PlayerInfo *player = &stored_player[j];
3746 int jx = player->jx, jy = player->jy;
3748 player->active = FALSE;
3749 player->present = FALSE;
3751 StorePlayer[jx][jy] = 0;
3752 Feld[jx][jy] = EL_EMPTY;
3759 /* when recording the game, store which players take part in the game */
3762 #if USE_NEW_PLAYER_ASSIGNMENTS
3763 for (i = 0; i < MAX_PLAYERS; i++)
3764 if (stored_player[i].connected)
3765 tape.player_participates[i] = TRUE;
3767 for (i = 0; i < MAX_PLAYERS; i++)
3768 if (stored_player[i].active)
3769 tape.player_participates[i] = TRUE;
3773 #if DEBUG_INIT_PLAYER
3776 printf("Player status after player assignment (final stage):\n");
3778 for (i = 0; i < MAX_PLAYERS; i++)
3780 struct PlayerInfo *player = &stored_player[i];
3782 printf("- player %d: present == %d, connected == %d, active == %d",
3788 if (local_player == player)
3789 printf(" (local player)");
3796 if (BorderElement == EL_EMPTY)
3799 SBX_Right = lev_fieldx - SCR_FIELDX;
3801 SBY_Lower = lev_fieldy - SCR_FIELDY;
3806 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3808 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3811 if (full_lev_fieldx <= SCR_FIELDX)
3812 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3813 if (full_lev_fieldy <= SCR_FIELDY)
3814 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3816 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3818 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3821 /* if local player not found, look for custom element that might create
3822 the player (make some assumptions about the right custom element) */
3823 if (!local_player->present)
3825 int start_x = 0, start_y = 0;
3826 int found_rating = 0;
3827 int found_element = EL_UNDEFINED;
3828 int player_nr = local_player->index_nr;
3830 SCAN_PLAYFIELD(x, y)
3832 int element = Feld[x][y];
3837 if (level.use_start_element[player_nr] &&
3838 level.start_element[player_nr] == element &&
3845 found_element = element;
3848 if (!IS_CUSTOM_ELEMENT(element))
3851 if (CAN_CHANGE(element))
3853 for (i = 0; i < element_info[element].num_change_pages; i++)
3855 /* check for player created from custom element as single target */
3856 content = element_info[element].change_page[i].target_element;
3857 is_player = ELEM_IS_PLAYER(content);
3859 if (is_player && (found_rating < 3 ||
3860 (found_rating == 3 && element < found_element)))
3866 found_element = element;
3871 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3873 /* check for player created from custom element as explosion content */
3874 content = element_info[element].content.e[xx][yy];
3875 is_player = ELEM_IS_PLAYER(content);
3877 if (is_player && (found_rating < 2 ||
3878 (found_rating == 2 && element < found_element)))
3880 start_x = x + xx - 1;
3881 start_y = y + yy - 1;
3884 found_element = element;
3887 if (!CAN_CHANGE(element))
3890 for (i = 0; i < element_info[element].num_change_pages; i++)
3892 /* check for player created from custom element as extended target */
3894 element_info[element].change_page[i].target_content.e[xx][yy];
3896 is_player = ELEM_IS_PLAYER(content);
3898 if (is_player && (found_rating < 1 ||
3899 (found_rating == 1 && element < found_element)))
3901 start_x = x + xx - 1;
3902 start_y = y + yy - 1;
3905 found_element = element;
3911 scroll_x = SCROLL_POSITION_X(start_x);
3912 scroll_y = SCROLL_POSITION_Y(start_y);
3916 scroll_x = SCROLL_POSITION_X(local_player->jx);
3917 scroll_y = SCROLL_POSITION_Y(local_player->jy);
3920 /* !!! FIX THIS (START) !!! */
3921 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3923 InitGameEngine_EM();
3925 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3927 InitGameEngine_SP();
3931 DrawLevel(REDRAW_FIELD);
3934 /* after drawing the level, correct some elements */
3935 if (game.timegate_time_left == 0)
3936 CloseAllOpenTimegates();
3939 /* blit playfield from scroll buffer to normal back buffer for fading in */
3940 BlitScreenToBitmap(backbuffer);
3941 /* !!! FIX THIS (END) !!! */
3943 DrawMaskedBorder(fade_mask);
3948 // full screen redraw is required at this point in the following cases:
3949 // - special editor door undrawn when game was started from level editor
3950 // - drawing area (playfield) was changed and has to be removed completely
3951 redraw_mask = REDRAW_ALL;
3955 if (!game.restart_level)
3957 /* copy default game door content to main double buffer */
3959 /* !!! CHECK AGAIN !!! */
3960 SetPanelBackground();
3961 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3962 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3965 SetPanelBackground();
3966 SetDrawBackgroundMask(REDRAW_DOOR_1);
3968 UpdateAndDisplayGameControlValues();
3970 if (!game.restart_level)
3976 CreateGameButtons();
3978 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3979 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3980 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3985 /* copy actual game door content to door double buffer for OpenDoor() */
3986 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3988 OpenDoor(DOOR_OPEN_ALL);
3990 PlaySound(SND_GAME_STARTING);
3992 if (setup.sound_music)
3995 KeyboardAutoRepeatOffUnlessAutoplay();
3997 #if DEBUG_INIT_PLAYER
4000 printf("Player status (final):\n");
4002 for (i = 0; i < MAX_PLAYERS; i++)
4004 struct PlayerInfo *player = &stored_player[i];
4006 printf("- player %d: present == %d, connected == %d, active == %d",
4012 if (local_player == player)
4013 printf(" (local player)");
4026 if (!game.restart_level && !tape.playing)
4028 LevelStats_incPlayed(level_nr);
4030 SaveLevelSetup_SeriesInfo();
4033 game.restart_level = FALSE;
4035 SaveEngineSnapshotToListInitial();
4038 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4039 int actual_player_x, int actual_player_y)
4041 /* this is used for non-R'n'D game engines to update certain engine values */
4043 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4045 actual_player_x = correctLevelPosX_EM(actual_player_x);
4046 actual_player_y = correctLevelPosY_EM(actual_player_y);
4049 /* needed to determine if sounds are played within the visible screen area */
4050 scroll_x = actual_scroll_x;
4051 scroll_y = actual_scroll_y;
4053 /* needed to get player position for "follow finger" playing input method */
4054 local_player->jx = actual_player_x;
4055 local_player->jy = actual_player_y;
4058 void InitMovDir(int x, int y)
4060 int i, element = Feld[x][y];
4061 static int xy[4][2] =
4068 static int direction[3][4] =
4070 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4071 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4072 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4081 Feld[x][y] = EL_BUG;
4082 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4085 case EL_SPACESHIP_RIGHT:
4086 case EL_SPACESHIP_UP:
4087 case EL_SPACESHIP_LEFT:
4088 case EL_SPACESHIP_DOWN:
4089 Feld[x][y] = EL_SPACESHIP;
4090 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4093 case EL_BD_BUTTERFLY_RIGHT:
4094 case EL_BD_BUTTERFLY_UP:
4095 case EL_BD_BUTTERFLY_LEFT:
4096 case EL_BD_BUTTERFLY_DOWN:
4097 Feld[x][y] = EL_BD_BUTTERFLY;
4098 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4101 case EL_BD_FIREFLY_RIGHT:
4102 case EL_BD_FIREFLY_UP:
4103 case EL_BD_FIREFLY_LEFT:
4104 case EL_BD_FIREFLY_DOWN:
4105 Feld[x][y] = EL_BD_FIREFLY;
4106 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4109 case EL_PACMAN_RIGHT:
4111 case EL_PACMAN_LEFT:
4112 case EL_PACMAN_DOWN:
4113 Feld[x][y] = EL_PACMAN;
4114 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4117 case EL_YAMYAM_LEFT:
4118 case EL_YAMYAM_RIGHT:
4120 case EL_YAMYAM_DOWN:
4121 Feld[x][y] = EL_YAMYAM;
4122 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4125 case EL_SP_SNIKSNAK:
4126 MovDir[x][y] = MV_UP;
4129 case EL_SP_ELECTRON:
4130 MovDir[x][y] = MV_LEFT;
4137 Feld[x][y] = EL_MOLE;
4138 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4142 if (IS_CUSTOM_ELEMENT(element))
4144 struct ElementInfo *ei = &element_info[element];
4145 int move_direction_initial = ei->move_direction_initial;
4146 int move_pattern = ei->move_pattern;
4148 if (move_direction_initial == MV_START_PREVIOUS)
4150 if (MovDir[x][y] != MV_NONE)
4153 move_direction_initial = MV_START_AUTOMATIC;
4156 if (move_direction_initial == MV_START_RANDOM)
4157 MovDir[x][y] = 1 << RND(4);
4158 else if (move_direction_initial & MV_ANY_DIRECTION)
4159 MovDir[x][y] = move_direction_initial;
4160 else if (move_pattern == MV_ALL_DIRECTIONS ||
4161 move_pattern == MV_TURNING_LEFT ||
4162 move_pattern == MV_TURNING_RIGHT ||
4163 move_pattern == MV_TURNING_LEFT_RIGHT ||
4164 move_pattern == MV_TURNING_RIGHT_LEFT ||
4165 move_pattern == MV_TURNING_RANDOM)
4166 MovDir[x][y] = 1 << RND(4);
4167 else if (move_pattern == MV_HORIZONTAL)
4168 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4169 else if (move_pattern == MV_VERTICAL)
4170 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4171 else if (move_pattern & MV_ANY_DIRECTION)
4172 MovDir[x][y] = element_info[element].move_pattern;
4173 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4174 move_pattern == MV_ALONG_RIGHT_SIDE)
4176 /* use random direction as default start direction */
4177 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4178 MovDir[x][y] = 1 << RND(4);
4180 for (i = 0; i < NUM_DIRECTIONS; i++)
4182 int x1 = x + xy[i][0];
4183 int y1 = y + xy[i][1];
4185 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4187 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4188 MovDir[x][y] = direction[0][i];
4190 MovDir[x][y] = direction[1][i];
4199 MovDir[x][y] = 1 << RND(4);
4201 if (element != EL_BUG &&
4202 element != EL_SPACESHIP &&
4203 element != EL_BD_BUTTERFLY &&
4204 element != EL_BD_FIREFLY)
4207 for (i = 0; i < NUM_DIRECTIONS; i++)
4209 int x1 = x + xy[i][0];
4210 int y1 = y + xy[i][1];
4212 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4214 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4216 MovDir[x][y] = direction[0][i];
4219 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4220 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4222 MovDir[x][y] = direction[1][i];
4231 GfxDir[x][y] = MovDir[x][y];
4234 void InitAmoebaNr(int x, int y)
4237 int group_nr = AmoebeNachbarNr(x, y);
4241 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4243 if (AmoebaCnt[i] == 0)
4251 AmoebaNr[x][y] = group_nr;
4252 AmoebaCnt[group_nr]++;
4253 AmoebaCnt2[group_nr]++;
4256 static void PlayerWins(struct PlayerInfo *player)
4258 player->LevelSolved = TRUE;
4259 player->GameOver = TRUE;
4261 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4262 level.native_em_level->lev->score : player->score);
4264 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4266 player->LevelSolved_CountingScore = player->score_final;
4271 static int time, time_final;
4272 static int score, score_final;
4273 static int game_over_delay_1 = 0;
4274 static int game_over_delay_2 = 0;
4275 int game_over_delay_value_1 = 50;
4276 int game_over_delay_value_2 = 50;
4278 if (!local_player->LevelSolved_GameWon)
4282 /* do not start end game actions before the player stops moving (to exit) */
4283 if (local_player->MovPos)
4286 local_player->LevelSolved_GameWon = TRUE;
4287 local_player->LevelSolved_SaveTape = tape.recording;
4288 local_player->LevelSolved_SaveScore = !tape.playing;
4292 LevelStats_incSolved(level_nr);
4294 SaveLevelSetup_SeriesInfo();
4297 if (tape.auto_play) /* tape might already be stopped here */
4298 tape.auto_play_level_solved = TRUE;
4302 game_over_delay_1 = game_over_delay_value_1;
4303 game_over_delay_2 = game_over_delay_value_2;
4305 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4306 score = score_final = local_player->score_final;
4311 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4313 else if (game.no_time_limit && TimePlayed < 999)
4316 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4319 local_player->score_final = score_final;
4321 if (level_editor_test_game)
4324 score = score_final;
4326 local_player->LevelSolved_CountingTime = time;
4327 local_player->LevelSolved_CountingScore = score;
4329 game_panel_controls[GAME_PANEL_TIME].value = time;
4330 game_panel_controls[GAME_PANEL_SCORE].value = score;
4332 DisplayGameControlValues();
4335 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4337 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4339 /* close exit door after last player */
4340 if ((AllPlayersGone &&
4341 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4342 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4343 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4344 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4345 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4347 int element = Feld[ExitX][ExitY];
4349 Feld[ExitX][ExitY] =
4350 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4351 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4352 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4353 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4354 EL_EM_STEEL_EXIT_CLOSING);
4356 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4359 /* player disappears */
4360 DrawLevelField(ExitX, ExitY);
4363 for (i = 0; i < MAX_PLAYERS; i++)
4365 struct PlayerInfo *player = &stored_player[i];
4367 if (player->present)
4369 RemovePlayer(player);
4371 /* player disappears */
4372 DrawLevelField(player->jx, player->jy);
4377 PlaySound(SND_GAME_WINNING);
4380 if (game_over_delay_1 > 0)
4382 game_over_delay_1--;
4387 if (time != time_final)
4389 int time_to_go = ABS(time_final - time);
4390 int time_count_dir = (time < time_final ? +1 : -1);
4391 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4393 time += time_count_steps * time_count_dir;
4394 score += time_count_steps * level.score[SC_TIME_BONUS];
4396 local_player->LevelSolved_CountingTime = time;
4397 local_player->LevelSolved_CountingScore = score;
4399 game_panel_controls[GAME_PANEL_TIME].value = time;
4400 game_panel_controls[GAME_PANEL_SCORE].value = score;
4402 DisplayGameControlValues();
4404 if (time == time_final)
4405 StopSound(SND_GAME_LEVELTIME_BONUS);
4406 else if (setup.sound_loops)
4407 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4409 PlaySound(SND_GAME_LEVELTIME_BONUS);
4414 local_player->LevelSolved_PanelOff = TRUE;
4416 if (game_over_delay_2 > 0)
4418 game_over_delay_2--;
4429 boolean raise_level = FALSE;
4431 local_player->LevelSolved_GameEnd = TRUE;
4433 if (!global.use_envelope_request)
4434 CloseDoor(DOOR_CLOSE_1);
4436 if (local_player->LevelSolved_SaveTape)
4438 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4441 CloseDoor(DOOR_CLOSE_ALL);
4443 if (level_editor_test_game)
4445 SetGameStatus(GAME_MODE_MAIN);
4452 if (!local_player->LevelSolved_SaveScore)
4454 SetGameStatus(GAME_MODE_MAIN);
4461 if (level_nr == leveldir_current->handicap_level)
4463 leveldir_current->handicap_level++;
4465 SaveLevelSetup_SeriesInfo();
4468 if (setup.increment_levels &&
4469 level_nr < leveldir_current->last_level)
4470 raise_level = TRUE; /* advance to next level */
4472 if ((hi_pos = NewHiScore()) >= 0)
4474 SetGameStatus(GAME_MODE_SCORES);
4476 DrawHallOfFame(hi_pos);
4486 SetGameStatus(GAME_MODE_MAIN);
4502 boolean one_score_entry_per_name = !program.many_scores_per_name;
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;
4520 if (one_score_entry_per_name)
4522 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4523 if (strEqual(setup.player_name, highscore[l].Name))
4526 if (m == k) /* player's new highscore overwrites his old one */
4530 for (l = m; l > k; l--)
4532 strcpy(highscore[l].Name, highscore[l - 1].Name);
4533 highscore[l].Score = highscore[l - 1].Score;
4539 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4540 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4541 highscore[k].Score = local_player->score_final;
4546 else if (one_score_entry_per_name &&
4547 !strncmp(setup.player_name, highscore[k].Name,
4548 MAX_PLAYER_NAME_LEN))
4549 break; /* player already there with a higher score */
4553 SaveScore(level_nr);
4558 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4560 int element = Feld[x][y];
4561 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4562 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4563 int horiz_move = (dx != 0);
4564 int sign = (horiz_move ? dx : dy);
4565 int step = sign * element_info[element].move_stepsize;
4567 /* special values for move stepsize for spring and things on conveyor belt */
4570 if (CAN_FALL(element) &&
4571 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4572 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4573 else if (element == EL_SPRING)
4574 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4580 inline static int getElementMoveStepsize(int x, int y)
4582 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4585 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4587 if (player->GfxAction != action || player->GfxDir != dir)
4589 player->GfxAction = action;
4590 player->GfxDir = dir;
4592 player->StepFrame = 0;
4596 static void ResetGfxFrame(int x, int y)
4598 int element = Feld[x][y];
4599 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4601 if (graphic_info[graphic].anim_global_sync)
4602 GfxFrame[x][y] = FrameCounter;
4603 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4604 GfxFrame[x][y] = CustomValue[x][y];
4605 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4606 GfxFrame[x][y] = element_info[element].collect_score;
4607 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4608 GfxFrame[x][y] = ChangeDelay[x][y];
4611 static void ResetGfxAnimation(int x, int y)
4613 GfxAction[x][y] = ACTION_DEFAULT;
4614 GfxDir[x][y] = MovDir[x][y];
4617 ResetGfxFrame(x, y);
4620 static void ResetRandomAnimationValue(int x, int y)
4622 GfxRandom[x][y] = INIT_GFX_RANDOM();
4625 void InitMovingField(int x, int y, int direction)
4627 int element = Feld[x][y];
4628 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4629 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4632 boolean is_moving_before, is_moving_after;
4634 /* check if element was/is moving or being moved before/after mode change */
4635 is_moving_before = (WasJustMoving[x][y] != 0);
4636 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4638 /* reset animation only for moving elements which change direction of moving
4639 or which just started or stopped moving
4640 (else CEs with property "can move" / "not moving" are reset each frame) */
4641 if (is_moving_before != is_moving_after ||
4642 direction != MovDir[x][y])
4643 ResetGfxAnimation(x, y);
4645 MovDir[x][y] = direction;
4646 GfxDir[x][y] = direction;
4648 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4649 direction == MV_DOWN && CAN_FALL(element) ?
4650 ACTION_FALLING : ACTION_MOVING);
4652 /* this is needed for CEs with property "can move" / "not moving" */
4654 if (is_moving_after)
4656 if (Feld[newx][newy] == EL_EMPTY)
4657 Feld[newx][newy] = EL_BLOCKED;
4659 MovDir[newx][newy] = MovDir[x][y];
4661 CustomValue[newx][newy] = CustomValue[x][y];
4663 GfxFrame[newx][newy] = GfxFrame[x][y];
4664 GfxRandom[newx][newy] = GfxRandom[x][y];
4665 GfxAction[newx][newy] = GfxAction[x][y];
4666 GfxDir[newx][newy] = GfxDir[x][y];
4670 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4672 int direction = MovDir[x][y];
4673 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4674 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4680 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4682 int oldx = x, oldy = y;
4683 int direction = MovDir[x][y];
4685 if (direction == MV_LEFT)
4687 else if (direction == MV_RIGHT)
4689 else if (direction == MV_UP)
4691 else if (direction == MV_DOWN)
4694 *comes_from_x = oldx;
4695 *comes_from_y = oldy;
4698 int MovingOrBlocked2Element(int x, int y)
4700 int element = Feld[x][y];
4702 if (element == EL_BLOCKED)
4706 Blocked2Moving(x, y, &oldx, &oldy);
4707 return Feld[oldx][oldy];
4713 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4715 /* like MovingOrBlocked2Element(), but if element is moving
4716 and (x,y) is the field the moving element is just leaving,
4717 return EL_BLOCKED instead of the element value */
4718 int element = Feld[x][y];
4720 if (IS_MOVING(x, y))
4722 if (element == EL_BLOCKED)
4726 Blocked2Moving(x, y, &oldx, &oldy);
4727 return Feld[oldx][oldy];
4736 static void RemoveField(int x, int y)
4738 Feld[x][y] = EL_EMPTY;
4744 CustomValue[x][y] = 0;
4747 ChangeDelay[x][y] = 0;
4748 ChangePage[x][y] = -1;
4749 Pushed[x][y] = FALSE;
4751 GfxElement[x][y] = EL_UNDEFINED;
4752 GfxAction[x][y] = ACTION_DEFAULT;
4753 GfxDir[x][y] = MV_NONE;
4756 void RemoveMovingField(int x, int y)
4758 int oldx = x, oldy = y, newx = x, newy = y;
4759 int element = Feld[x][y];
4760 int next_element = EL_UNDEFINED;
4762 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4765 if (IS_MOVING(x, y))
4767 Moving2Blocked(x, y, &newx, &newy);
4769 if (Feld[newx][newy] != EL_BLOCKED)
4771 /* element is moving, but target field is not free (blocked), but
4772 already occupied by something different (example: acid pool);
4773 in this case, only remove the moving field, but not the target */
4775 RemoveField(oldx, oldy);
4777 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4779 TEST_DrawLevelField(oldx, oldy);
4784 else if (element == EL_BLOCKED)
4786 Blocked2Moving(x, y, &oldx, &oldy);
4787 if (!IS_MOVING(oldx, oldy))
4791 if (element == EL_BLOCKED &&
4792 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4793 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4794 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4795 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4796 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4797 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4798 next_element = get_next_element(Feld[oldx][oldy]);
4800 RemoveField(oldx, oldy);
4801 RemoveField(newx, newy);
4803 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4805 if (next_element != EL_UNDEFINED)
4806 Feld[oldx][oldy] = next_element;
4808 TEST_DrawLevelField(oldx, oldy);
4809 TEST_DrawLevelField(newx, newy);
4812 void DrawDynamite(int x, int y)
4814 int sx = SCREENX(x), sy = SCREENY(y);
4815 int graphic = el2img(Feld[x][y]);
4818 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4821 if (IS_WALKABLE_INSIDE(Back[x][y]))
4825 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4826 else if (Store[x][y])
4827 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4829 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4831 if (Back[x][y] || Store[x][y])
4832 DrawGraphicThruMask(sx, sy, graphic, frame);
4834 DrawGraphic(sx, sy, graphic, frame);
4837 void CheckDynamite(int x, int y)
4839 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4843 if (MovDelay[x][y] != 0)
4846 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4852 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4857 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4859 boolean num_checked_players = 0;
4862 for (i = 0; i < MAX_PLAYERS; i++)
4864 if (stored_player[i].active)
4866 int sx = stored_player[i].jx;
4867 int sy = stored_player[i].jy;
4869 if (num_checked_players == 0)
4876 *sx1 = MIN(*sx1, sx);
4877 *sy1 = MIN(*sy1, sy);
4878 *sx2 = MAX(*sx2, sx);
4879 *sy2 = MAX(*sy2, sy);
4882 num_checked_players++;
4887 static boolean checkIfAllPlayersFitToScreen_RND()
4889 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4891 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4893 return (sx2 - sx1 < SCR_FIELDX &&
4894 sy2 - sy1 < SCR_FIELDY);
4897 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4899 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4901 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4903 *sx = (sx1 + sx2) / 2;
4904 *sy = (sy1 + sy2) / 2;
4907 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4908 boolean center_screen, boolean quick_relocation)
4910 unsigned int frame_delay_value_old = GetVideoFrameDelay();
4911 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4912 boolean no_delay = (tape.warp_forward);
4913 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4914 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4915 int new_scroll_x, new_scroll_y;
4917 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4919 /* case 1: quick relocation inside visible screen (without scrolling) */
4926 if (!level.shifted_relocation || center_screen)
4928 /* relocation _with_ centering of screen */
4930 new_scroll_x = SCROLL_POSITION_X(x);
4931 new_scroll_y = SCROLL_POSITION_Y(y);
4935 /* relocation _without_ centering of screen */
4937 int center_scroll_x = SCROLL_POSITION_X(old_x);
4938 int center_scroll_y = SCROLL_POSITION_Y(old_y);
4939 int offset_x = x + (scroll_x - center_scroll_x);
4940 int offset_y = y + (scroll_y - center_scroll_y);
4942 /* for new screen position, apply previous offset to center position */
4943 new_scroll_x = SCROLL_POSITION_X(offset_x);
4944 new_scroll_y = SCROLL_POSITION_Y(offset_y);
4947 if (quick_relocation)
4949 /* case 2: quick relocation (redraw without visible scrolling) */
4951 scroll_x = new_scroll_x;
4952 scroll_y = new_scroll_y;
4959 /* case 3: visible relocation (with scrolling to new position) */
4961 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4963 SetVideoFrameDelay(wait_delay_value);
4965 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4968 int fx = FX, fy = FY;
4970 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4971 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4973 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4979 fx += dx * TILEX / 2;
4980 fy += dy * TILEY / 2;
4982 ScrollLevel(dx, dy);
4985 /* scroll in two steps of half tile size to make things smoother */
4986 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4988 /* scroll second step to align at full tile size */
4989 BlitScreenToBitmap(window);
4995 SetVideoFrameDelay(frame_delay_value_old);
4998 void RelocatePlayer(int jx, int jy, int el_player_raw)
5000 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5001 int player_nr = GET_PLAYER_NR(el_player);
5002 struct PlayerInfo *player = &stored_player[player_nr];
5003 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5004 boolean no_delay = (tape.warp_forward);
5005 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5006 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5007 int old_jx = player->jx;
5008 int old_jy = player->jy;
5009 int old_element = Feld[old_jx][old_jy];
5010 int element = Feld[jx][jy];
5011 boolean player_relocated = (old_jx != jx || old_jy != jy);
5013 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5014 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5015 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5016 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5017 int leave_side_horiz = move_dir_horiz;
5018 int leave_side_vert = move_dir_vert;
5019 int enter_side = enter_side_horiz | enter_side_vert;
5020 int leave_side = leave_side_horiz | leave_side_vert;
5022 if (player->GameOver) /* do not reanimate dead player */
5025 if (!player_relocated) /* no need to relocate the player */
5028 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5030 RemoveField(jx, jy); /* temporarily remove newly placed player */
5031 DrawLevelField(jx, jy);
5034 if (player->present)
5036 while (player->MovPos)
5038 ScrollPlayer(player, SCROLL_GO_ON);
5039 ScrollScreen(NULL, SCROLL_GO_ON);
5041 AdvanceFrameAndPlayerCounters(player->index_nr);
5045 BackToFront_WithFrameDelay(wait_delay_value);
5048 DrawPlayer(player); /* needed here only to cleanup last field */
5049 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5051 player->is_moving = FALSE;
5054 if (IS_CUSTOM_ELEMENT(old_element))
5055 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5057 player->index_bit, leave_side);
5059 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5061 player->index_bit, leave_side);
5063 Feld[jx][jy] = el_player;
5064 InitPlayerField(jx, jy, el_player, TRUE);
5066 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5067 possible that the relocation target field did not contain a player element,
5068 but a walkable element, to which the new player was relocated -- in this
5069 case, restore that (already initialized!) element on the player field */
5070 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5072 Feld[jx][jy] = element; /* restore previously existing element */
5075 /* only visually relocate centered player */
5076 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5077 FALSE, level.instant_relocation);
5079 TestIfPlayerTouchesBadThing(jx, jy);
5080 TestIfPlayerTouchesCustomElement(jx, jy);
5082 if (IS_CUSTOM_ELEMENT(element))
5083 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5084 player->index_bit, enter_side);
5086 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5087 player->index_bit, enter_side);
5089 if (player->is_switching)
5091 /* ensure that relocation while still switching an element does not cause
5092 a new element to be treated as also switched directly after relocation
5093 (this is important for teleporter switches that teleport the player to
5094 a place where another teleporter switch is in the same direction, which
5095 would then incorrectly be treated as immediately switched before the
5096 direction key that caused the switch was released) */
5098 player->switch_x += jx - old_jx;
5099 player->switch_y += jy - old_jy;
5103 void Explode(int ex, int ey, int phase, int mode)
5109 /* !!! eliminate this variable !!! */
5110 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5112 if (game.explosions_delayed)
5114 ExplodeField[ex][ey] = mode;
5118 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5120 int center_element = Feld[ex][ey];
5121 int artwork_element, explosion_element; /* set these values later */
5123 /* remove things displayed in background while burning dynamite */
5124 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5127 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5129 /* put moving element to center field (and let it explode there) */
5130 center_element = MovingOrBlocked2Element(ex, ey);
5131 RemoveMovingField(ex, ey);
5132 Feld[ex][ey] = center_element;
5135 /* now "center_element" is finally determined -- set related values now */
5136 artwork_element = center_element; /* for custom player artwork */
5137 explosion_element = center_element; /* for custom player artwork */
5139 if (IS_PLAYER(ex, ey))
5141 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5143 artwork_element = stored_player[player_nr].artwork_element;
5145 if (level.use_explosion_element[player_nr])
5147 explosion_element = level.explosion_element[player_nr];
5148 artwork_element = explosion_element;
5152 if (mode == EX_TYPE_NORMAL ||
5153 mode == EX_TYPE_CENTER ||
5154 mode == EX_TYPE_CROSS)
5155 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5157 last_phase = element_info[explosion_element].explosion_delay + 1;
5159 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5161 int xx = x - ex + 1;
5162 int yy = y - ey + 1;
5165 if (!IN_LEV_FIELD(x, y) ||
5166 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5167 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5170 element = Feld[x][y];
5172 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5174 element = MovingOrBlocked2Element(x, y);
5176 if (!IS_EXPLOSION_PROOF(element))
5177 RemoveMovingField(x, y);
5180 /* indestructible elements can only explode in center (but not flames) */
5181 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5182 mode == EX_TYPE_BORDER)) ||
5183 element == EL_FLAMES)
5186 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5187 behaviour, for example when touching a yamyam that explodes to rocks
5188 with active deadly shield, a rock is created under the player !!! */
5189 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5191 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5192 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5193 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5195 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5198 if (IS_ACTIVE_BOMB(element))
5200 /* re-activate things under the bomb like gate or penguin */
5201 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5208 /* save walkable background elements while explosion on same tile */
5209 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5210 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5211 Back[x][y] = element;
5213 /* ignite explodable elements reached by other explosion */
5214 if (element == EL_EXPLOSION)
5215 element = Store2[x][y];
5217 if (AmoebaNr[x][y] &&
5218 (element == EL_AMOEBA_FULL ||
5219 element == EL_BD_AMOEBA ||
5220 element == EL_AMOEBA_GROWING))
5222 AmoebaCnt[AmoebaNr[x][y]]--;
5223 AmoebaCnt2[AmoebaNr[x][y]]--;
5228 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5230 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5232 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5234 if (PLAYERINFO(ex, ey)->use_murphy)
5235 Store[x][y] = EL_EMPTY;
5238 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5239 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5240 else if (ELEM_IS_PLAYER(center_element))
5241 Store[x][y] = EL_EMPTY;
5242 else if (center_element == EL_YAMYAM)
5243 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5244 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5245 Store[x][y] = element_info[center_element].content.e[xx][yy];
5247 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5248 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5249 otherwise) -- FIX THIS !!! */
5250 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5251 Store[x][y] = element_info[element].content.e[1][1];
5253 else if (!CAN_EXPLODE(element))
5254 Store[x][y] = element_info[element].content.e[1][1];
5257 Store[x][y] = EL_EMPTY;
5259 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5260 center_element == EL_AMOEBA_TO_DIAMOND)
5261 Store2[x][y] = element;
5263 Feld[x][y] = EL_EXPLOSION;
5264 GfxElement[x][y] = artwork_element;
5266 ExplodePhase[x][y] = 1;
5267 ExplodeDelay[x][y] = last_phase;
5272 if (center_element == EL_YAMYAM)
5273 game.yamyam_content_nr =
5274 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5286 GfxFrame[x][y] = 0; /* restart explosion animation */
5288 last_phase = ExplodeDelay[x][y];
5290 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5292 /* this can happen if the player leaves an explosion just in time */
5293 if (GfxElement[x][y] == EL_UNDEFINED)
5294 GfxElement[x][y] = EL_EMPTY;
5296 border_element = Store2[x][y];
5297 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5298 border_element = StorePlayer[x][y];
5300 if (phase == element_info[border_element].ignition_delay ||
5301 phase == last_phase)
5303 boolean border_explosion = FALSE;
5305 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5306 !PLAYER_EXPLOSION_PROTECTED(x, y))
5308 KillPlayerUnlessExplosionProtected(x, y);
5309 border_explosion = TRUE;
5311 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5313 Feld[x][y] = Store2[x][y];
5316 border_explosion = TRUE;
5318 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5320 AmoebeUmwandeln(x, y);
5322 border_explosion = TRUE;
5325 /* if an element just explodes due to another explosion (chain-reaction),
5326 do not immediately end the new explosion when it was the last frame of
5327 the explosion (as it would be done in the following "if"-statement!) */
5328 if (border_explosion && phase == last_phase)
5332 if (phase == last_phase)
5336 element = Feld[x][y] = Store[x][y];
5337 Store[x][y] = Store2[x][y] = 0;
5338 GfxElement[x][y] = EL_UNDEFINED;
5340 /* player can escape from explosions and might therefore be still alive */
5341 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5342 element <= EL_PLAYER_IS_EXPLODING_4)
5344 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5345 int explosion_element = EL_PLAYER_1 + player_nr;
5346 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5347 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5349 if (level.use_explosion_element[player_nr])
5350 explosion_element = level.explosion_element[player_nr];
5352 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5353 element_info[explosion_element].content.e[xx][yy]);
5356 /* restore probably existing indestructible background element */
5357 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5358 element = Feld[x][y] = Back[x][y];
5361 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5362 GfxDir[x][y] = MV_NONE;
5363 ChangeDelay[x][y] = 0;
5364 ChangePage[x][y] = -1;
5366 CustomValue[x][y] = 0;
5368 InitField_WithBug2(x, y, FALSE);
5370 TEST_DrawLevelField(x, y);
5372 TestIfElementTouchesCustomElement(x, y);
5374 if (GFX_CRUMBLED(element))
5375 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5377 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5378 StorePlayer[x][y] = 0;
5380 if (ELEM_IS_PLAYER(element))
5381 RelocatePlayer(x, y, element);
5383 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5385 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5386 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5389 TEST_DrawLevelFieldCrumbled(x, y);
5391 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5393 DrawLevelElement(x, y, Back[x][y]);
5394 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5396 else if (IS_WALKABLE_UNDER(Back[x][y]))
5398 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5399 DrawLevelElementThruMask(x, y, Back[x][y]);
5401 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5402 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5406 void DynaExplode(int ex, int ey)
5409 int dynabomb_element = Feld[ex][ey];
5410 int dynabomb_size = 1;
5411 boolean dynabomb_xl = FALSE;
5412 struct PlayerInfo *player;
5413 static int xy[4][2] =
5421 if (IS_ACTIVE_BOMB(dynabomb_element))
5423 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5424 dynabomb_size = player->dynabomb_size;
5425 dynabomb_xl = player->dynabomb_xl;
5426 player->dynabombs_left++;
5429 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5431 for (i = 0; i < NUM_DIRECTIONS; i++)
5433 for (j = 1; j <= dynabomb_size; j++)
5435 int x = ex + j * xy[i][0];
5436 int y = ey + j * xy[i][1];
5439 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5442 element = Feld[x][y];
5444 /* do not restart explosions of fields with active bombs */
5445 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5448 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5450 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5451 !IS_DIGGABLE(element) && !dynabomb_xl)
5457 void Bang(int x, int y)
5459 int element = MovingOrBlocked2Element(x, y);
5460 int explosion_type = EX_TYPE_NORMAL;
5462 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5464 struct PlayerInfo *player = PLAYERINFO(x, y);
5466 element = Feld[x][y] = player->initial_element;
5468 if (level.use_explosion_element[player->index_nr])
5470 int explosion_element = level.explosion_element[player->index_nr];
5472 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5473 explosion_type = EX_TYPE_CROSS;
5474 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5475 explosion_type = EX_TYPE_CENTER;
5483 case EL_BD_BUTTERFLY:
5486 case EL_DARK_YAMYAM:
5490 RaiseScoreElement(element);
5493 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5494 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5495 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5496 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5497 case EL_DYNABOMB_INCREASE_NUMBER:
5498 case EL_DYNABOMB_INCREASE_SIZE:
5499 case EL_DYNABOMB_INCREASE_POWER:
5500 explosion_type = EX_TYPE_DYNA;
5503 case EL_DC_LANDMINE:
5504 explosion_type = EX_TYPE_CENTER;
5509 case EL_LAMP_ACTIVE:
5510 case EL_AMOEBA_TO_DIAMOND:
5511 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5512 explosion_type = EX_TYPE_CENTER;
5516 if (element_info[element].explosion_type == EXPLODES_CROSS)
5517 explosion_type = EX_TYPE_CROSS;
5518 else if (element_info[element].explosion_type == EXPLODES_1X1)
5519 explosion_type = EX_TYPE_CENTER;
5523 if (explosion_type == EX_TYPE_DYNA)
5526 Explode(x, y, EX_PHASE_START, explosion_type);
5528 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5531 void SplashAcid(int x, int y)
5533 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5534 (!IN_LEV_FIELD(x - 1, y - 2) ||
5535 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5536 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5538 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5539 (!IN_LEV_FIELD(x + 1, y - 2) ||
5540 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5541 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5543 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5546 static void InitBeltMovement()
5548 static int belt_base_element[4] =
5550 EL_CONVEYOR_BELT_1_LEFT,
5551 EL_CONVEYOR_BELT_2_LEFT,
5552 EL_CONVEYOR_BELT_3_LEFT,
5553 EL_CONVEYOR_BELT_4_LEFT
5555 static int belt_base_active_element[4] =
5557 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5558 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5559 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5560 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5565 /* set frame order for belt animation graphic according to belt direction */
5566 for (i = 0; i < NUM_BELTS; i++)
5570 for (j = 0; j < NUM_BELT_PARTS; j++)
5572 int element = belt_base_active_element[belt_nr] + j;
5573 int graphic_1 = el2img(element);
5574 int graphic_2 = el2panelimg(element);
5576 if (game.belt_dir[i] == MV_LEFT)
5578 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5579 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5583 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5584 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5589 SCAN_PLAYFIELD(x, y)
5591 int element = Feld[x][y];
5593 for (i = 0; i < NUM_BELTS; i++)
5595 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5597 int e_belt_nr = getBeltNrFromBeltElement(element);
5600 if (e_belt_nr == belt_nr)
5602 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5604 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5611 static void ToggleBeltSwitch(int x, int y)
5613 static int belt_base_element[4] =
5615 EL_CONVEYOR_BELT_1_LEFT,
5616 EL_CONVEYOR_BELT_2_LEFT,
5617 EL_CONVEYOR_BELT_3_LEFT,
5618 EL_CONVEYOR_BELT_4_LEFT
5620 static int belt_base_active_element[4] =
5622 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5623 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5624 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5625 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5627 static int belt_base_switch_element[4] =
5629 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5630 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5631 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5632 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5634 static int belt_move_dir[4] =
5642 int element = Feld[x][y];
5643 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5644 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5645 int belt_dir = belt_move_dir[belt_dir_nr];
5648 if (!IS_BELT_SWITCH(element))
5651 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5652 game.belt_dir[belt_nr] = belt_dir;
5654 if (belt_dir_nr == 3)
5657 /* set frame order for belt animation graphic according to belt direction */
5658 for (i = 0; i < NUM_BELT_PARTS; i++)
5660 int element = belt_base_active_element[belt_nr] + i;
5661 int graphic_1 = el2img(element);
5662 int graphic_2 = el2panelimg(element);
5664 if (belt_dir == MV_LEFT)
5666 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5667 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5671 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5672 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5676 SCAN_PLAYFIELD(xx, yy)
5678 int element = Feld[xx][yy];
5680 if (IS_BELT_SWITCH(element))
5682 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5684 if (e_belt_nr == belt_nr)
5686 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5687 TEST_DrawLevelField(xx, yy);
5690 else if (IS_BELT(element) && belt_dir != MV_NONE)
5692 int e_belt_nr = getBeltNrFromBeltElement(element);
5694 if (e_belt_nr == belt_nr)
5696 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5698 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5699 TEST_DrawLevelField(xx, yy);
5702 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5704 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5706 if (e_belt_nr == belt_nr)
5708 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5710 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5711 TEST_DrawLevelField(xx, yy);
5717 static void ToggleSwitchgateSwitch(int x, int y)
5721 game.switchgate_pos = !game.switchgate_pos;
5723 SCAN_PLAYFIELD(xx, yy)
5725 int element = Feld[xx][yy];
5727 if (element == EL_SWITCHGATE_SWITCH_UP)
5729 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5730 TEST_DrawLevelField(xx, yy);
5732 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5734 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5735 TEST_DrawLevelField(xx, yy);
5737 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5739 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5740 TEST_DrawLevelField(xx, yy);
5742 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5744 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5745 TEST_DrawLevelField(xx, yy);
5747 else if (element == EL_SWITCHGATE_OPEN ||
5748 element == EL_SWITCHGATE_OPENING)
5750 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5752 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5754 else if (element == EL_SWITCHGATE_CLOSED ||
5755 element == EL_SWITCHGATE_CLOSING)
5757 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5759 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5764 static int getInvisibleActiveFromInvisibleElement(int element)
5766 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5767 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5768 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5772 static int getInvisibleFromInvisibleActiveElement(int element)
5774 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5775 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5776 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5780 static void RedrawAllLightSwitchesAndInvisibleElements()
5784 SCAN_PLAYFIELD(x, y)
5786 int element = Feld[x][y];
5788 if (element == EL_LIGHT_SWITCH &&
5789 game.light_time_left > 0)
5791 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5792 TEST_DrawLevelField(x, y);
5794 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5795 game.light_time_left == 0)
5797 Feld[x][y] = EL_LIGHT_SWITCH;
5798 TEST_DrawLevelField(x, y);
5800 else if (element == EL_EMC_DRIPPER &&
5801 game.light_time_left > 0)
5803 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5804 TEST_DrawLevelField(x, y);
5806 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5807 game.light_time_left == 0)
5809 Feld[x][y] = EL_EMC_DRIPPER;
5810 TEST_DrawLevelField(x, y);
5812 else if (element == EL_INVISIBLE_STEELWALL ||
5813 element == EL_INVISIBLE_WALL ||
5814 element == EL_INVISIBLE_SAND)
5816 if (game.light_time_left > 0)
5817 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5819 TEST_DrawLevelField(x, y);
5821 /* uncrumble neighbour fields, if needed */
5822 if (element == EL_INVISIBLE_SAND)
5823 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5825 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5826 element == EL_INVISIBLE_WALL_ACTIVE ||
5827 element == EL_INVISIBLE_SAND_ACTIVE)
5829 if (game.light_time_left == 0)
5830 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5832 TEST_DrawLevelField(x, y);
5834 /* re-crumble neighbour fields, if needed */
5835 if (element == EL_INVISIBLE_SAND)
5836 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5841 static void RedrawAllInvisibleElementsForLenses()
5845 SCAN_PLAYFIELD(x, y)
5847 int element = Feld[x][y];
5849 if (element == EL_EMC_DRIPPER &&
5850 game.lenses_time_left > 0)
5852 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5853 TEST_DrawLevelField(x, y);
5855 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5856 game.lenses_time_left == 0)
5858 Feld[x][y] = EL_EMC_DRIPPER;
5859 TEST_DrawLevelField(x, y);
5861 else if (element == EL_INVISIBLE_STEELWALL ||
5862 element == EL_INVISIBLE_WALL ||
5863 element == EL_INVISIBLE_SAND)
5865 if (game.lenses_time_left > 0)
5866 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5868 TEST_DrawLevelField(x, y);
5870 /* uncrumble neighbour fields, if needed */
5871 if (element == EL_INVISIBLE_SAND)
5872 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5874 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5875 element == EL_INVISIBLE_WALL_ACTIVE ||
5876 element == EL_INVISIBLE_SAND_ACTIVE)
5878 if (game.lenses_time_left == 0)
5879 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5881 TEST_DrawLevelField(x, y);
5883 /* re-crumble neighbour fields, if needed */
5884 if (element == EL_INVISIBLE_SAND)
5885 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5890 static void RedrawAllInvisibleElementsForMagnifier()
5894 SCAN_PLAYFIELD(x, y)
5896 int element = Feld[x][y];
5898 if (element == EL_EMC_FAKE_GRASS &&
5899 game.magnify_time_left > 0)
5901 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5902 TEST_DrawLevelField(x, y);
5904 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5905 game.magnify_time_left == 0)
5907 Feld[x][y] = EL_EMC_FAKE_GRASS;
5908 TEST_DrawLevelField(x, y);
5910 else if (IS_GATE_GRAY(element) &&
5911 game.magnify_time_left > 0)
5913 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5914 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5915 IS_EM_GATE_GRAY(element) ?
5916 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5917 IS_EMC_GATE_GRAY(element) ?
5918 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5919 IS_DC_GATE_GRAY(element) ?
5920 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5922 TEST_DrawLevelField(x, y);
5924 else if (IS_GATE_GRAY_ACTIVE(element) &&
5925 game.magnify_time_left == 0)
5927 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5928 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5929 IS_EM_GATE_GRAY_ACTIVE(element) ?
5930 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5931 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5932 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5933 IS_DC_GATE_GRAY_ACTIVE(element) ?
5934 EL_DC_GATE_WHITE_GRAY :
5936 TEST_DrawLevelField(x, y);
5941 static void ToggleLightSwitch(int x, int y)
5943 int element = Feld[x][y];
5945 game.light_time_left =
5946 (element == EL_LIGHT_SWITCH ?
5947 level.time_light * FRAMES_PER_SECOND : 0);
5949 RedrawAllLightSwitchesAndInvisibleElements();
5952 static void ActivateTimegateSwitch(int x, int y)
5956 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5958 SCAN_PLAYFIELD(xx, yy)
5960 int element = Feld[xx][yy];
5962 if (element == EL_TIMEGATE_CLOSED ||
5963 element == EL_TIMEGATE_CLOSING)
5965 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5966 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5970 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5972 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5973 TEST_DrawLevelField(xx, yy);
5979 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5980 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5983 void Impact(int x, int y)
5985 boolean last_line = (y == lev_fieldy - 1);
5986 boolean object_hit = FALSE;
5987 boolean impact = (last_line || object_hit);
5988 int element = Feld[x][y];
5989 int smashed = EL_STEELWALL;
5991 if (!last_line) /* check if element below was hit */
5993 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5996 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5997 MovDir[x][y + 1] != MV_DOWN ||
5998 MovPos[x][y + 1] <= TILEY / 2));
6000 /* do not smash moving elements that left the smashed field in time */
6001 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6002 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6005 #if USE_QUICKSAND_IMPACT_BUGFIX
6006 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6008 RemoveMovingField(x, y + 1);
6009 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6010 Feld[x][y + 2] = EL_ROCK;
6011 TEST_DrawLevelField(x, y + 2);
6016 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6018 RemoveMovingField(x, y + 1);
6019 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6020 Feld[x][y + 2] = EL_ROCK;
6021 TEST_DrawLevelField(x, y + 2);
6028 smashed = MovingOrBlocked2Element(x, y + 1);
6030 impact = (last_line || object_hit);
6033 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6035 SplashAcid(x, y + 1);
6039 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6040 /* only reset graphic animation if graphic really changes after impact */
6042 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6044 ResetGfxAnimation(x, y);
6045 TEST_DrawLevelField(x, y);
6048 if (impact && CAN_EXPLODE_IMPACT(element))
6053 else if (impact && element == EL_PEARL &&
6054 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6056 ResetGfxAnimation(x, y);
6058 Feld[x][y] = EL_PEARL_BREAKING;
6059 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6062 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6064 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6069 if (impact && element == EL_AMOEBA_DROP)
6071 if (object_hit && IS_PLAYER(x, y + 1))
6072 KillPlayerUnlessEnemyProtected(x, y + 1);
6073 else if (object_hit && smashed == EL_PENGUIN)
6077 Feld[x][y] = EL_AMOEBA_GROWING;
6078 Store[x][y] = EL_AMOEBA_WET;
6080 ResetRandomAnimationValue(x, y);
6085 if (object_hit) /* check which object was hit */
6087 if ((CAN_PASS_MAGIC_WALL(element) &&
6088 (smashed == EL_MAGIC_WALL ||
6089 smashed == EL_BD_MAGIC_WALL)) ||
6090 (CAN_PASS_DC_MAGIC_WALL(element) &&
6091 smashed == EL_DC_MAGIC_WALL))
6094 int activated_magic_wall =
6095 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6096 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6097 EL_DC_MAGIC_WALL_ACTIVE);
6099 /* activate magic wall / mill */
6100 SCAN_PLAYFIELD(xx, yy)
6102 if (Feld[xx][yy] == smashed)
6103 Feld[xx][yy] = activated_magic_wall;
6106 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6107 game.magic_wall_active = TRUE;
6109 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6110 SND_MAGIC_WALL_ACTIVATING :
6111 smashed == EL_BD_MAGIC_WALL ?
6112 SND_BD_MAGIC_WALL_ACTIVATING :
6113 SND_DC_MAGIC_WALL_ACTIVATING));
6116 if (IS_PLAYER(x, y + 1))
6118 if (CAN_SMASH_PLAYER(element))
6120 KillPlayerUnlessEnemyProtected(x, y + 1);
6124 else if (smashed == EL_PENGUIN)
6126 if (CAN_SMASH_PLAYER(element))
6132 else if (element == EL_BD_DIAMOND)
6134 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6140 else if (((element == EL_SP_INFOTRON ||
6141 element == EL_SP_ZONK) &&
6142 (smashed == EL_SP_SNIKSNAK ||
6143 smashed == EL_SP_ELECTRON ||
6144 smashed == EL_SP_DISK_ORANGE)) ||
6145 (element == EL_SP_INFOTRON &&
6146 smashed == EL_SP_DISK_YELLOW))
6151 else if (CAN_SMASH_EVERYTHING(element))
6153 if (IS_CLASSIC_ENEMY(smashed) ||
6154 CAN_EXPLODE_SMASHED(smashed))
6159 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6161 if (smashed == EL_LAMP ||
6162 smashed == EL_LAMP_ACTIVE)
6167 else if (smashed == EL_NUT)
6169 Feld[x][y + 1] = EL_NUT_BREAKING;
6170 PlayLevelSound(x, y, SND_NUT_BREAKING);
6171 RaiseScoreElement(EL_NUT);
6174 else if (smashed == EL_PEARL)
6176 ResetGfxAnimation(x, y);
6178 Feld[x][y + 1] = EL_PEARL_BREAKING;
6179 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6182 else if (smashed == EL_DIAMOND)
6184 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6185 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6188 else if (IS_BELT_SWITCH(smashed))
6190 ToggleBeltSwitch(x, y + 1);
6192 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6193 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6194 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6195 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6197 ToggleSwitchgateSwitch(x, y + 1);
6199 else if (smashed == EL_LIGHT_SWITCH ||
6200 smashed == EL_LIGHT_SWITCH_ACTIVE)
6202 ToggleLightSwitch(x, y + 1);
6206 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6208 CheckElementChangeBySide(x, y + 1, smashed, element,
6209 CE_SWITCHED, CH_SIDE_TOP);
6210 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6216 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6221 /* play sound of magic wall / mill */
6223 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6224 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6225 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6227 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6228 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6229 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6230 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6231 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6232 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6237 /* play sound of object that hits the ground */
6238 if (last_line || object_hit)
6239 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6242 inline static void TurnRoundExt(int x, int y)
6254 { 0, 0 }, { 0, 0 }, { 0, 0 },
6259 int left, right, back;
6263 { MV_DOWN, MV_UP, MV_RIGHT },
6264 { MV_UP, MV_DOWN, MV_LEFT },
6266 { MV_LEFT, MV_RIGHT, MV_DOWN },
6270 { MV_RIGHT, MV_LEFT, MV_UP }
6273 int element = Feld[x][y];
6274 int move_pattern = element_info[element].move_pattern;
6276 int old_move_dir = MovDir[x][y];
6277 int left_dir = turn[old_move_dir].left;
6278 int right_dir = turn[old_move_dir].right;
6279 int back_dir = turn[old_move_dir].back;
6281 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6282 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6283 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6284 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6286 int left_x = x + left_dx, left_y = y + left_dy;
6287 int right_x = x + right_dx, right_y = y + right_dy;
6288 int move_x = x + move_dx, move_y = y + move_dy;
6292 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6294 TestIfBadThingTouchesOtherBadThing(x, y);
6296 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6297 MovDir[x][y] = right_dir;
6298 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6299 MovDir[x][y] = left_dir;
6301 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6303 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6306 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6308 TestIfBadThingTouchesOtherBadThing(x, y);
6310 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6311 MovDir[x][y] = left_dir;
6312 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6313 MovDir[x][y] = right_dir;
6315 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6317 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6320 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6322 TestIfBadThingTouchesOtherBadThing(x, y);
6324 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6325 MovDir[x][y] = left_dir;
6326 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6327 MovDir[x][y] = right_dir;
6329 if (MovDir[x][y] != old_move_dir)
6332 else if (element == EL_YAMYAM)
6334 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6335 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6337 if (can_turn_left && can_turn_right)
6338 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6339 else if (can_turn_left)
6340 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6341 else if (can_turn_right)
6342 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6344 MovDir[x][y] = back_dir;
6346 MovDelay[x][y] = 16 + 16 * RND(3);
6348 else if (element == EL_DARK_YAMYAM)
6350 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6352 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6355 if (can_turn_left && can_turn_right)
6356 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6357 else if (can_turn_left)
6358 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6359 else if (can_turn_right)
6360 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6362 MovDir[x][y] = back_dir;
6364 MovDelay[x][y] = 16 + 16 * RND(3);
6366 else if (element == EL_PACMAN)
6368 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6369 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6371 if (can_turn_left && can_turn_right)
6372 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6373 else if (can_turn_left)
6374 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6375 else if (can_turn_right)
6376 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6378 MovDir[x][y] = back_dir;
6380 MovDelay[x][y] = 6 + RND(40);
6382 else if (element == EL_PIG)
6384 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6385 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6386 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6387 boolean should_turn_left, should_turn_right, should_move_on;
6389 int rnd = RND(rnd_value);
6391 should_turn_left = (can_turn_left &&
6393 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6394 y + back_dy + left_dy)));
6395 should_turn_right = (can_turn_right &&
6397 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6398 y + back_dy + right_dy)));
6399 should_move_on = (can_move_on &&
6402 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6403 y + move_dy + left_dy) ||
6404 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6405 y + move_dy + right_dy)));
6407 if (should_turn_left || should_turn_right || should_move_on)
6409 if (should_turn_left && should_turn_right && should_move_on)
6410 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6411 rnd < 2 * rnd_value / 3 ? right_dir :
6413 else if (should_turn_left && should_turn_right)
6414 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6415 else if (should_turn_left && should_move_on)
6416 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6417 else if (should_turn_right && should_move_on)
6418 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6419 else if (should_turn_left)
6420 MovDir[x][y] = left_dir;
6421 else if (should_turn_right)
6422 MovDir[x][y] = right_dir;
6423 else if (should_move_on)
6424 MovDir[x][y] = old_move_dir;
6426 else if (can_move_on && rnd > rnd_value / 8)
6427 MovDir[x][y] = old_move_dir;
6428 else if (can_turn_left && can_turn_right)
6429 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6430 else if (can_turn_left && rnd > rnd_value / 8)
6431 MovDir[x][y] = left_dir;
6432 else if (can_turn_right && rnd > rnd_value/8)
6433 MovDir[x][y] = right_dir;
6435 MovDir[x][y] = back_dir;
6437 xx = x + move_xy[MovDir[x][y]].dx;
6438 yy = y + move_xy[MovDir[x][y]].dy;
6440 if (!IN_LEV_FIELD(xx, yy) ||
6441 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6442 MovDir[x][y] = old_move_dir;
6446 else if (element == EL_DRAGON)
6448 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6449 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6450 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6452 int rnd = RND(rnd_value);
6454 if (can_move_on && rnd > rnd_value / 8)
6455 MovDir[x][y] = old_move_dir;
6456 else if (can_turn_left && can_turn_right)
6457 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6458 else if (can_turn_left && rnd > rnd_value / 8)
6459 MovDir[x][y] = left_dir;
6460 else if (can_turn_right && rnd > rnd_value / 8)
6461 MovDir[x][y] = right_dir;
6463 MovDir[x][y] = back_dir;
6465 xx = x + move_xy[MovDir[x][y]].dx;
6466 yy = y + move_xy[MovDir[x][y]].dy;
6468 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6469 MovDir[x][y] = old_move_dir;
6473 else if (element == EL_MOLE)
6475 boolean can_move_on =
6476 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6477 IS_AMOEBOID(Feld[move_x][move_y]) ||
6478 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6481 boolean can_turn_left =
6482 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6483 IS_AMOEBOID(Feld[left_x][left_y])));
6485 boolean can_turn_right =
6486 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6487 IS_AMOEBOID(Feld[right_x][right_y])));
6489 if (can_turn_left && can_turn_right)
6490 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6491 else if (can_turn_left)
6492 MovDir[x][y] = left_dir;
6494 MovDir[x][y] = right_dir;
6497 if (MovDir[x][y] != old_move_dir)
6500 else if (element == EL_BALLOON)
6502 MovDir[x][y] = game.wind_direction;
6505 else if (element == EL_SPRING)
6507 if (MovDir[x][y] & MV_HORIZONTAL)
6509 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6510 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6512 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6513 ResetGfxAnimation(move_x, move_y);
6514 TEST_DrawLevelField(move_x, move_y);
6516 MovDir[x][y] = back_dir;
6518 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6519 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6520 MovDir[x][y] = MV_NONE;
6525 else if (element == EL_ROBOT ||
6526 element == EL_SATELLITE ||
6527 element == EL_PENGUIN ||
6528 element == EL_EMC_ANDROID)
6530 int attr_x = -1, attr_y = -1;
6541 for (i = 0; i < MAX_PLAYERS; i++)
6543 struct PlayerInfo *player = &stored_player[i];
6544 int jx = player->jx, jy = player->jy;
6546 if (!player->active)
6550 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6558 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6559 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6560 game.engine_version < VERSION_IDENT(3,1,0,0)))
6566 if (element == EL_PENGUIN)
6569 static int xy[4][2] =
6577 for (i = 0; i < NUM_DIRECTIONS; i++)
6579 int ex = x + xy[i][0];
6580 int ey = y + xy[i][1];
6582 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6583 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6584 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6585 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6594 MovDir[x][y] = MV_NONE;
6596 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6597 else if (attr_x > x)
6598 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6600 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6601 else if (attr_y > y)
6602 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6604 if (element == EL_ROBOT)
6608 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6609 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6610 Moving2Blocked(x, y, &newx, &newy);
6612 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6613 MovDelay[x][y] = 8 + 8 * !RND(3);
6615 MovDelay[x][y] = 16;
6617 else if (element == EL_PENGUIN)
6623 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6625 boolean first_horiz = RND(2);
6626 int new_move_dir = MovDir[x][y];
6629 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6630 Moving2Blocked(x, y, &newx, &newy);
6632 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6636 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6637 Moving2Blocked(x, y, &newx, &newy);
6639 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6642 MovDir[x][y] = old_move_dir;
6646 else if (element == EL_SATELLITE)
6652 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6654 boolean first_horiz = RND(2);
6655 int new_move_dir = MovDir[x][y];
6658 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6659 Moving2Blocked(x, y, &newx, &newy);
6661 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6665 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6666 Moving2Blocked(x, y, &newx, &newy);
6668 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6671 MovDir[x][y] = old_move_dir;
6675 else if (element == EL_EMC_ANDROID)
6677 static int check_pos[16] =
6679 -1, /* 0 => (invalid) */
6680 7, /* 1 => MV_LEFT */
6681 3, /* 2 => MV_RIGHT */
6682 -1, /* 3 => (invalid) */
6684 0, /* 5 => MV_LEFT | MV_UP */
6685 2, /* 6 => MV_RIGHT | MV_UP */
6686 -1, /* 7 => (invalid) */
6687 5, /* 8 => MV_DOWN */
6688 6, /* 9 => MV_LEFT | MV_DOWN */
6689 4, /* 10 => MV_RIGHT | MV_DOWN */
6690 -1, /* 11 => (invalid) */
6691 -1, /* 12 => (invalid) */
6692 -1, /* 13 => (invalid) */
6693 -1, /* 14 => (invalid) */
6694 -1, /* 15 => (invalid) */
6702 { -1, -1, MV_LEFT | MV_UP },
6704 { +1, -1, MV_RIGHT | MV_UP },
6705 { +1, 0, MV_RIGHT },
6706 { +1, +1, MV_RIGHT | MV_DOWN },
6708 { -1, +1, MV_LEFT | MV_DOWN },
6711 int start_pos, check_order;
6712 boolean can_clone = FALSE;
6715 /* check if there is any free field around current position */
6716 for (i = 0; i < 8; i++)
6718 int newx = x + check_xy[i].dx;
6719 int newy = y + check_xy[i].dy;
6721 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6729 if (can_clone) /* randomly find an element to clone */
6733 start_pos = check_pos[RND(8)];
6734 check_order = (RND(2) ? -1 : +1);
6736 for (i = 0; i < 8; i++)
6738 int pos_raw = start_pos + i * check_order;
6739 int pos = (pos_raw + 8) % 8;
6740 int newx = x + check_xy[pos].dx;
6741 int newy = y + check_xy[pos].dy;
6743 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6745 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6746 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6748 Store[x][y] = Feld[newx][newy];
6757 if (can_clone) /* randomly find a direction to move */
6761 start_pos = check_pos[RND(8)];
6762 check_order = (RND(2) ? -1 : +1);
6764 for (i = 0; i < 8; i++)
6766 int pos_raw = start_pos + i * check_order;
6767 int pos = (pos_raw + 8) % 8;
6768 int newx = x + check_xy[pos].dx;
6769 int newy = y + check_xy[pos].dy;
6770 int new_move_dir = check_xy[pos].dir;
6772 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6774 MovDir[x][y] = new_move_dir;
6775 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6784 if (can_clone) /* cloning and moving successful */
6787 /* cannot clone -- try to move towards player */
6789 start_pos = check_pos[MovDir[x][y] & 0x0f];
6790 check_order = (RND(2) ? -1 : +1);
6792 for (i = 0; i < 3; i++)
6794 /* first check start_pos, then previous/next or (next/previous) pos */
6795 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6796 int pos = (pos_raw + 8) % 8;
6797 int newx = x + check_xy[pos].dx;
6798 int newy = y + check_xy[pos].dy;
6799 int new_move_dir = check_xy[pos].dir;
6801 if (IS_PLAYER(newx, newy))
6804 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6806 MovDir[x][y] = new_move_dir;
6807 MovDelay[x][y] = level.android_move_time * 8 + 1;
6814 else if (move_pattern == MV_TURNING_LEFT ||
6815 move_pattern == MV_TURNING_RIGHT ||
6816 move_pattern == MV_TURNING_LEFT_RIGHT ||
6817 move_pattern == MV_TURNING_RIGHT_LEFT ||
6818 move_pattern == MV_TURNING_RANDOM ||
6819 move_pattern == MV_ALL_DIRECTIONS)
6821 boolean can_turn_left =
6822 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6823 boolean can_turn_right =
6824 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6826 if (element_info[element].move_stepsize == 0) /* "not moving" */
6829 if (move_pattern == MV_TURNING_LEFT)
6830 MovDir[x][y] = left_dir;
6831 else if (move_pattern == MV_TURNING_RIGHT)
6832 MovDir[x][y] = right_dir;
6833 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6834 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6835 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6836 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6837 else if (move_pattern == MV_TURNING_RANDOM)
6838 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6839 can_turn_right && !can_turn_left ? right_dir :
6840 RND(2) ? left_dir : right_dir);
6841 else if (can_turn_left && can_turn_right)
6842 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6843 else if (can_turn_left)
6844 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6845 else if (can_turn_right)
6846 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6848 MovDir[x][y] = back_dir;
6850 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6852 else if (move_pattern == MV_HORIZONTAL ||
6853 move_pattern == MV_VERTICAL)
6855 if (move_pattern & old_move_dir)
6856 MovDir[x][y] = back_dir;
6857 else if (move_pattern == MV_HORIZONTAL)
6858 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6859 else if (move_pattern == MV_VERTICAL)
6860 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6862 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6864 else if (move_pattern & MV_ANY_DIRECTION)
6866 MovDir[x][y] = move_pattern;
6867 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6869 else if (move_pattern & MV_WIND_DIRECTION)
6871 MovDir[x][y] = game.wind_direction;
6872 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6874 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6876 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6877 MovDir[x][y] = left_dir;
6878 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6879 MovDir[x][y] = right_dir;
6881 if (MovDir[x][y] != old_move_dir)
6882 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6884 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6886 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6887 MovDir[x][y] = right_dir;
6888 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6889 MovDir[x][y] = left_dir;
6891 if (MovDir[x][y] != old_move_dir)
6892 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6894 else if (move_pattern == MV_TOWARDS_PLAYER ||
6895 move_pattern == MV_AWAY_FROM_PLAYER)
6897 int attr_x = -1, attr_y = -1;
6899 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6910 for (i = 0; i < MAX_PLAYERS; i++)
6912 struct PlayerInfo *player = &stored_player[i];
6913 int jx = player->jx, jy = player->jy;
6915 if (!player->active)
6919 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6927 MovDir[x][y] = MV_NONE;
6929 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6930 else if (attr_x > x)
6931 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6933 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6934 else if (attr_y > y)
6935 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6937 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6939 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6941 boolean first_horiz = RND(2);
6942 int new_move_dir = MovDir[x][y];
6944 if (element_info[element].move_stepsize == 0) /* "not moving" */
6946 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6947 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6953 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6954 Moving2Blocked(x, y, &newx, &newy);
6956 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6960 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6961 Moving2Blocked(x, y, &newx, &newy);
6963 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6966 MovDir[x][y] = old_move_dir;
6969 else if (move_pattern == MV_WHEN_PUSHED ||
6970 move_pattern == MV_WHEN_DROPPED)
6972 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6973 MovDir[x][y] = MV_NONE;
6977 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6979 static int test_xy[7][2] =
6989 static int test_dir[7] =
6999 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7000 int move_preference = -1000000; /* start with very low preference */
7001 int new_move_dir = MV_NONE;
7002 int start_test = RND(4);
7005 for (i = 0; i < NUM_DIRECTIONS; i++)
7007 int move_dir = test_dir[start_test + i];
7008 int move_dir_preference;
7010 xx = x + test_xy[start_test + i][0];
7011 yy = y + test_xy[start_test + i][1];
7013 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7014 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7016 new_move_dir = move_dir;
7021 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7024 move_dir_preference = -1 * RunnerVisit[xx][yy];
7025 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7026 move_dir_preference = PlayerVisit[xx][yy];
7028 if (move_dir_preference > move_preference)
7030 /* prefer field that has not been visited for the longest time */
7031 move_preference = move_dir_preference;
7032 new_move_dir = move_dir;
7034 else if (move_dir_preference == move_preference &&
7035 move_dir == old_move_dir)
7037 /* prefer last direction when all directions are preferred equally */
7038 move_preference = move_dir_preference;
7039 new_move_dir = move_dir;
7043 MovDir[x][y] = new_move_dir;
7044 if (old_move_dir != new_move_dir)
7045 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7049 static void TurnRound(int x, int y)
7051 int direction = MovDir[x][y];
7055 GfxDir[x][y] = MovDir[x][y];
7057 if (direction != MovDir[x][y])
7061 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7063 ResetGfxFrame(x, y);
7066 static boolean JustBeingPushed(int x, int y)
7070 for (i = 0; i < MAX_PLAYERS; i++)
7072 struct PlayerInfo *player = &stored_player[i];
7074 if (player->active && player->is_pushing && player->MovPos)
7076 int next_jx = player->jx + (player->jx - player->last_jx);
7077 int next_jy = player->jy + (player->jy - player->last_jy);
7079 if (x == next_jx && y == next_jy)
7087 void StartMoving(int x, int y)
7089 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7090 int element = Feld[x][y];
7095 if (MovDelay[x][y] == 0)
7096 GfxAction[x][y] = ACTION_DEFAULT;
7098 if (CAN_FALL(element) && y < lev_fieldy - 1)
7100 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7101 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7102 if (JustBeingPushed(x, y))
7105 if (element == EL_QUICKSAND_FULL)
7107 if (IS_FREE(x, y + 1))
7109 InitMovingField(x, y, MV_DOWN);
7110 started_moving = TRUE;
7112 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7113 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7114 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7115 Store[x][y] = EL_ROCK;
7117 Store[x][y] = EL_ROCK;
7120 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7122 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7124 if (!MovDelay[x][y])
7126 MovDelay[x][y] = TILEY + 1;
7128 ResetGfxAnimation(x, y);
7129 ResetGfxAnimation(x, y + 1);
7134 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7135 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7142 Feld[x][y] = EL_QUICKSAND_EMPTY;
7143 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7144 Store[x][y + 1] = Store[x][y];
7147 PlayLevelSoundAction(x, y, ACTION_FILLING);
7149 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7151 if (!MovDelay[x][y])
7153 MovDelay[x][y] = TILEY + 1;
7155 ResetGfxAnimation(x, y);
7156 ResetGfxAnimation(x, y + 1);
7161 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7162 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7169 Feld[x][y] = EL_QUICKSAND_EMPTY;
7170 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7171 Store[x][y + 1] = Store[x][y];
7174 PlayLevelSoundAction(x, y, ACTION_FILLING);
7177 else if (element == EL_QUICKSAND_FAST_FULL)
7179 if (IS_FREE(x, y + 1))
7181 InitMovingField(x, y, MV_DOWN);
7182 started_moving = TRUE;
7184 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7185 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7186 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7187 Store[x][y] = EL_ROCK;
7189 Store[x][y] = EL_ROCK;
7192 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7194 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7196 if (!MovDelay[x][y])
7198 MovDelay[x][y] = TILEY + 1;
7200 ResetGfxAnimation(x, y);
7201 ResetGfxAnimation(x, y + 1);
7206 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7207 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7214 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7215 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7216 Store[x][y + 1] = Store[x][y];
7219 PlayLevelSoundAction(x, y, ACTION_FILLING);
7221 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7223 if (!MovDelay[x][y])
7225 MovDelay[x][y] = TILEY + 1;
7227 ResetGfxAnimation(x, y);
7228 ResetGfxAnimation(x, y + 1);
7233 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7234 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7241 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7242 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7243 Store[x][y + 1] = Store[x][y];
7246 PlayLevelSoundAction(x, y, ACTION_FILLING);
7249 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7250 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7252 InitMovingField(x, y, MV_DOWN);
7253 started_moving = TRUE;
7255 Feld[x][y] = EL_QUICKSAND_FILLING;
7256 Store[x][y] = element;
7258 PlayLevelSoundAction(x, y, ACTION_FILLING);
7260 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7261 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7263 InitMovingField(x, y, MV_DOWN);
7264 started_moving = TRUE;
7266 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7267 Store[x][y] = element;
7269 PlayLevelSoundAction(x, y, ACTION_FILLING);
7271 else if (element == EL_MAGIC_WALL_FULL)
7273 if (IS_FREE(x, y + 1))
7275 InitMovingField(x, y, MV_DOWN);
7276 started_moving = TRUE;
7278 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7279 Store[x][y] = EL_CHANGED(Store[x][y]);
7281 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7283 if (!MovDelay[x][y])
7284 MovDelay[x][y] = TILEY / 4 + 1;
7293 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7294 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7295 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7299 else if (element == EL_BD_MAGIC_WALL_FULL)
7301 if (IS_FREE(x, y + 1))
7303 InitMovingField(x, y, MV_DOWN);
7304 started_moving = TRUE;
7306 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7307 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7309 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7311 if (!MovDelay[x][y])
7312 MovDelay[x][y] = TILEY / 4 + 1;
7321 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7322 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7323 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7327 else if (element == EL_DC_MAGIC_WALL_FULL)
7329 if (IS_FREE(x, y + 1))
7331 InitMovingField(x, y, MV_DOWN);
7332 started_moving = TRUE;
7334 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7335 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7337 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7339 if (!MovDelay[x][y])
7340 MovDelay[x][y] = TILEY / 4 + 1;
7349 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7350 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7351 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7355 else if ((CAN_PASS_MAGIC_WALL(element) &&
7356 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7357 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7358 (CAN_PASS_DC_MAGIC_WALL(element) &&
7359 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7362 InitMovingField(x, y, MV_DOWN);
7363 started_moving = TRUE;
7366 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7367 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7368 EL_DC_MAGIC_WALL_FILLING);
7369 Store[x][y] = element;
7371 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7373 SplashAcid(x, y + 1);
7375 InitMovingField(x, y, MV_DOWN);
7376 started_moving = TRUE;
7378 Store[x][y] = EL_ACID;
7381 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7382 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7383 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7384 CAN_FALL(element) && WasJustFalling[x][y] &&
7385 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7387 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7388 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7389 (Feld[x][y + 1] == EL_BLOCKED)))
7391 /* this is needed for a special case not covered by calling "Impact()"
7392 from "ContinueMoving()": if an element moves to a tile directly below
7393 another element which was just falling on that tile (which was empty
7394 in the previous frame), the falling element above would just stop
7395 instead of smashing the element below (in previous version, the above
7396 element was just checked for "moving" instead of "falling", resulting
7397 in incorrect smashes caused by horizontal movement of the above
7398 element; also, the case of the player being the element to smash was
7399 simply not covered here... :-/ ) */
7401 CheckCollision[x][y] = 0;
7402 CheckImpact[x][y] = 0;
7406 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7408 if (MovDir[x][y] == MV_NONE)
7410 InitMovingField(x, y, MV_DOWN);
7411 started_moving = TRUE;
7414 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7416 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7417 MovDir[x][y] = MV_DOWN;
7419 InitMovingField(x, y, MV_DOWN);
7420 started_moving = TRUE;
7422 else if (element == EL_AMOEBA_DROP)
7424 Feld[x][y] = EL_AMOEBA_GROWING;
7425 Store[x][y] = EL_AMOEBA_WET;
7427 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7428 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7429 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7430 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7432 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7433 (IS_FREE(x - 1, y + 1) ||
7434 Feld[x - 1][y + 1] == EL_ACID));
7435 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7436 (IS_FREE(x + 1, y + 1) ||
7437 Feld[x + 1][y + 1] == EL_ACID));
7438 boolean can_fall_any = (can_fall_left || can_fall_right);
7439 boolean can_fall_both = (can_fall_left && can_fall_right);
7440 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7442 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7444 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7445 can_fall_right = FALSE;
7446 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7447 can_fall_left = FALSE;
7448 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7449 can_fall_right = FALSE;
7450 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7451 can_fall_left = FALSE;
7453 can_fall_any = (can_fall_left || can_fall_right);
7454 can_fall_both = FALSE;
7459 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7460 can_fall_right = FALSE; /* slip down on left side */
7462 can_fall_left = !(can_fall_right = RND(2));
7464 can_fall_both = FALSE;
7469 /* if not determined otherwise, prefer left side for slipping down */
7470 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7471 started_moving = TRUE;
7474 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7476 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7477 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7478 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7479 int belt_dir = game.belt_dir[belt_nr];
7481 if ((belt_dir == MV_LEFT && left_is_free) ||
7482 (belt_dir == MV_RIGHT && right_is_free))
7484 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7486 InitMovingField(x, y, belt_dir);
7487 started_moving = TRUE;
7489 Pushed[x][y] = TRUE;
7490 Pushed[nextx][y] = TRUE;
7492 GfxAction[x][y] = ACTION_DEFAULT;
7496 MovDir[x][y] = 0; /* if element was moving, stop it */
7501 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7502 if (CAN_MOVE(element) && !started_moving)
7504 int move_pattern = element_info[element].move_pattern;
7507 Moving2Blocked(x, y, &newx, &newy);
7509 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7512 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7513 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7515 WasJustMoving[x][y] = 0;
7516 CheckCollision[x][y] = 0;
7518 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7520 if (Feld[x][y] != element) /* element has changed */
7524 if (!MovDelay[x][y]) /* start new movement phase */
7526 /* all objects that can change their move direction after each step
7527 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7529 if (element != EL_YAMYAM &&
7530 element != EL_DARK_YAMYAM &&
7531 element != EL_PACMAN &&
7532 !(move_pattern & MV_ANY_DIRECTION) &&
7533 move_pattern != MV_TURNING_LEFT &&
7534 move_pattern != MV_TURNING_RIGHT &&
7535 move_pattern != MV_TURNING_LEFT_RIGHT &&
7536 move_pattern != MV_TURNING_RIGHT_LEFT &&
7537 move_pattern != MV_TURNING_RANDOM)
7541 if (MovDelay[x][y] && (element == EL_BUG ||
7542 element == EL_SPACESHIP ||
7543 element == EL_SP_SNIKSNAK ||
7544 element == EL_SP_ELECTRON ||
7545 element == EL_MOLE))
7546 TEST_DrawLevelField(x, y);
7550 if (MovDelay[x][y]) /* wait some time before next movement */
7554 if (element == EL_ROBOT ||
7555 element == EL_YAMYAM ||
7556 element == EL_DARK_YAMYAM)
7558 DrawLevelElementAnimationIfNeeded(x, y, element);
7559 PlayLevelSoundAction(x, y, ACTION_WAITING);
7561 else if (element == EL_SP_ELECTRON)
7562 DrawLevelElementAnimationIfNeeded(x, y, element);
7563 else if (element == EL_DRAGON)
7566 int dir = MovDir[x][y];
7567 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7568 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7569 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7570 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7571 dir == MV_UP ? IMG_FLAMES_1_UP :
7572 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7573 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7575 GfxAction[x][y] = ACTION_ATTACKING;
7577 if (IS_PLAYER(x, y))
7578 DrawPlayerField(x, y);
7580 TEST_DrawLevelField(x, y);
7582 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7584 for (i = 1; i <= 3; i++)
7586 int xx = x + i * dx;
7587 int yy = y + i * dy;
7588 int sx = SCREENX(xx);
7589 int sy = SCREENY(yy);
7590 int flame_graphic = graphic + (i - 1);
7592 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7597 int flamed = MovingOrBlocked2Element(xx, yy);
7599 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7602 RemoveMovingField(xx, yy);
7604 ChangeDelay[xx][yy] = 0;
7606 Feld[xx][yy] = EL_FLAMES;
7608 if (IN_SCR_FIELD(sx, sy))
7610 TEST_DrawLevelFieldCrumbled(xx, yy);
7611 DrawGraphic(sx, sy, flame_graphic, frame);
7616 if (Feld[xx][yy] == EL_FLAMES)
7617 Feld[xx][yy] = EL_EMPTY;
7618 TEST_DrawLevelField(xx, yy);
7623 if (MovDelay[x][y]) /* element still has to wait some time */
7625 PlayLevelSoundAction(x, y, ACTION_WAITING);
7631 /* now make next step */
7633 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7635 if (DONT_COLLIDE_WITH(element) &&
7636 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7637 !PLAYER_ENEMY_PROTECTED(newx, newy))
7639 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7644 else if (CAN_MOVE_INTO_ACID(element) &&
7645 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7646 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7647 (MovDir[x][y] == MV_DOWN ||
7648 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7650 SplashAcid(newx, newy);
7651 Store[x][y] = EL_ACID;
7653 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7655 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7656 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7657 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7658 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7661 TEST_DrawLevelField(x, y);
7663 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7664 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7665 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7667 local_player->friends_still_needed--;
7668 if (!local_player->friends_still_needed &&
7669 !local_player->GameOver && AllPlayersGone)
7670 PlayerWins(local_player);
7674 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7676 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7677 TEST_DrawLevelField(newx, newy);
7679 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7681 else if (!IS_FREE(newx, newy))
7683 GfxAction[x][y] = ACTION_WAITING;
7685 if (IS_PLAYER(x, y))
7686 DrawPlayerField(x, y);
7688 TEST_DrawLevelField(x, y);
7693 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7695 if (IS_FOOD_PIG(Feld[newx][newy]))
7697 if (IS_MOVING(newx, newy))
7698 RemoveMovingField(newx, newy);
7701 Feld[newx][newy] = EL_EMPTY;
7702 TEST_DrawLevelField(newx, newy);
7705 PlayLevelSound(x, y, SND_PIG_DIGGING);
7707 else if (!IS_FREE(newx, newy))
7709 if (IS_PLAYER(x, y))
7710 DrawPlayerField(x, y);
7712 TEST_DrawLevelField(x, y);
7717 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7719 if (Store[x][y] != EL_EMPTY)
7721 boolean can_clone = FALSE;
7724 /* check if element to clone is still there */
7725 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7727 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7735 /* cannot clone or target field not free anymore -- do not clone */
7736 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7737 Store[x][y] = EL_EMPTY;
7740 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7742 if (IS_MV_DIAGONAL(MovDir[x][y]))
7744 int diagonal_move_dir = MovDir[x][y];
7745 int stored = Store[x][y];
7746 int change_delay = 8;
7749 /* android is moving diagonally */
7751 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7753 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7754 GfxElement[x][y] = EL_EMC_ANDROID;
7755 GfxAction[x][y] = ACTION_SHRINKING;
7756 GfxDir[x][y] = diagonal_move_dir;
7757 ChangeDelay[x][y] = change_delay;
7759 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7762 DrawLevelGraphicAnimation(x, y, graphic);
7763 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7765 if (Feld[newx][newy] == EL_ACID)
7767 SplashAcid(newx, newy);
7772 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7774 Store[newx][newy] = EL_EMC_ANDROID;
7775 GfxElement[newx][newy] = EL_EMC_ANDROID;
7776 GfxAction[newx][newy] = ACTION_GROWING;
7777 GfxDir[newx][newy] = diagonal_move_dir;
7778 ChangeDelay[newx][newy] = change_delay;
7780 graphic = el_act_dir2img(GfxElement[newx][newy],
7781 GfxAction[newx][newy], GfxDir[newx][newy]);
7783 DrawLevelGraphicAnimation(newx, newy, graphic);
7784 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7790 Feld[newx][newy] = EL_EMPTY;
7791 TEST_DrawLevelField(newx, newy);
7793 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7796 else if (!IS_FREE(newx, newy))
7801 else if (IS_CUSTOM_ELEMENT(element) &&
7802 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7804 if (!DigFieldByCE(newx, newy, element))
7807 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7809 RunnerVisit[x][y] = FrameCounter;
7810 PlayerVisit[x][y] /= 8; /* expire player visit path */
7813 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7815 if (!IS_FREE(newx, newy))
7817 if (IS_PLAYER(x, y))
7818 DrawPlayerField(x, y);
7820 TEST_DrawLevelField(x, y);
7826 boolean wanna_flame = !RND(10);
7827 int dx = newx - x, dy = newy - y;
7828 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7829 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7830 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7831 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7832 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7833 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7836 IS_CLASSIC_ENEMY(element1) ||
7837 IS_CLASSIC_ENEMY(element2)) &&
7838 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7839 element1 != EL_FLAMES && element2 != EL_FLAMES)
7841 ResetGfxAnimation(x, y);
7842 GfxAction[x][y] = ACTION_ATTACKING;
7844 if (IS_PLAYER(x, y))
7845 DrawPlayerField(x, y);
7847 TEST_DrawLevelField(x, y);
7849 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7851 MovDelay[x][y] = 50;
7853 Feld[newx][newy] = EL_FLAMES;
7854 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7855 Feld[newx1][newy1] = EL_FLAMES;
7856 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7857 Feld[newx2][newy2] = EL_FLAMES;
7863 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7864 Feld[newx][newy] == EL_DIAMOND)
7866 if (IS_MOVING(newx, newy))
7867 RemoveMovingField(newx, newy);
7870 Feld[newx][newy] = EL_EMPTY;
7871 TEST_DrawLevelField(newx, newy);
7874 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7876 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7877 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7879 if (AmoebaNr[newx][newy])
7881 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7882 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7883 Feld[newx][newy] == EL_BD_AMOEBA)
7884 AmoebaCnt[AmoebaNr[newx][newy]]--;
7887 if (IS_MOVING(newx, newy))
7889 RemoveMovingField(newx, newy);
7893 Feld[newx][newy] = EL_EMPTY;
7894 TEST_DrawLevelField(newx, newy);
7897 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7899 else if ((element == EL_PACMAN || element == EL_MOLE)
7900 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7902 if (AmoebaNr[newx][newy])
7904 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7905 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7906 Feld[newx][newy] == EL_BD_AMOEBA)
7907 AmoebaCnt[AmoebaNr[newx][newy]]--;
7910 if (element == EL_MOLE)
7912 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7913 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7915 ResetGfxAnimation(x, y);
7916 GfxAction[x][y] = ACTION_DIGGING;
7917 TEST_DrawLevelField(x, y);
7919 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7921 return; /* wait for shrinking amoeba */
7923 else /* element == EL_PACMAN */
7925 Feld[newx][newy] = EL_EMPTY;
7926 TEST_DrawLevelField(newx, newy);
7927 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7930 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7931 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7932 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7934 /* wait for shrinking amoeba to completely disappear */
7937 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7939 /* object was running against a wall */
7943 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7944 DrawLevelElementAnimation(x, y, element);
7946 if (DONT_TOUCH(element))
7947 TestIfBadThingTouchesPlayer(x, y);
7952 InitMovingField(x, y, MovDir[x][y]);
7954 PlayLevelSoundAction(x, y, ACTION_MOVING);
7958 ContinueMoving(x, y);
7961 void ContinueMoving(int x, int y)
7963 int element = Feld[x][y];
7964 struct ElementInfo *ei = &element_info[element];
7965 int direction = MovDir[x][y];
7966 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7967 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7968 int newx = x + dx, newy = y + dy;
7969 int stored = Store[x][y];
7970 int stored_new = Store[newx][newy];
7971 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7972 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7973 boolean last_line = (newy == lev_fieldy - 1);
7975 MovPos[x][y] += getElementMoveStepsize(x, y);
7977 if (pushed_by_player) /* special case: moving object pushed by player */
7978 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7980 if (ABS(MovPos[x][y]) < TILEX)
7982 TEST_DrawLevelField(x, y);
7984 return; /* element is still moving */
7987 /* element reached destination field */
7989 Feld[x][y] = EL_EMPTY;
7990 Feld[newx][newy] = element;
7991 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7993 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7995 element = Feld[newx][newy] = EL_ACID;
7997 else if (element == EL_MOLE)
7999 Feld[x][y] = EL_SAND;
8001 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8003 else if (element == EL_QUICKSAND_FILLING)
8005 element = Feld[newx][newy] = get_next_element(element);
8006 Store[newx][newy] = Store[x][y];
8008 else if (element == EL_QUICKSAND_EMPTYING)
8010 Feld[x][y] = get_next_element(element);
8011 element = Feld[newx][newy] = Store[x][y];
8013 else if (element == EL_QUICKSAND_FAST_FILLING)
8015 element = Feld[newx][newy] = get_next_element(element);
8016 Store[newx][newy] = Store[x][y];
8018 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8020 Feld[x][y] = get_next_element(element);
8021 element = Feld[newx][newy] = Store[x][y];
8023 else if (element == EL_MAGIC_WALL_FILLING)
8025 element = Feld[newx][newy] = get_next_element(element);
8026 if (!game.magic_wall_active)
8027 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8028 Store[newx][newy] = Store[x][y];
8030 else if (element == EL_MAGIC_WALL_EMPTYING)
8032 Feld[x][y] = get_next_element(element);
8033 if (!game.magic_wall_active)
8034 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8035 element = Feld[newx][newy] = Store[x][y];
8037 InitField(newx, newy, FALSE);
8039 else if (element == EL_BD_MAGIC_WALL_FILLING)
8041 element = Feld[newx][newy] = get_next_element(element);
8042 if (!game.magic_wall_active)
8043 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8044 Store[newx][newy] = Store[x][y];
8046 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8048 Feld[x][y] = get_next_element(element);
8049 if (!game.magic_wall_active)
8050 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8051 element = Feld[newx][newy] = Store[x][y];
8053 InitField(newx, newy, FALSE);
8055 else if (element == EL_DC_MAGIC_WALL_FILLING)
8057 element = Feld[newx][newy] = get_next_element(element);
8058 if (!game.magic_wall_active)
8059 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8060 Store[newx][newy] = Store[x][y];
8062 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8064 Feld[x][y] = get_next_element(element);
8065 if (!game.magic_wall_active)
8066 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8067 element = Feld[newx][newy] = Store[x][y];
8069 InitField(newx, newy, FALSE);
8071 else if (element == EL_AMOEBA_DROPPING)
8073 Feld[x][y] = get_next_element(element);
8074 element = Feld[newx][newy] = Store[x][y];
8076 else if (element == EL_SOKOBAN_OBJECT)
8079 Feld[x][y] = Back[x][y];
8081 if (Back[newx][newy])
8082 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8084 Back[x][y] = Back[newx][newy] = 0;
8087 Store[x][y] = EL_EMPTY;
8092 MovDelay[newx][newy] = 0;
8094 if (CAN_CHANGE_OR_HAS_ACTION(element))
8096 /* copy element change control values to new field */
8097 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8098 ChangePage[newx][newy] = ChangePage[x][y];
8099 ChangeCount[newx][newy] = ChangeCount[x][y];
8100 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8103 CustomValue[newx][newy] = CustomValue[x][y];
8105 ChangeDelay[x][y] = 0;
8106 ChangePage[x][y] = -1;
8107 ChangeCount[x][y] = 0;
8108 ChangeEvent[x][y] = -1;
8110 CustomValue[x][y] = 0;
8112 /* copy animation control values to new field */
8113 GfxFrame[newx][newy] = GfxFrame[x][y];
8114 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8115 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8116 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8118 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8120 /* some elements can leave other elements behind after moving */
8121 if (ei->move_leave_element != EL_EMPTY &&
8122 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8123 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8125 int move_leave_element = ei->move_leave_element;
8127 /* this makes it possible to leave the removed element again */
8128 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8129 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8131 Feld[x][y] = move_leave_element;
8133 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8134 MovDir[x][y] = direction;
8136 InitField(x, y, FALSE);
8138 if (GFX_CRUMBLED(Feld[x][y]))
8139 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8141 if (ELEM_IS_PLAYER(move_leave_element))
8142 RelocatePlayer(x, y, move_leave_element);
8145 /* do this after checking for left-behind element */
8146 ResetGfxAnimation(x, y); /* reset animation values for old field */
8148 if (!CAN_MOVE(element) ||
8149 (CAN_FALL(element) && direction == MV_DOWN &&
8150 (element == EL_SPRING ||
8151 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8152 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8153 GfxDir[x][y] = MovDir[newx][newy] = 0;
8155 TEST_DrawLevelField(x, y);
8156 TEST_DrawLevelField(newx, newy);
8158 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8160 /* prevent pushed element from moving on in pushed direction */
8161 if (pushed_by_player && CAN_MOVE(element) &&
8162 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8163 !(element_info[element].move_pattern & direction))
8164 TurnRound(newx, newy);
8166 /* prevent elements on conveyor belt from moving on in last direction */
8167 if (pushed_by_conveyor && CAN_FALL(element) &&
8168 direction & MV_HORIZONTAL)
8169 MovDir[newx][newy] = 0;
8171 if (!pushed_by_player)
8173 int nextx = newx + dx, nexty = newy + dy;
8174 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8176 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8178 if (CAN_FALL(element) && direction == MV_DOWN)
8179 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8181 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8182 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8184 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8185 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8188 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8190 TestIfBadThingTouchesPlayer(newx, newy);
8191 TestIfBadThingTouchesFriend(newx, newy);
8193 if (!IS_CUSTOM_ELEMENT(element))
8194 TestIfBadThingTouchesOtherBadThing(newx, newy);
8196 else if (element == EL_PENGUIN)
8197 TestIfFriendTouchesBadThing(newx, newy);
8199 if (DONT_GET_HIT_BY(element))
8201 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8204 /* give the player one last chance (one more frame) to move away */
8205 if (CAN_FALL(element) && direction == MV_DOWN &&
8206 (last_line || (!IS_FREE(x, newy + 1) &&
8207 (!IS_PLAYER(x, newy + 1) ||
8208 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8211 if (pushed_by_player && !game.use_change_when_pushing_bug)
8213 int push_side = MV_DIR_OPPOSITE(direction);
8214 struct PlayerInfo *player = PLAYERINFO(x, y);
8216 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8217 player->index_bit, push_side);
8218 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8219 player->index_bit, push_side);
8222 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8223 MovDelay[newx][newy] = 1;
8225 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8227 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8228 TestIfElementHitsCustomElement(newx, newy, direction);
8229 TestIfPlayerTouchesCustomElement(newx, newy);
8230 TestIfElementTouchesCustomElement(newx, newy);
8232 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8233 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8234 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8235 MV_DIR_OPPOSITE(direction));
8238 int AmoebeNachbarNr(int ax, int ay)
8241 int element = Feld[ax][ay];
8243 static int xy[4][2] =
8251 for (i = 0; i < NUM_DIRECTIONS; i++)
8253 int x = ax + xy[i][0];
8254 int y = ay + xy[i][1];
8256 if (!IN_LEV_FIELD(x, y))
8259 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8260 group_nr = AmoebaNr[x][y];
8266 void AmoebenVereinigen(int ax, int ay)
8268 int i, x, y, xx, yy;
8269 int new_group_nr = AmoebaNr[ax][ay];
8270 static int xy[4][2] =
8278 if (new_group_nr == 0)
8281 for (i = 0; i < NUM_DIRECTIONS; i++)
8286 if (!IN_LEV_FIELD(x, y))
8289 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8290 Feld[x][y] == EL_BD_AMOEBA ||
8291 Feld[x][y] == EL_AMOEBA_DEAD) &&
8292 AmoebaNr[x][y] != new_group_nr)
8294 int old_group_nr = AmoebaNr[x][y];
8296 if (old_group_nr == 0)
8299 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8300 AmoebaCnt[old_group_nr] = 0;
8301 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8302 AmoebaCnt2[old_group_nr] = 0;
8304 SCAN_PLAYFIELD(xx, yy)
8306 if (AmoebaNr[xx][yy] == old_group_nr)
8307 AmoebaNr[xx][yy] = new_group_nr;
8313 void AmoebeUmwandeln(int ax, int ay)
8317 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8319 int group_nr = AmoebaNr[ax][ay];
8324 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8325 printf("AmoebeUmwandeln(): This should never happen!\n");
8330 SCAN_PLAYFIELD(x, y)
8332 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8335 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8339 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8340 SND_AMOEBA_TURNING_TO_GEM :
8341 SND_AMOEBA_TURNING_TO_ROCK));
8346 static int xy[4][2] =
8354 for (i = 0; i < NUM_DIRECTIONS; i++)
8359 if (!IN_LEV_FIELD(x, y))
8362 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8364 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8365 SND_AMOEBA_TURNING_TO_GEM :
8366 SND_AMOEBA_TURNING_TO_ROCK));
8373 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8376 int group_nr = AmoebaNr[ax][ay];
8377 boolean done = FALSE;
8382 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8383 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8388 SCAN_PLAYFIELD(x, y)
8390 if (AmoebaNr[x][y] == group_nr &&
8391 (Feld[x][y] == EL_AMOEBA_DEAD ||
8392 Feld[x][y] == EL_BD_AMOEBA ||
8393 Feld[x][y] == EL_AMOEBA_GROWING))
8396 Feld[x][y] = new_element;
8397 InitField(x, y, FALSE);
8398 TEST_DrawLevelField(x, y);
8404 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8405 SND_BD_AMOEBA_TURNING_TO_ROCK :
8406 SND_BD_AMOEBA_TURNING_TO_GEM));
8409 void AmoebeWaechst(int x, int y)
8411 static unsigned int sound_delay = 0;
8412 static unsigned int sound_delay_value = 0;
8414 if (!MovDelay[x][y]) /* start new growing cycle */
8418 if (DelayReached(&sound_delay, sound_delay_value))
8420 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8421 sound_delay_value = 30;
8425 if (MovDelay[x][y]) /* wait some time before growing bigger */
8428 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8430 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8431 6 - MovDelay[x][y]);
8433 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8436 if (!MovDelay[x][y])
8438 Feld[x][y] = Store[x][y];
8440 TEST_DrawLevelField(x, y);
8445 void AmoebaDisappearing(int x, int y)
8447 static unsigned int sound_delay = 0;
8448 static unsigned int sound_delay_value = 0;
8450 if (!MovDelay[x][y]) /* start new shrinking cycle */
8454 if (DelayReached(&sound_delay, sound_delay_value))
8455 sound_delay_value = 30;
8458 if (MovDelay[x][y]) /* wait some time before shrinking */
8461 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8463 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8464 6 - MovDelay[x][y]);
8466 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8469 if (!MovDelay[x][y])
8471 Feld[x][y] = EL_EMPTY;
8472 TEST_DrawLevelField(x, y);
8474 /* don't let mole enter this field in this cycle;
8475 (give priority to objects falling to this field from above) */
8481 void AmoebeAbleger(int ax, int ay)
8484 int element = Feld[ax][ay];
8485 int graphic = el2img(element);
8486 int newax = ax, neway = ay;
8487 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8488 static int xy[4][2] =
8496 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8498 Feld[ax][ay] = EL_AMOEBA_DEAD;
8499 TEST_DrawLevelField(ax, ay);
8503 if (IS_ANIMATED(graphic))
8504 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8506 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8507 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8509 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8512 if (MovDelay[ax][ay])
8516 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8519 int x = ax + xy[start][0];
8520 int y = ay + xy[start][1];
8522 if (!IN_LEV_FIELD(x, y))
8525 if (IS_FREE(x, y) ||
8526 CAN_GROW_INTO(Feld[x][y]) ||
8527 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8528 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8534 if (newax == ax && neway == ay)
8537 else /* normal or "filled" (BD style) amoeba */
8540 boolean waiting_for_player = FALSE;
8542 for (i = 0; i < NUM_DIRECTIONS; i++)
8544 int j = (start + i) % 4;
8545 int x = ax + xy[j][0];
8546 int y = ay + xy[j][1];
8548 if (!IN_LEV_FIELD(x, y))
8551 if (IS_FREE(x, y) ||
8552 CAN_GROW_INTO(Feld[x][y]) ||
8553 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8554 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8560 else if (IS_PLAYER(x, y))
8561 waiting_for_player = TRUE;
8564 if (newax == ax && neway == ay) /* amoeba cannot grow */
8566 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8568 Feld[ax][ay] = EL_AMOEBA_DEAD;
8569 TEST_DrawLevelField(ax, ay);
8570 AmoebaCnt[AmoebaNr[ax][ay]]--;
8572 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8574 if (element == EL_AMOEBA_FULL)
8575 AmoebeUmwandeln(ax, ay);
8576 else if (element == EL_BD_AMOEBA)
8577 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8582 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8584 /* amoeba gets larger by growing in some direction */
8586 int new_group_nr = AmoebaNr[ax][ay];
8589 if (new_group_nr == 0)
8591 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8592 printf("AmoebeAbleger(): This should never happen!\n");
8597 AmoebaNr[newax][neway] = new_group_nr;
8598 AmoebaCnt[new_group_nr]++;
8599 AmoebaCnt2[new_group_nr]++;
8601 /* if amoeba touches other amoeba(s) after growing, unify them */
8602 AmoebenVereinigen(newax, neway);
8604 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8606 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8612 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8613 (neway == lev_fieldy - 1 && newax != ax))
8615 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8616 Store[newax][neway] = element;
8618 else if (neway == ay || element == EL_EMC_DRIPPER)
8620 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8622 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8626 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8627 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8628 Store[ax][ay] = EL_AMOEBA_DROP;
8629 ContinueMoving(ax, ay);
8633 TEST_DrawLevelField(newax, neway);
8636 void Life(int ax, int ay)
8640 int element = Feld[ax][ay];
8641 int graphic = el2img(element);
8642 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8644 boolean changed = FALSE;
8646 if (IS_ANIMATED(graphic))
8647 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8652 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8653 MovDelay[ax][ay] = life_time;
8655 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8658 if (MovDelay[ax][ay])
8662 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8664 int xx = ax+x1, yy = ay+y1;
8667 if (!IN_LEV_FIELD(xx, yy))
8670 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8672 int x = xx+x2, y = yy+y2;
8674 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8677 if (((Feld[x][y] == element ||
8678 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8680 (IS_FREE(x, y) && Stop[x][y]))
8684 if (xx == ax && yy == ay) /* field in the middle */
8686 if (nachbarn < life_parameter[0] ||
8687 nachbarn > life_parameter[1])
8689 Feld[xx][yy] = EL_EMPTY;
8691 TEST_DrawLevelField(xx, yy);
8692 Stop[xx][yy] = TRUE;
8696 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8697 { /* free border field */
8698 if (nachbarn >= life_parameter[2] &&
8699 nachbarn <= life_parameter[3])
8701 Feld[xx][yy] = element;
8702 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8704 TEST_DrawLevelField(xx, yy);
8705 Stop[xx][yy] = TRUE;
8712 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8713 SND_GAME_OF_LIFE_GROWING);
8716 static void InitRobotWheel(int x, int y)
8718 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8721 static void RunRobotWheel(int x, int y)
8723 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8726 static void StopRobotWheel(int x, int y)
8728 if (ZX == x && ZY == y)
8732 game.robot_wheel_active = FALSE;
8736 static void InitTimegateWheel(int x, int y)
8738 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8741 static void RunTimegateWheel(int x, int y)
8743 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8746 static void InitMagicBallDelay(int x, int y)
8748 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8751 static void ActivateMagicBall(int bx, int by)
8755 if (level.ball_random)
8757 int pos_border = RND(8); /* select one of the eight border elements */
8758 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8759 int xx = pos_content % 3;
8760 int yy = pos_content / 3;
8765 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8766 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8770 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8772 int xx = x - bx + 1;
8773 int yy = y - by + 1;
8775 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8776 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8780 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8783 void CheckExit(int x, int y)
8785 if (local_player->gems_still_needed > 0 ||
8786 local_player->sokobanfields_still_needed > 0 ||
8787 local_player->lights_still_needed > 0)
8789 int element = Feld[x][y];
8790 int graphic = el2img(element);
8792 if (IS_ANIMATED(graphic))
8793 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8798 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8801 Feld[x][y] = EL_EXIT_OPENING;
8803 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8806 void CheckExitEM(int x, int y)
8808 if (local_player->gems_still_needed > 0 ||
8809 local_player->sokobanfields_still_needed > 0 ||
8810 local_player->lights_still_needed > 0)
8812 int element = Feld[x][y];
8813 int graphic = el2img(element);
8815 if (IS_ANIMATED(graphic))
8816 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8821 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8824 Feld[x][y] = EL_EM_EXIT_OPENING;
8826 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8829 void CheckExitSteel(int x, int y)
8831 if (local_player->gems_still_needed > 0 ||
8832 local_player->sokobanfields_still_needed > 0 ||
8833 local_player->lights_still_needed > 0)
8835 int element = Feld[x][y];
8836 int graphic = el2img(element);
8838 if (IS_ANIMATED(graphic))
8839 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8844 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8847 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8849 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8852 void CheckExitSteelEM(int x, int y)
8854 if (local_player->gems_still_needed > 0 ||
8855 local_player->sokobanfields_still_needed > 0 ||
8856 local_player->lights_still_needed > 0)
8858 int element = Feld[x][y];
8859 int graphic = el2img(element);
8861 if (IS_ANIMATED(graphic))
8862 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8867 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8870 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8872 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8875 void CheckExitSP(int x, int y)
8877 if (local_player->gems_still_needed > 0)
8879 int element = Feld[x][y];
8880 int graphic = el2img(element);
8882 if (IS_ANIMATED(graphic))
8883 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8888 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8891 Feld[x][y] = EL_SP_EXIT_OPENING;
8893 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8896 static void CloseAllOpenTimegates()
8900 SCAN_PLAYFIELD(x, y)
8902 int element = Feld[x][y];
8904 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8906 Feld[x][y] = EL_TIMEGATE_CLOSING;
8908 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8913 void DrawTwinkleOnField(int x, int y)
8915 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8918 if (Feld[x][y] == EL_BD_DIAMOND)
8921 if (MovDelay[x][y] == 0) /* next animation frame */
8922 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8924 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8928 DrawLevelElementAnimation(x, y, Feld[x][y]);
8930 if (MovDelay[x][y] != 0)
8932 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8933 10 - MovDelay[x][y]);
8935 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8940 void MauerWaechst(int x, int y)
8944 if (!MovDelay[x][y]) /* next animation frame */
8945 MovDelay[x][y] = 3 * delay;
8947 if (MovDelay[x][y]) /* wait some time before next frame */
8951 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8953 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8954 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8956 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8959 if (!MovDelay[x][y])
8961 if (MovDir[x][y] == MV_LEFT)
8963 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8964 TEST_DrawLevelField(x - 1, y);
8966 else if (MovDir[x][y] == MV_RIGHT)
8968 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8969 TEST_DrawLevelField(x + 1, y);
8971 else if (MovDir[x][y] == MV_UP)
8973 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8974 TEST_DrawLevelField(x, y - 1);
8978 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8979 TEST_DrawLevelField(x, y + 1);
8982 Feld[x][y] = Store[x][y];
8984 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8985 TEST_DrawLevelField(x, y);
8990 void MauerAbleger(int ax, int ay)
8992 int element = Feld[ax][ay];
8993 int graphic = el2img(element);
8994 boolean oben_frei = FALSE, unten_frei = FALSE;
8995 boolean links_frei = FALSE, rechts_frei = FALSE;
8996 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8997 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8998 boolean new_wall = FALSE;
9000 if (IS_ANIMATED(graphic))
9001 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9003 if (!MovDelay[ax][ay]) /* start building new wall */
9004 MovDelay[ax][ay] = 6;
9006 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9009 if (MovDelay[ax][ay])
9013 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9015 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9017 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9019 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9022 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9023 element == EL_EXPANDABLE_WALL_ANY)
9027 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9028 Store[ax][ay-1] = element;
9029 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9030 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9031 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9032 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9037 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9038 Store[ax][ay+1] = element;
9039 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9040 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9041 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9042 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9047 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9048 element == EL_EXPANDABLE_WALL_ANY ||
9049 element == EL_EXPANDABLE_WALL ||
9050 element == EL_BD_EXPANDABLE_WALL)
9054 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9055 Store[ax-1][ay] = element;
9056 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9057 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9058 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9059 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9065 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9066 Store[ax+1][ay] = element;
9067 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9068 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9069 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9070 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9075 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9076 TEST_DrawLevelField(ax, ay);
9078 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9080 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9081 unten_massiv = TRUE;
9082 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9083 links_massiv = TRUE;
9084 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9085 rechts_massiv = TRUE;
9087 if (((oben_massiv && unten_massiv) ||
9088 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9089 element == EL_EXPANDABLE_WALL) &&
9090 ((links_massiv && rechts_massiv) ||
9091 element == EL_EXPANDABLE_WALL_VERTICAL))
9092 Feld[ax][ay] = EL_WALL;
9095 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9098 void MauerAblegerStahl(int ax, int ay)
9100 int element = Feld[ax][ay];
9101 int graphic = el2img(element);
9102 boolean oben_frei = FALSE, unten_frei = FALSE;
9103 boolean links_frei = FALSE, rechts_frei = FALSE;
9104 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9105 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9106 boolean new_wall = FALSE;
9108 if (IS_ANIMATED(graphic))
9109 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9111 if (!MovDelay[ax][ay]) /* start building new wall */
9112 MovDelay[ax][ay] = 6;
9114 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9117 if (MovDelay[ax][ay])
9121 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9123 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9125 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9127 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9130 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9131 element == EL_EXPANDABLE_STEELWALL_ANY)
9135 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9136 Store[ax][ay-1] = element;
9137 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9138 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9139 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9140 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9145 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9146 Store[ax][ay+1] = element;
9147 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9148 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9149 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9150 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9155 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9156 element == EL_EXPANDABLE_STEELWALL_ANY)
9160 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9161 Store[ax-1][ay] = element;
9162 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9163 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9164 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9165 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9171 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9172 Store[ax+1][ay] = element;
9173 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9174 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9175 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9176 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9181 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9183 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9184 unten_massiv = TRUE;
9185 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9186 links_massiv = TRUE;
9187 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9188 rechts_massiv = TRUE;
9190 if (((oben_massiv && unten_massiv) ||
9191 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9192 ((links_massiv && rechts_massiv) ||
9193 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9194 Feld[ax][ay] = EL_STEELWALL;
9197 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9200 void CheckForDragon(int x, int y)
9203 boolean dragon_found = FALSE;
9204 static int xy[4][2] =
9212 for (i = 0; i < NUM_DIRECTIONS; i++)
9214 for (j = 0; j < 4; j++)
9216 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9218 if (IN_LEV_FIELD(xx, yy) &&
9219 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9221 if (Feld[xx][yy] == EL_DRAGON)
9222 dragon_found = TRUE;
9231 for (i = 0; i < NUM_DIRECTIONS; i++)
9233 for (j = 0; j < 3; j++)
9235 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9237 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9239 Feld[xx][yy] = EL_EMPTY;
9240 TEST_DrawLevelField(xx, yy);
9249 static void InitBuggyBase(int x, int y)
9251 int element = Feld[x][y];
9252 int activating_delay = FRAMES_PER_SECOND / 4;
9255 (element == EL_SP_BUGGY_BASE ?
9256 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9257 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9259 element == EL_SP_BUGGY_BASE_ACTIVE ?
9260 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9263 static void WarnBuggyBase(int x, int y)
9266 static int xy[4][2] =
9274 for (i = 0; i < NUM_DIRECTIONS; i++)
9276 int xx = x + xy[i][0];
9277 int yy = y + xy[i][1];
9279 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9281 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9288 static void InitTrap(int x, int y)
9290 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9293 static void ActivateTrap(int x, int y)
9295 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9298 static void ChangeActiveTrap(int x, int y)
9300 int graphic = IMG_TRAP_ACTIVE;
9302 /* if new animation frame was drawn, correct crumbled sand border */
9303 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9304 TEST_DrawLevelFieldCrumbled(x, y);
9307 static int getSpecialActionElement(int element, int number, int base_element)
9309 return (element != EL_EMPTY ? element :
9310 number != -1 ? base_element + number - 1 :
9314 static int getModifiedActionNumber(int value_old, int operator, int operand,
9315 int value_min, int value_max)
9317 int value_new = (operator == CA_MODE_SET ? operand :
9318 operator == CA_MODE_ADD ? value_old + operand :
9319 operator == CA_MODE_SUBTRACT ? value_old - operand :
9320 operator == CA_MODE_MULTIPLY ? value_old * operand :
9321 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9322 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9325 return (value_new < value_min ? value_min :
9326 value_new > value_max ? value_max :
9330 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9332 struct ElementInfo *ei = &element_info[element];
9333 struct ElementChangeInfo *change = &ei->change_page[page];
9334 int target_element = change->target_element;
9335 int action_type = change->action_type;
9336 int action_mode = change->action_mode;
9337 int action_arg = change->action_arg;
9338 int action_element = change->action_element;
9341 if (!change->has_action)
9344 /* ---------- determine action paramater values -------------------------- */
9346 int level_time_value =
9347 (level.time > 0 ? TimeLeft :
9350 int action_arg_element_raw =
9351 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9352 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9353 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9354 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9355 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9356 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9357 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9359 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9361 int action_arg_direction =
9362 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9363 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9364 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9365 change->actual_trigger_side :
9366 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9367 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9370 int action_arg_number_min =
9371 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9374 int action_arg_number_max =
9375 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9376 action_type == CA_SET_LEVEL_GEMS ? 999 :
9377 action_type == CA_SET_LEVEL_TIME ? 9999 :
9378 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9379 action_type == CA_SET_CE_VALUE ? 9999 :
9380 action_type == CA_SET_CE_SCORE ? 9999 :
9383 int action_arg_number_reset =
9384 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9385 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9386 action_type == CA_SET_LEVEL_TIME ? level.time :
9387 action_type == CA_SET_LEVEL_SCORE ? 0 :
9388 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9389 action_type == CA_SET_CE_SCORE ? 0 :
9392 int action_arg_number =
9393 (action_arg <= CA_ARG_MAX ? action_arg :
9394 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9395 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9396 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9397 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9398 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9399 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9400 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9401 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9402 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9403 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9404 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9405 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9406 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9407 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9408 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9409 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9410 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9411 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9412 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9413 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9414 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9417 int action_arg_number_old =
9418 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9419 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9420 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9421 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9422 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9425 int action_arg_number_new =
9426 getModifiedActionNumber(action_arg_number_old,
9427 action_mode, action_arg_number,
9428 action_arg_number_min, action_arg_number_max);
9430 int trigger_player_bits =
9431 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9432 change->actual_trigger_player_bits : change->trigger_player);
9434 int action_arg_player_bits =
9435 (action_arg >= CA_ARG_PLAYER_1 &&
9436 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9437 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9438 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9441 /* ---------- execute action -------------------------------------------- */
9443 switch (action_type)
9450 /* ---------- level actions ------------------------------------------- */
9452 case CA_RESTART_LEVEL:
9454 game.restart_level = TRUE;
9459 case CA_SHOW_ENVELOPE:
9461 int element = getSpecialActionElement(action_arg_element,
9462 action_arg_number, EL_ENVELOPE_1);
9464 if (IS_ENVELOPE(element))
9465 local_player->show_envelope = element;
9470 case CA_SET_LEVEL_TIME:
9472 if (level.time > 0) /* only modify limited time value */
9474 TimeLeft = action_arg_number_new;
9476 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9478 DisplayGameControlValues();
9480 if (!TimeLeft && setup.time_limit)
9481 for (i = 0; i < MAX_PLAYERS; i++)
9482 KillPlayer(&stored_player[i]);
9488 case CA_SET_LEVEL_SCORE:
9490 local_player->score = action_arg_number_new;
9492 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9494 DisplayGameControlValues();
9499 case CA_SET_LEVEL_GEMS:
9501 local_player->gems_still_needed = action_arg_number_new;
9503 game.snapshot.collected_item = TRUE;
9505 game_panel_controls[GAME_PANEL_GEMS].value =
9506 local_player->gems_still_needed;
9508 DisplayGameControlValues();
9513 case CA_SET_LEVEL_WIND:
9515 game.wind_direction = action_arg_direction;
9520 case CA_SET_LEVEL_RANDOM_SEED:
9522 /* ensure that setting a new random seed while playing is predictable */
9523 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9528 /* ---------- player actions ------------------------------------------ */
9530 case CA_MOVE_PLAYER:
9532 /* automatically move to the next field in specified direction */
9533 for (i = 0; i < MAX_PLAYERS; i++)
9534 if (trigger_player_bits & (1 << i))
9535 stored_player[i].programmed_action = action_arg_direction;
9540 case CA_EXIT_PLAYER:
9542 for (i = 0; i < MAX_PLAYERS; i++)
9543 if (action_arg_player_bits & (1 << i))
9544 PlayerWins(&stored_player[i]);
9549 case CA_KILL_PLAYER:
9551 for (i = 0; i < MAX_PLAYERS; i++)
9552 if (action_arg_player_bits & (1 << i))
9553 KillPlayer(&stored_player[i]);
9558 case CA_SET_PLAYER_KEYS:
9560 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9561 int element = getSpecialActionElement(action_arg_element,
9562 action_arg_number, EL_KEY_1);
9564 if (IS_KEY(element))
9566 for (i = 0; i < MAX_PLAYERS; i++)
9568 if (trigger_player_bits & (1 << i))
9570 stored_player[i].key[KEY_NR(element)] = key_state;
9572 DrawGameDoorValues();
9580 case CA_SET_PLAYER_SPEED:
9582 for (i = 0; i < MAX_PLAYERS; i++)
9584 if (trigger_player_bits & (1 << i))
9586 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9588 if (action_arg == CA_ARG_SPEED_FASTER &&
9589 stored_player[i].cannot_move)
9591 action_arg_number = STEPSIZE_VERY_SLOW;
9593 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9594 action_arg == CA_ARG_SPEED_FASTER)
9596 action_arg_number = 2;
9597 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9600 else if (action_arg == CA_ARG_NUMBER_RESET)
9602 action_arg_number = level.initial_player_stepsize[i];
9606 getModifiedActionNumber(move_stepsize,
9609 action_arg_number_min,
9610 action_arg_number_max);
9612 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9619 case CA_SET_PLAYER_SHIELD:
9621 for (i = 0; i < MAX_PLAYERS; i++)
9623 if (trigger_player_bits & (1 << i))
9625 if (action_arg == CA_ARG_SHIELD_OFF)
9627 stored_player[i].shield_normal_time_left = 0;
9628 stored_player[i].shield_deadly_time_left = 0;
9630 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9632 stored_player[i].shield_normal_time_left = 999999;
9634 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9636 stored_player[i].shield_normal_time_left = 999999;
9637 stored_player[i].shield_deadly_time_left = 999999;
9645 case CA_SET_PLAYER_GRAVITY:
9647 for (i = 0; i < MAX_PLAYERS; i++)
9649 if (trigger_player_bits & (1 << i))
9651 stored_player[i].gravity =
9652 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9653 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9654 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9655 stored_player[i].gravity);
9662 case CA_SET_PLAYER_ARTWORK:
9664 for (i = 0; i < MAX_PLAYERS; i++)
9666 if (trigger_player_bits & (1 << i))
9668 int artwork_element = action_arg_element;
9670 if (action_arg == CA_ARG_ELEMENT_RESET)
9672 (level.use_artwork_element[i] ? level.artwork_element[i] :
9673 stored_player[i].element_nr);
9675 if (stored_player[i].artwork_element != artwork_element)
9676 stored_player[i].Frame = 0;
9678 stored_player[i].artwork_element = artwork_element;
9680 SetPlayerWaiting(&stored_player[i], FALSE);
9682 /* set number of special actions for bored and sleeping animation */
9683 stored_player[i].num_special_action_bored =
9684 get_num_special_action(artwork_element,
9685 ACTION_BORING_1, ACTION_BORING_LAST);
9686 stored_player[i].num_special_action_sleeping =
9687 get_num_special_action(artwork_element,
9688 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9695 case CA_SET_PLAYER_INVENTORY:
9697 for (i = 0; i < MAX_PLAYERS; i++)
9699 struct PlayerInfo *player = &stored_player[i];
9702 if (trigger_player_bits & (1 << i))
9704 int inventory_element = action_arg_element;
9706 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9707 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9708 action_arg == CA_ARG_ELEMENT_ACTION)
9710 int element = inventory_element;
9711 int collect_count = element_info[element].collect_count_initial;
9713 if (!IS_CUSTOM_ELEMENT(element))
9716 if (collect_count == 0)
9717 player->inventory_infinite_element = element;
9719 for (k = 0; k < collect_count; k++)
9720 if (player->inventory_size < MAX_INVENTORY_SIZE)
9721 player->inventory_element[player->inventory_size++] =
9724 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9725 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9726 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9728 if (player->inventory_infinite_element != EL_UNDEFINED &&
9729 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9730 action_arg_element_raw))
9731 player->inventory_infinite_element = EL_UNDEFINED;
9733 for (k = 0, j = 0; j < player->inventory_size; j++)
9735 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9736 action_arg_element_raw))
9737 player->inventory_element[k++] = player->inventory_element[j];
9740 player->inventory_size = k;
9742 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9744 if (player->inventory_size > 0)
9746 for (j = 0; j < player->inventory_size - 1; j++)
9747 player->inventory_element[j] = player->inventory_element[j + 1];
9749 player->inventory_size--;
9752 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9754 if (player->inventory_size > 0)
9755 player->inventory_size--;
9757 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9759 player->inventory_infinite_element = EL_UNDEFINED;
9760 player->inventory_size = 0;
9762 else if (action_arg == CA_ARG_INVENTORY_RESET)
9764 player->inventory_infinite_element = EL_UNDEFINED;
9765 player->inventory_size = 0;
9767 if (level.use_initial_inventory[i])
9769 for (j = 0; j < level.initial_inventory_size[i]; j++)
9771 int element = level.initial_inventory_content[i][j];
9772 int collect_count = element_info[element].collect_count_initial;
9774 if (!IS_CUSTOM_ELEMENT(element))
9777 if (collect_count == 0)
9778 player->inventory_infinite_element = element;
9780 for (k = 0; k < collect_count; k++)
9781 if (player->inventory_size < MAX_INVENTORY_SIZE)
9782 player->inventory_element[player->inventory_size++] =
9793 /* ---------- CE actions ---------------------------------------------- */
9795 case CA_SET_CE_VALUE:
9797 int last_ce_value = CustomValue[x][y];
9799 CustomValue[x][y] = action_arg_number_new;
9801 if (CustomValue[x][y] != last_ce_value)
9803 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9804 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9806 if (CustomValue[x][y] == 0)
9808 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9809 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9816 case CA_SET_CE_SCORE:
9818 int last_ce_score = ei->collect_score;
9820 ei->collect_score = action_arg_number_new;
9822 if (ei->collect_score != last_ce_score)
9824 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9825 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9827 if (ei->collect_score == 0)
9831 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9832 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9835 This is a very special case that seems to be a mixture between
9836 CheckElementChange() and CheckTriggeredElementChange(): while
9837 the first one only affects single elements that are triggered
9838 directly, the second one affects multiple elements in the playfield
9839 that are triggered indirectly by another element. This is a third
9840 case: Changing the CE score always affects multiple identical CEs,
9841 so every affected CE must be checked, not only the single CE for
9842 which the CE score was changed in the first place (as every instance
9843 of that CE shares the same CE score, and therefore also can change)!
9845 SCAN_PLAYFIELD(xx, yy)
9847 if (Feld[xx][yy] == element)
9848 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9849 CE_SCORE_GETS_ZERO);
9857 case CA_SET_CE_ARTWORK:
9859 int artwork_element = action_arg_element;
9860 boolean reset_frame = FALSE;
9863 if (action_arg == CA_ARG_ELEMENT_RESET)
9864 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9867 if (ei->gfx_element != artwork_element)
9870 ei->gfx_element = artwork_element;
9872 SCAN_PLAYFIELD(xx, yy)
9874 if (Feld[xx][yy] == element)
9878 ResetGfxAnimation(xx, yy);
9879 ResetRandomAnimationValue(xx, yy);
9882 TEST_DrawLevelField(xx, yy);
9889 /* ---------- engine actions ------------------------------------------ */
9891 case CA_SET_ENGINE_SCAN_MODE:
9893 InitPlayfieldScanMode(action_arg);
9903 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9905 int old_element = Feld[x][y];
9906 int new_element = GetElementFromGroupElement(element);
9907 int previous_move_direction = MovDir[x][y];
9908 int last_ce_value = CustomValue[x][y];
9909 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9910 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9911 boolean add_player_onto_element = (new_element_is_player &&
9912 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9913 IS_WALKABLE(old_element));
9915 if (!add_player_onto_element)
9917 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9918 RemoveMovingField(x, y);
9922 Feld[x][y] = new_element;
9924 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9925 MovDir[x][y] = previous_move_direction;
9927 if (element_info[new_element].use_last_ce_value)
9928 CustomValue[x][y] = last_ce_value;
9930 InitField_WithBug1(x, y, FALSE);
9932 new_element = Feld[x][y]; /* element may have changed */
9934 ResetGfxAnimation(x, y);
9935 ResetRandomAnimationValue(x, y);
9937 TEST_DrawLevelField(x, y);
9939 if (GFX_CRUMBLED(new_element))
9940 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9943 /* check if element under the player changes from accessible to unaccessible
9944 (needed for special case of dropping element which then changes) */
9945 /* (must be checked after creating new element for walkable group elements) */
9946 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9947 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9954 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9955 if (new_element_is_player)
9956 RelocatePlayer(x, y, new_element);
9959 ChangeCount[x][y]++; /* count number of changes in the same frame */
9961 TestIfBadThingTouchesPlayer(x, y);
9962 TestIfPlayerTouchesCustomElement(x, y);
9963 TestIfElementTouchesCustomElement(x, y);
9966 static void CreateField(int x, int y, int element)
9968 CreateFieldExt(x, y, element, FALSE);
9971 static void CreateElementFromChange(int x, int y, int element)
9973 element = GET_VALID_RUNTIME_ELEMENT(element);
9975 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9977 int old_element = Feld[x][y];
9979 /* prevent changed element from moving in same engine frame
9980 unless both old and new element can either fall or move */
9981 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9982 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9986 CreateFieldExt(x, y, element, TRUE);
9989 static boolean ChangeElement(int x, int y, int element, int page)
9991 struct ElementInfo *ei = &element_info[element];
9992 struct ElementChangeInfo *change = &ei->change_page[page];
9993 int ce_value = CustomValue[x][y];
9994 int ce_score = ei->collect_score;
9996 int old_element = Feld[x][y];
9998 /* always use default change event to prevent running into a loop */
9999 if (ChangeEvent[x][y] == -1)
10000 ChangeEvent[x][y] = CE_DELAY;
10002 if (ChangeEvent[x][y] == CE_DELAY)
10004 /* reset actual trigger element, trigger player and action element */
10005 change->actual_trigger_element = EL_EMPTY;
10006 change->actual_trigger_player = EL_EMPTY;
10007 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10008 change->actual_trigger_side = CH_SIDE_NONE;
10009 change->actual_trigger_ce_value = 0;
10010 change->actual_trigger_ce_score = 0;
10013 /* do not change elements more than a specified maximum number of changes */
10014 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10017 ChangeCount[x][y]++; /* count number of changes in the same frame */
10019 if (change->explode)
10026 if (change->use_target_content)
10028 boolean complete_replace = TRUE;
10029 boolean can_replace[3][3];
10032 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10035 boolean is_walkable;
10036 boolean is_diggable;
10037 boolean is_collectible;
10038 boolean is_removable;
10039 boolean is_destructible;
10040 int ex = x + xx - 1;
10041 int ey = y + yy - 1;
10042 int content_element = change->target_content.e[xx][yy];
10045 can_replace[xx][yy] = TRUE;
10047 if (ex == x && ey == y) /* do not check changing element itself */
10050 if (content_element == EL_EMPTY_SPACE)
10052 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10057 if (!IN_LEV_FIELD(ex, ey))
10059 can_replace[xx][yy] = FALSE;
10060 complete_replace = FALSE;
10067 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10068 e = MovingOrBlocked2Element(ex, ey);
10070 is_empty = (IS_FREE(ex, ey) ||
10071 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10073 is_walkable = (is_empty || IS_WALKABLE(e));
10074 is_diggable = (is_empty || IS_DIGGABLE(e));
10075 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10076 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10077 is_removable = (is_diggable || is_collectible);
10079 can_replace[xx][yy] =
10080 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10081 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10082 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10083 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10084 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10085 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10086 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10088 if (!can_replace[xx][yy])
10089 complete_replace = FALSE;
10092 if (!change->only_if_complete || complete_replace)
10094 boolean something_has_changed = FALSE;
10096 if (change->only_if_complete && change->use_random_replace &&
10097 RND(100) < change->random_percentage)
10100 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10102 int ex = x + xx - 1;
10103 int ey = y + yy - 1;
10104 int content_element;
10106 if (can_replace[xx][yy] && (!change->use_random_replace ||
10107 RND(100) < change->random_percentage))
10109 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10110 RemoveMovingField(ex, ey);
10112 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10114 content_element = change->target_content.e[xx][yy];
10115 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10116 ce_value, ce_score);
10118 CreateElementFromChange(ex, ey, target_element);
10120 something_has_changed = TRUE;
10122 /* for symmetry reasons, freeze newly created border elements */
10123 if (ex != x || ey != y)
10124 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10128 if (something_has_changed)
10130 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10131 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10137 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10138 ce_value, ce_score);
10140 if (element == EL_DIAGONAL_GROWING ||
10141 element == EL_DIAGONAL_SHRINKING)
10143 target_element = Store[x][y];
10145 Store[x][y] = EL_EMPTY;
10148 CreateElementFromChange(x, y, target_element);
10150 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10151 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10154 /* this uses direct change before indirect change */
10155 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10160 static void HandleElementChange(int x, int y, int page)
10162 int element = MovingOrBlocked2Element(x, y);
10163 struct ElementInfo *ei = &element_info[element];
10164 struct ElementChangeInfo *change = &ei->change_page[page];
10165 boolean handle_action_before_change = FALSE;
10168 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10169 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10172 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10173 x, y, element, element_info[element].token_name);
10174 printf("HandleElementChange(): This should never happen!\n");
10179 /* this can happen with classic bombs on walkable, changing elements */
10180 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10185 if (ChangeDelay[x][y] == 0) /* initialize element change */
10187 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10189 if (change->can_change)
10191 /* !!! not clear why graphic animation should be reset at all here !!! */
10192 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10193 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10196 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10198 When using an animation frame delay of 1 (this only happens with
10199 "sp_zonk.moving.left/right" in the classic graphics), the default
10200 (non-moving) animation shows wrong animation frames (while the
10201 moving animation, like "sp_zonk.moving.left/right", is correct,
10202 so this graphical bug never shows up with the classic graphics).
10203 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10204 be drawn instead of the correct frames 0,1,2,3. This is caused by
10205 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10206 an element change: First when the change delay ("ChangeDelay[][]")
10207 counter has reached zero after decrementing, then a second time in
10208 the next frame (after "GfxFrame[][]" was already incremented) when
10209 "ChangeDelay[][]" is reset to the initial delay value again.
10211 This causes frame 0 to be drawn twice, while the last frame won't
10212 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10214 As some animations may already be cleverly designed around this bug
10215 (at least the "Snake Bite" snake tail animation does this), it cannot
10216 simply be fixed here without breaking such existing animations.
10217 Unfortunately, it cannot easily be detected if a graphics set was
10218 designed "before" or "after" the bug was fixed. As a workaround,
10219 a new graphics set option "game.graphics_engine_version" was added
10220 to be able to specify the game's major release version for which the
10221 graphics set was designed, which can then be used to decide if the
10222 bugfix should be used (version 4 and above) or not (version 3 or
10223 below, or if no version was specified at all, as with old sets).
10225 (The wrong/fixed animation frames can be tested with the test level set
10226 "test_gfxframe" and level "000", which contains a specially prepared
10227 custom element at level position (x/y) == (11/9) which uses the zonk
10228 animation mentioned above. Using "game.graphics_engine_version: 4"
10229 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10230 This can also be seen from the debug output for this test element.)
10233 /* when a custom element is about to change (for example by change delay),
10234 do not reset graphic animation when the custom element is moving */
10235 if (game.graphics_engine_version < 4 &&
10238 ResetGfxAnimation(x, y);
10239 ResetRandomAnimationValue(x, y);
10242 if (change->pre_change_function)
10243 change->pre_change_function(x, y);
10247 ChangeDelay[x][y]--;
10249 if (ChangeDelay[x][y] != 0) /* continue element change */
10251 if (change->can_change)
10253 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10255 if (IS_ANIMATED(graphic))
10256 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10258 if (change->change_function)
10259 change->change_function(x, y);
10262 else /* finish element change */
10264 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10266 page = ChangePage[x][y];
10267 ChangePage[x][y] = -1;
10269 change = &ei->change_page[page];
10272 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10274 ChangeDelay[x][y] = 1; /* try change after next move step */
10275 ChangePage[x][y] = page; /* remember page to use for change */
10280 /* special case: set new level random seed before changing element */
10281 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10282 handle_action_before_change = TRUE;
10284 if (change->has_action && handle_action_before_change)
10285 ExecuteCustomElementAction(x, y, element, page);
10287 if (change->can_change)
10289 if (ChangeElement(x, y, element, page))
10291 if (change->post_change_function)
10292 change->post_change_function(x, y);
10296 if (change->has_action && !handle_action_before_change)
10297 ExecuteCustomElementAction(x, y, element, page);
10301 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10302 int trigger_element,
10304 int trigger_player,
10308 boolean change_done_any = FALSE;
10309 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10312 if (!(trigger_events[trigger_element][trigger_event]))
10315 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10317 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10319 int element = EL_CUSTOM_START + i;
10320 boolean change_done = FALSE;
10323 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10324 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10327 for (p = 0; p < element_info[element].num_change_pages; p++)
10329 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10331 if (change->can_change_or_has_action &&
10332 change->has_event[trigger_event] &&
10333 change->trigger_side & trigger_side &&
10334 change->trigger_player & trigger_player &&
10335 change->trigger_page & trigger_page_bits &&
10336 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10338 change->actual_trigger_element = trigger_element;
10339 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10340 change->actual_trigger_player_bits = trigger_player;
10341 change->actual_trigger_side = trigger_side;
10342 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10343 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10345 if ((change->can_change && !change_done) || change->has_action)
10349 SCAN_PLAYFIELD(x, y)
10351 if (Feld[x][y] == element)
10353 if (change->can_change && !change_done)
10355 /* if element already changed in this frame, not only prevent
10356 another element change (checked in ChangeElement()), but
10357 also prevent additional element actions for this element */
10359 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10360 !level.use_action_after_change_bug)
10363 ChangeDelay[x][y] = 1;
10364 ChangeEvent[x][y] = trigger_event;
10366 HandleElementChange(x, y, p);
10368 else if (change->has_action)
10370 /* if element already changed in this frame, not only prevent
10371 another element change (checked in ChangeElement()), but
10372 also prevent additional element actions for this element */
10374 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10375 !level.use_action_after_change_bug)
10378 ExecuteCustomElementAction(x, y, element, p);
10379 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10384 if (change->can_change)
10386 change_done = TRUE;
10387 change_done_any = TRUE;
10394 RECURSION_LOOP_DETECTION_END();
10396 return change_done_any;
10399 static boolean CheckElementChangeExt(int x, int y,
10401 int trigger_element,
10403 int trigger_player,
10406 boolean change_done = FALSE;
10409 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10410 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10413 if (Feld[x][y] == EL_BLOCKED)
10415 Blocked2Moving(x, y, &x, &y);
10416 element = Feld[x][y];
10419 /* check if element has already changed or is about to change after moving */
10420 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10421 Feld[x][y] != element) ||
10423 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10424 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10425 ChangePage[x][y] != -1)))
10428 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10430 for (p = 0; p < element_info[element].num_change_pages; p++)
10432 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10434 /* check trigger element for all events where the element that is checked
10435 for changing interacts with a directly adjacent element -- this is
10436 different to element changes that affect other elements to change on the
10437 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10438 boolean check_trigger_element =
10439 (trigger_event == CE_TOUCHING_X ||
10440 trigger_event == CE_HITTING_X ||
10441 trigger_event == CE_HIT_BY_X ||
10442 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10444 if (change->can_change_or_has_action &&
10445 change->has_event[trigger_event] &&
10446 change->trigger_side & trigger_side &&
10447 change->trigger_player & trigger_player &&
10448 (!check_trigger_element ||
10449 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10451 change->actual_trigger_element = trigger_element;
10452 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10453 change->actual_trigger_player_bits = trigger_player;
10454 change->actual_trigger_side = trigger_side;
10455 change->actual_trigger_ce_value = CustomValue[x][y];
10456 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10458 /* special case: trigger element not at (x,y) position for some events */
10459 if (check_trigger_element)
10471 { 0, 0 }, { 0, 0 }, { 0, 0 },
10475 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10476 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10478 change->actual_trigger_ce_value = CustomValue[xx][yy];
10479 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10482 if (change->can_change && !change_done)
10484 ChangeDelay[x][y] = 1;
10485 ChangeEvent[x][y] = trigger_event;
10487 HandleElementChange(x, y, p);
10489 change_done = TRUE;
10491 else if (change->has_action)
10493 ExecuteCustomElementAction(x, y, element, p);
10494 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10499 RECURSION_LOOP_DETECTION_END();
10501 return change_done;
10504 static void PlayPlayerSound(struct PlayerInfo *player)
10506 int jx = player->jx, jy = player->jy;
10507 int sound_element = player->artwork_element;
10508 int last_action = player->last_action_waiting;
10509 int action = player->action_waiting;
10511 if (player->is_waiting)
10513 if (action != last_action)
10514 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10516 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10520 if (action != last_action)
10521 StopSound(element_info[sound_element].sound[last_action]);
10523 if (last_action == ACTION_SLEEPING)
10524 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10528 static void PlayAllPlayersSound()
10532 for (i = 0; i < MAX_PLAYERS; i++)
10533 if (stored_player[i].active)
10534 PlayPlayerSound(&stored_player[i]);
10537 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10539 boolean last_waiting = player->is_waiting;
10540 int move_dir = player->MovDir;
10542 player->dir_waiting = move_dir;
10543 player->last_action_waiting = player->action_waiting;
10547 if (!last_waiting) /* not waiting -> waiting */
10549 player->is_waiting = TRUE;
10551 player->frame_counter_bored =
10553 game.player_boring_delay_fixed +
10554 GetSimpleRandom(game.player_boring_delay_random);
10555 player->frame_counter_sleeping =
10557 game.player_sleeping_delay_fixed +
10558 GetSimpleRandom(game.player_sleeping_delay_random);
10560 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10563 if (game.player_sleeping_delay_fixed +
10564 game.player_sleeping_delay_random > 0 &&
10565 player->anim_delay_counter == 0 &&
10566 player->post_delay_counter == 0 &&
10567 FrameCounter >= player->frame_counter_sleeping)
10568 player->is_sleeping = TRUE;
10569 else if (game.player_boring_delay_fixed +
10570 game.player_boring_delay_random > 0 &&
10571 FrameCounter >= player->frame_counter_bored)
10572 player->is_bored = TRUE;
10574 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10575 player->is_bored ? ACTION_BORING :
10578 if (player->is_sleeping && player->use_murphy)
10580 /* special case for sleeping Murphy when leaning against non-free tile */
10582 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10583 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10584 !IS_MOVING(player->jx - 1, player->jy)))
10585 move_dir = MV_LEFT;
10586 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10587 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10588 !IS_MOVING(player->jx + 1, player->jy)))
10589 move_dir = MV_RIGHT;
10591 player->is_sleeping = FALSE;
10593 player->dir_waiting = move_dir;
10596 if (player->is_sleeping)
10598 if (player->num_special_action_sleeping > 0)
10600 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10602 int last_special_action = player->special_action_sleeping;
10603 int num_special_action = player->num_special_action_sleeping;
10604 int special_action =
10605 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10606 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10607 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10608 last_special_action + 1 : ACTION_SLEEPING);
10609 int special_graphic =
10610 el_act_dir2img(player->artwork_element, special_action, move_dir);
10612 player->anim_delay_counter =
10613 graphic_info[special_graphic].anim_delay_fixed +
10614 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10615 player->post_delay_counter =
10616 graphic_info[special_graphic].post_delay_fixed +
10617 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10619 player->special_action_sleeping = special_action;
10622 if (player->anim_delay_counter > 0)
10624 player->action_waiting = player->special_action_sleeping;
10625 player->anim_delay_counter--;
10627 else if (player->post_delay_counter > 0)
10629 player->post_delay_counter--;
10633 else if (player->is_bored)
10635 if (player->num_special_action_bored > 0)
10637 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10639 int special_action =
10640 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10641 int special_graphic =
10642 el_act_dir2img(player->artwork_element, special_action, move_dir);
10644 player->anim_delay_counter =
10645 graphic_info[special_graphic].anim_delay_fixed +
10646 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10647 player->post_delay_counter =
10648 graphic_info[special_graphic].post_delay_fixed +
10649 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10651 player->special_action_bored = special_action;
10654 if (player->anim_delay_counter > 0)
10656 player->action_waiting = player->special_action_bored;
10657 player->anim_delay_counter--;
10659 else if (player->post_delay_counter > 0)
10661 player->post_delay_counter--;
10666 else if (last_waiting) /* waiting -> not waiting */
10668 player->is_waiting = FALSE;
10669 player->is_bored = FALSE;
10670 player->is_sleeping = FALSE;
10672 player->frame_counter_bored = -1;
10673 player->frame_counter_sleeping = -1;
10675 player->anim_delay_counter = 0;
10676 player->post_delay_counter = 0;
10678 player->dir_waiting = player->MovDir;
10679 player->action_waiting = ACTION_DEFAULT;
10681 player->special_action_bored = ACTION_DEFAULT;
10682 player->special_action_sleeping = ACTION_DEFAULT;
10686 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10688 if ((!player->is_moving && player->was_moving) ||
10689 (player->MovPos == 0 && player->was_moving) ||
10690 (player->is_snapping && !player->was_snapping) ||
10691 (player->is_dropping && !player->was_dropping))
10693 if (!CheckSaveEngineSnapshotToList())
10696 player->was_moving = FALSE;
10697 player->was_snapping = TRUE;
10698 player->was_dropping = TRUE;
10702 if (player->is_moving)
10703 player->was_moving = TRUE;
10705 if (!player->is_snapping)
10706 player->was_snapping = FALSE;
10708 if (!player->is_dropping)
10709 player->was_dropping = FALSE;
10713 static void CheckSingleStepMode(struct PlayerInfo *player)
10715 if (tape.single_step && tape.recording && !tape.pausing)
10717 /* as it is called "single step mode", just return to pause mode when the
10718 player stopped moving after one tile (or never starts moving at all) */
10719 if (!player->is_moving &&
10720 !player->is_pushing &&
10721 !player->is_dropping_pressed)
10723 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10724 SnapField(player, 0, 0); /* stop snapping */
10728 CheckSaveEngineSnapshot(player);
10731 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10733 int left = player_action & JOY_LEFT;
10734 int right = player_action & JOY_RIGHT;
10735 int up = player_action & JOY_UP;
10736 int down = player_action & JOY_DOWN;
10737 int button1 = player_action & JOY_BUTTON_1;
10738 int button2 = player_action & JOY_BUTTON_2;
10739 int dx = (left ? -1 : right ? 1 : 0);
10740 int dy = (up ? -1 : down ? 1 : 0);
10742 if (!player->active || tape.pausing)
10748 SnapField(player, dx, dy);
10752 DropElement(player);
10754 MovePlayer(player, dx, dy);
10757 CheckSingleStepMode(player);
10759 SetPlayerWaiting(player, FALSE);
10761 return player_action;
10765 /* no actions for this player (no input at player's configured device) */
10767 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10768 SnapField(player, 0, 0);
10769 CheckGravityMovementWhenNotMoving(player);
10771 if (player->MovPos == 0)
10772 SetPlayerWaiting(player, TRUE);
10774 if (player->MovPos == 0) /* needed for tape.playing */
10775 player->is_moving = FALSE;
10777 player->is_dropping = FALSE;
10778 player->is_dropping_pressed = FALSE;
10779 player->drop_pressed_delay = 0;
10781 CheckSingleStepMode(player);
10787 static void CheckLevelTime()
10791 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10792 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10794 if (level.native_em_level->lev->home == 0) /* all players at home */
10796 PlayerWins(local_player);
10798 AllPlayersGone = TRUE;
10800 level.native_em_level->lev->home = -1;
10803 if (level.native_em_level->ply[0]->alive == 0 &&
10804 level.native_em_level->ply[1]->alive == 0 &&
10805 level.native_em_level->ply[2]->alive == 0 &&
10806 level.native_em_level->ply[3]->alive == 0) /* all dead */
10807 AllPlayersGone = TRUE;
10809 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10811 if (game_sp.LevelSolved &&
10812 !game_sp.GameOver) /* game won */
10814 PlayerWins(local_player);
10816 game_sp.GameOver = TRUE;
10818 AllPlayersGone = TRUE;
10821 if (game_sp.GameOver) /* game lost */
10822 AllPlayersGone = TRUE;
10825 if (TimeFrames >= FRAMES_PER_SECOND)
10830 for (i = 0; i < MAX_PLAYERS; i++)
10832 struct PlayerInfo *player = &stored_player[i];
10834 if (SHIELD_ON(player))
10836 player->shield_normal_time_left--;
10838 if (player->shield_deadly_time_left > 0)
10839 player->shield_deadly_time_left--;
10843 if (!local_player->LevelSolved && !level.use_step_counter)
10851 if (TimeLeft <= 10 && setup.time_limit)
10852 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10854 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10855 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10857 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10859 if (!TimeLeft && setup.time_limit)
10861 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10862 level.native_em_level->lev->killed_out_of_time = TRUE;
10864 for (i = 0; i < MAX_PLAYERS; i++)
10865 KillPlayer(&stored_player[i]);
10868 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10870 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10873 level.native_em_level->lev->time =
10874 (game.no_time_limit ? TimePlayed : TimeLeft);
10877 if (tape.recording || tape.playing)
10878 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10881 if (tape.recording || tape.playing)
10882 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10884 UpdateAndDisplayGameControlValues();
10887 void AdvanceFrameAndPlayerCounters(int player_nr)
10891 /* advance frame counters (global frame counter and time frame counter) */
10895 /* advance player counters (counters for move delay, move animation etc.) */
10896 for (i = 0; i < MAX_PLAYERS; i++)
10898 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10899 int move_delay_value = stored_player[i].move_delay_value;
10900 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10902 if (!advance_player_counters) /* not all players may be affected */
10905 if (move_frames == 0) /* less than one move per game frame */
10907 int stepsize = TILEX / move_delay_value;
10908 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10909 int count = (stored_player[i].is_moving ?
10910 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10912 if (count % delay == 0)
10916 stored_player[i].Frame += move_frames;
10918 if (stored_player[i].MovPos != 0)
10919 stored_player[i].StepFrame += move_frames;
10921 if (stored_player[i].move_delay > 0)
10922 stored_player[i].move_delay--;
10924 /* due to bugs in previous versions, counter must count up, not down */
10925 if (stored_player[i].push_delay != -1)
10926 stored_player[i].push_delay++;
10928 if (stored_player[i].drop_delay > 0)
10929 stored_player[i].drop_delay--;
10931 if (stored_player[i].is_dropping_pressed)
10932 stored_player[i].drop_pressed_delay++;
10936 void StartGameActions(boolean init_network_game, boolean record_tape,
10939 unsigned int new_random_seed = InitRND(random_seed);
10942 TapeStartRecording(new_random_seed);
10944 #if defined(NETWORK_AVALIABLE)
10945 if (init_network_game)
10947 SendToServer_StartPlaying();
10956 void GameActionsExt()
10959 static unsigned int game_frame_delay = 0;
10961 unsigned int game_frame_delay_value;
10962 byte *recorded_player_action;
10963 byte summarized_player_action = 0;
10964 byte tape_action[MAX_PLAYERS];
10967 /* detect endless loops, caused by custom element programming */
10968 if (recursion_loop_detected && recursion_loop_depth == 0)
10970 char *message = getStringCat3("Internal Error! Element ",
10971 EL_NAME(recursion_loop_element),
10972 " caused endless loop! Quit the game?");
10974 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10975 EL_NAME(recursion_loop_element));
10977 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10979 recursion_loop_detected = FALSE; /* if game should be continued */
10986 if (game.restart_level)
10987 StartGameActions(options.network, setup.autorecord, level.random_seed);
10989 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10990 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10992 if (level.native_em_level->lev->home == 0) /* all players at home */
10994 PlayerWins(local_player);
10996 AllPlayersGone = TRUE;
10998 level.native_em_level->lev->home = -1;
11001 if (level.native_em_level->ply[0]->alive == 0 &&
11002 level.native_em_level->ply[1]->alive == 0 &&
11003 level.native_em_level->ply[2]->alive == 0 &&
11004 level.native_em_level->ply[3]->alive == 0) /* all dead */
11005 AllPlayersGone = TRUE;
11007 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11009 if (game_sp.LevelSolved &&
11010 !game_sp.GameOver) /* game won */
11012 PlayerWins(local_player);
11014 game_sp.GameOver = TRUE;
11016 AllPlayersGone = TRUE;
11019 if (game_sp.GameOver) /* game lost */
11020 AllPlayersGone = TRUE;
11023 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11026 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11029 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11032 game_frame_delay_value =
11033 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11035 if (tape.playing && tape.warp_forward && !tape.pausing)
11036 game_frame_delay_value = 0;
11038 SetVideoFrameDelay(game_frame_delay_value);
11042 /* ---------- main game synchronization point ---------- */
11044 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11046 printf("::: skip == %d\n", skip);
11049 /* ---------- main game synchronization point ---------- */
11051 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11055 if (network_playing && !network_player_action_received)
11057 /* try to get network player actions in time */
11059 #if defined(NETWORK_AVALIABLE)
11060 /* last chance to get network player actions without main loop delay */
11061 HandleNetworking();
11064 /* game was quit by network peer */
11065 if (game_status != GAME_MODE_PLAYING)
11068 if (!network_player_action_received)
11069 return; /* failed to get network player actions in time */
11071 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11077 /* at this point we know that we really continue executing the game */
11079 network_player_action_received = FALSE;
11081 /* when playing tape, read previously recorded player input from tape data */
11082 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11084 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11088 if (tape.set_centered_player)
11090 game.centered_player_nr_next = tape.centered_player_nr_next;
11091 game.set_centered_player = TRUE;
11094 for (i = 0; i < MAX_PLAYERS; i++)
11096 summarized_player_action |= stored_player[i].action;
11098 if (!network_playing && (game.team_mode || tape.playing))
11099 stored_player[i].effective_action = stored_player[i].action;
11102 #if defined(NETWORK_AVALIABLE)
11103 if (network_playing)
11104 SendToServer_MovePlayer(summarized_player_action);
11107 // summarize all actions at local players mapped input device position
11108 // (this allows using different input devices in single player mode)
11109 if (!options.network && !game.team_mode)
11110 stored_player[map_player_action[local_player->index_nr]].effective_action =
11111 summarized_player_action;
11113 if (tape.recording &&
11115 setup.input_on_focus &&
11116 game.centered_player_nr != -1)
11118 for (i = 0; i < MAX_PLAYERS; i++)
11119 stored_player[i].effective_action =
11120 (i == game.centered_player_nr ? summarized_player_action : 0);
11123 if (recorded_player_action != NULL)
11124 for (i = 0; i < MAX_PLAYERS; i++)
11125 stored_player[i].effective_action = recorded_player_action[i];
11127 for (i = 0; i < MAX_PLAYERS; i++)
11129 tape_action[i] = stored_player[i].effective_action;
11131 /* (this may happen in the RND game engine if a player was not present on
11132 the playfield on level start, but appeared later from a custom element */
11133 if (setup.team_mode &&
11136 !tape.player_participates[i])
11137 tape.player_participates[i] = TRUE;
11140 /* only record actions from input devices, but not programmed actions */
11141 if (tape.recording)
11142 TapeRecordAction(tape_action);
11144 #if USE_NEW_PLAYER_ASSIGNMENTS
11145 // !!! also map player actions in single player mode !!!
11146 // if (game.team_mode)
11149 byte mapped_action[MAX_PLAYERS];
11151 #if DEBUG_PLAYER_ACTIONS
11153 for (i = 0; i < MAX_PLAYERS; i++)
11154 printf(" %d, ", stored_player[i].effective_action);
11157 for (i = 0; i < MAX_PLAYERS; i++)
11158 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11160 for (i = 0; i < MAX_PLAYERS; i++)
11161 stored_player[i].effective_action = mapped_action[i];
11163 #if DEBUG_PLAYER_ACTIONS
11165 for (i = 0; i < MAX_PLAYERS; i++)
11166 printf(" %d, ", stored_player[i].effective_action);
11170 #if DEBUG_PLAYER_ACTIONS
11174 for (i = 0; i < MAX_PLAYERS; i++)
11175 printf(" %d, ", stored_player[i].effective_action);
11181 for (i = 0; i < MAX_PLAYERS; i++)
11183 // allow engine snapshot in case of changed movement attempt
11184 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11185 (stored_player[i].effective_action & KEY_MOTION))
11186 game.snapshot.changed_action = TRUE;
11188 // allow engine snapshot in case of snapping/dropping attempt
11189 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11190 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11191 game.snapshot.changed_action = TRUE;
11193 game.snapshot.last_action[i] = stored_player[i].effective_action;
11196 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11198 GameActions_EM_Main();
11200 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11202 GameActions_SP_Main();
11206 GameActions_RND_Main();
11209 BlitScreenToBitmap(backbuffer);
11213 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11215 if (options.debug) /* calculate frames per second */
11217 static unsigned int fps_counter = 0;
11218 static int fps_frames = 0;
11219 unsigned int fps_delay_ms = Counter() - fps_counter;
11223 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11225 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11228 fps_counter = Counter();
11231 redraw_mask |= REDRAW_FPS;
11235 static void GameActions_CheckSaveEngineSnapshot()
11237 if (!game.snapshot.save_snapshot)
11240 // clear flag for saving snapshot _before_ saving snapshot
11241 game.snapshot.save_snapshot = FALSE;
11243 SaveEngineSnapshotToList();
11250 GameActions_CheckSaveEngineSnapshot();
11253 void GameActions_EM_Main()
11255 byte effective_action[MAX_PLAYERS];
11256 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11259 for (i = 0; i < MAX_PLAYERS; i++)
11260 effective_action[i] = stored_player[i].effective_action;
11262 GameActions_EM(effective_action, warp_mode);
11265 void GameActions_SP_Main()
11267 byte effective_action[MAX_PLAYERS];
11268 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11271 for (i = 0; i < MAX_PLAYERS; i++)
11272 effective_action[i] = stored_player[i].effective_action;
11274 GameActions_SP(effective_action, warp_mode);
11276 for (i = 0; i < MAX_PLAYERS; i++)
11278 if (stored_player[i].force_dropping)
11279 stored_player[i].action |= KEY_BUTTON_DROP;
11281 stored_player[i].force_dropping = FALSE;
11285 void GameActions_RND_Main()
11290 void GameActions_RND()
11292 int magic_wall_x = 0, magic_wall_y = 0;
11293 int i, x, y, element, graphic, last_gfx_frame;
11295 InitPlayfieldScanModeVars();
11297 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11299 SCAN_PLAYFIELD(x, y)
11301 ChangeCount[x][y] = 0;
11302 ChangeEvent[x][y] = -1;
11306 if (game.set_centered_player)
11308 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11310 /* switching to "all players" only possible if all players fit to screen */
11311 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11313 game.centered_player_nr_next = game.centered_player_nr;
11314 game.set_centered_player = FALSE;
11317 /* do not switch focus to non-existing (or non-active) player */
11318 if (game.centered_player_nr_next >= 0 &&
11319 !stored_player[game.centered_player_nr_next].active)
11321 game.centered_player_nr_next = game.centered_player_nr;
11322 game.set_centered_player = FALSE;
11326 if (game.set_centered_player &&
11327 ScreenMovPos == 0) /* screen currently aligned at tile position */
11331 if (game.centered_player_nr_next == -1)
11333 setScreenCenteredToAllPlayers(&sx, &sy);
11337 sx = stored_player[game.centered_player_nr_next].jx;
11338 sy = stored_player[game.centered_player_nr_next].jy;
11341 game.centered_player_nr = game.centered_player_nr_next;
11342 game.set_centered_player = FALSE;
11344 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11345 DrawGameDoorValues();
11348 for (i = 0; i < MAX_PLAYERS; i++)
11350 int actual_player_action = stored_player[i].effective_action;
11353 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11354 - rnd_equinox_tetrachloride 048
11355 - rnd_equinox_tetrachloride_ii 096
11356 - rnd_emanuel_schmieg 002
11357 - doctor_sloan_ww 001, 020
11359 if (stored_player[i].MovPos == 0)
11360 CheckGravityMovement(&stored_player[i]);
11363 /* overwrite programmed action with tape action */
11364 if (stored_player[i].programmed_action)
11365 actual_player_action = stored_player[i].programmed_action;
11367 PlayerActions(&stored_player[i], actual_player_action);
11369 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11372 ScrollScreen(NULL, SCROLL_GO_ON);
11374 /* for backwards compatibility, the following code emulates a fixed bug that
11375 occured when pushing elements (causing elements that just made their last
11376 pushing step to already (if possible) make their first falling step in the
11377 same game frame, which is bad); this code is also needed to use the famous
11378 "spring push bug" which is used in older levels and might be wanted to be
11379 used also in newer levels, but in this case the buggy pushing code is only
11380 affecting the "spring" element and no other elements */
11382 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11384 for (i = 0; i < MAX_PLAYERS; i++)
11386 struct PlayerInfo *player = &stored_player[i];
11387 int x = player->jx;
11388 int y = player->jy;
11390 if (player->active && player->is_pushing && player->is_moving &&
11392 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11393 Feld[x][y] == EL_SPRING))
11395 ContinueMoving(x, y);
11397 /* continue moving after pushing (this is actually a bug) */
11398 if (!IS_MOVING(x, y))
11399 Stop[x][y] = FALSE;
11404 SCAN_PLAYFIELD(x, y)
11406 ChangeCount[x][y] = 0;
11407 ChangeEvent[x][y] = -1;
11409 /* this must be handled before main playfield loop */
11410 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11413 if (MovDelay[x][y] <= 0)
11417 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11420 if (MovDelay[x][y] <= 0)
11423 TEST_DrawLevelField(x, y);
11425 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11430 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11432 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11433 printf("GameActions(): This should never happen!\n");
11435 ChangePage[x][y] = -1;
11439 Stop[x][y] = FALSE;
11440 if (WasJustMoving[x][y] > 0)
11441 WasJustMoving[x][y]--;
11442 if (WasJustFalling[x][y] > 0)
11443 WasJustFalling[x][y]--;
11444 if (CheckCollision[x][y] > 0)
11445 CheckCollision[x][y]--;
11446 if (CheckImpact[x][y] > 0)
11447 CheckImpact[x][y]--;
11451 /* reset finished pushing action (not done in ContinueMoving() to allow
11452 continuous pushing animation for elements with zero push delay) */
11453 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11455 ResetGfxAnimation(x, y);
11456 TEST_DrawLevelField(x, y);
11460 if (IS_BLOCKED(x, y))
11464 Blocked2Moving(x, y, &oldx, &oldy);
11465 if (!IS_MOVING(oldx, oldy))
11467 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11468 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11469 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11470 printf("GameActions(): This should never happen!\n");
11476 SCAN_PLAYFIELD(x, y)
11478 element = Feld[x][y];
11479 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11480 last_gfx_frame = GfxFrame[x][y];
11482 ResetGfxFrame(x, y);
11484 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11485 DrawLevelGraphicAnimation(x, y, graphic);
11487 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11488 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11489 ResetRandomAnimationValue(x, y);
11491 SetRandomAnimationValue(x, y);
11493 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11495 if (IS_INACTIVE(element))
11497 if (IS_ANIMATED(graphic))
11498 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11503 /* this may take place after moving, so 'element' may have changed */
11504 if (IS_CHANGING(x, y) &&
11505 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11507 int page = element_info[element].event_page_nr[CE_DELAY];
11509 HandleElementChange(x, y, page);
11511 element = Feld[x][y];
11512 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11515 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11519 element = Feld[x][y];
11520 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11522 if (IS_ANIMATED(graphic) &&
11523 !IS_MOVING(x, y) &&
11525 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11527 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11528 TEST_DrawTwinkleOnField(x, y);
11530 else if (element == EL_ACID)
11533 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11535 else if ((element == EL_EXIT_OPEN ||
11536 element == EL_EM_EXIT_OPEN ||
11537 element == EL_SP_EXIT_OPEN ||
11538 element == EL_STEEL_EXIT_OPEN ||
11539 element == EL_EM_STEEL_EXIT_OPEN ||
11540 element == EL_SP_TERMINAL ||
11541 element == EL_SP_TERMINAL_ACTIVE ||
11542 element == EL_EXTRA_TIME ||
11543 element == EL_SHIELD_NORMAL ||
11544 element == EL_SHIELD_DEADLY) &&
11545 IS_ANIMATED(graphic))
11546 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11547 else if (IS_MOVING(x, y))
11548 ContinueMoving(x, y);
11549 else if (IS_ACTIVE_BOMB(element))
11550 CheckDynamite(x, y);
11551 else if (element == EL_AMOEBA_GROWING)
11552 AmoebeWaechst(x, y);
11553 else if (element == EL_AMOEBA_SHRINKING)
11554 AmoebaDisappearing(x, y);
11556 #if !USE_NEW_AMOEBA_CODE
11557 else if (IS_AMOEBALIVE(element))
11558 AmoebeAbleger(x, y);
11561 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11563 else if (element == EL_EXIT_CLOSED)
11565 else if (element == EL_EM_EXIT_CLOSED)
11567 else if (element == EL_STEEL_EXIT_CLOSED)
11568 CheckExitSteel(x, y);
11569 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11570 CheckExitSteelEM(x, y);
11571 else if (element == EL_SP_EXIT_CLOSED)
11573 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11574 element == EL_EXPANDABLE_STEELWALL_GROWING)
11575 MauerWaechst(x, y);
11576 else if (element == EL_EXPANDABLE_WALL ||
11577 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11578 element == EL_EXPANDABLE_WALL_VERTICAL ||
11579 element == EL_EXPANDABLE_WALL_ANY ||
11580 element == EL_BD_EXPANDABLE_WALL)
11581 MauerAbleger(x, y);
11582 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11583 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11584 element == EL_EXPANDABLE_STEELWALL_ANY)
11585 MauerAblegerStahl(x, y);
11586 else if (element == EL_FLAMES)
11587 CheckForDragon(x, y);
11588 else if (element == EL_EXPLOSION)
11589 ; /* drawing of correct explosion animation is handled separately */
11590 else if (element == EL_ELEMENT_SNAPPING ||
11591 element == EL_DIAGONAL_SHRINKING ||
11592 element == EL_DIAGONAL_GROWING)
11594 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11596 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11598 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11599 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11601 if (IS_BELT_ACTIVE(element))
11602 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11604 if (game.magic_wall_active)
11606 int jx = local_player->jx, jy = local_player->jy;
11608 /* play the element sound at the position nearest to the player */
11609 if ((element == EL_MAGIC_WALL_FULL ||
11610 element == EL_MAGIC_WALL_ACTIVE ||
11611 element == EL_MAGIC_WALL_EMPTYING ||
11612 element == EL_BD_MAGIC_WALL_FULL ||
11613 element == EL_BD_MAGIC_WALL_ACTIVE ||
11614 element == EL_BD_MAGIC_WALL_EMPTYING ||
11615 element == EL_DC_MAGIC_WALL_FULL ||
11616 element == EL_DC_MAGIC_WALL_ACTIVE ||
11617 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11618 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11626 #if USE_NEW_AMOEBA_CODE
11627 /* new experimental amoeba growth stuff */
11628 if (!(FrameCounter % 8))
11630 static unsigned int random = 1684108901;
11632 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11634 x = RND(lev_fieldx);
11635 y = RND(lev_fieldy);
11636 element = Feld[x][y];
11638 if (!IS_PLAYER(x,y) &&
11639 (element == EL_EMPTY ||
11640 CAN_GROW_INTO(element) ||
11641 element == EL_QUICKSAND_EMPTY ||
11642 element == EL_QUICKSAND_FAST_EMPTY ||
11643 element == EL_ACID_SPLASH_LEFT ||
11644 element == EL_ACID_SPLASH_RIGHT))
11646 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11647 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11648 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11649 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11650 Feld[x][y] = EL_AMOEBA_DROP;
11653 random = random * 129 + 1;
11658 game.explosions_delayed = FALSE;
11660 SCAN_PLAYFIELD(x, y)
11662 element = Feld[x][y];
11664 if (ExplodeField[x][y])
11665 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11666 else if (element == EL_EXPLOSION)
11667 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11669 ExplodeField[x][y] = EX_TYPE_NONE;
11672 game.explosions_delayed = TRUE;
11674 if (game.magic_wall_active)
11676 if (!(game.magic_wall_time_left % 4))
11678 int element = Feld[magic_wall_x][magic_wall_y];
11680 if (element == EL_BD_MAGIC_WALL_FULL ||
11681 element == EL_BD_MAGIC_WALL_ACTIVE ||
11682 element == EL_BD_MAGIC_WALL_EMPTYING)
11683 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11684 else if (element == EL_DC_MAGIC_WALL_FULL ||
11685 element == EL_DC_MAGIC_WALL_ACTIVE ||
11686 element == EL_DC_MAGIC_WALL_EMPTYING)
11687 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11689 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11692 if (game.magic_wall_time_left > 0)
11694 game.magic_wall_time_left--;
11696 if (!game.magic_wall_time_left)
11698 SCAN_PLAYFIELD(x, y)
11700 element = Feld[x][y];
11702 if (element == EL_MAGIC_WALL_ACTIVE ||
11703 element == EL_MAGIC_WALL_FULL)
11705 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11706 TEST_DrawLevelField(x, y);
11708 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11709 element == EL_BD_MAGIC_WALL_FULL)
11711 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11712 TEST_DrawLevelField(x, y);
11714 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11715 element == EL_DC_MAGIC_WALL_FULL)
11717 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11718 TEST_DrawLevelField(x, y);
11722 game.magic_wall_active = FALSE;
11727 if (game.light_time_left > 0)
11729 game.light_time_left--;
11731 if (game.light_time_left == 0)
11732 RedrawAllLightSwitchesAndInvisibleElements();
11735 if (game.timegate_time_left > 0)
11737 game.timegate_time_left--;
11739 if (game.timegate_time_left == 0)
11740 CloseAllOpenTimegates();
11743 if (game.lenses_time_left > 0)
11745 game.lenses_time_left--;
11747 if (game.lenses_time_left == 0)
11748 RedrawAllInvisibleElementsForLenses();
11751 if (game.magnify_time_left > 0)
11753 game.magnify_time_left--;
11755 if (game.magnify_time_left == 0)
11756 RedrawAllInvisibleElementsForMagnifier();
11759 for (i = 0; i < MAX_PLAYERS; i++)
11761 struct PlayerInfo *player = &stored_player[i];
11763 if (SHIELD_ON(player))
11765 if (player->shield_deadly_time_left)
11766 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11767 else if (player->shield_normal_time_left)
11768 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11772 #if USE_DELAYED_GFX_REDRAW
11773 SCAN_PLAYFIELD(x, y)
11775 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11777 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11778 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11780 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11781 DrawLevelField(x, y);
11783 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11784 DrawLevelFieldCrumbled(x, y);
11786 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11787 DrawLevelFieldCrumbledNeighbours(x, y);
11789 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11790 DrawTwinkleOnField(x, y);
11793 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11798 PlayAllPlayersSound();
11800 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11802 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11804 local_player->show_envelope = 0;
11807 /* use random number generator in every frame to make it less predictable */
11808 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11812 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11814 int min_x = x, min_y = y, max_x = x, max_y = y;
11817 for (i = 0; i < MAX_PLAYERS; i++)
11819 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11821 if (!stored_player[i].active || &stored_player[i] == player)
11824 min_x = MIN(min_x, jx);
11825 min_y = MIN(min_y, jy);
11826 max_x = MAX(max_x, jx);
11827 max_y = MAX(max_y, jy);
11830 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11833 static boolean AllPlayersInVisibleScreen()
11837 for (i = 0; i < MAX_PLAYERS; i++)
11839 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11841 if (!stored_player[i].active)
11844 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11851 void ScrollLevel(int dx, int dy)
11853 int scroll_offset = 2 * TILEX_VAR;
11856 BlitBitmap(drawto_field, drawto_field,
11857 FX + TILEX_VAR * (dx == -1) - scroll_offset,
11858 FY + TILEY_VAR * (dy == -1) - scroll_offset,
11859 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11860 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11861 FX + TILEX_VAR * (dx == 1) - scroll_offset,
11862 FY + TILEY_VAR * (dy == 1) - scroll_offset);
11866 x = (dx == 1 ? BX1 : BX2);
11867 for (y = BY1; y <= BY2; y++)
11868 DrawScreenField(x, y);
11873 y = (dy == 1 ? BY1 : BY2);
11874 for (x = BX1; x <= BX2; x++)
11875 DrawScreenField(x, y);
11878 redraw_mask |= REDRAW_FIELD;
11881 static boolean canFallDown(struct PlayerInfo *player)
11883 int jx = player->jx, jy = player->jy;
11885 return (IN_LEV_FIELD(jx, jy + 1) &&
11886 (IS_FREE(jx, jy + 1) ||
11887 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11888 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11889 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11892 static boolean canPassField(int x, int y, int move_dir)
11894 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11895 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11896 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11897 int nextx = x + dx;
11898 int nexty = y + dy;
11899 int element = Feld[x][y];
11901 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11902 !CAN_MOVE(element) &&
11903 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11904 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11905 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11908 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11910 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11911 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11912 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11916 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11917 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11918 (IS_DIGGABLE(Feld[newx][newy]) ||
11919 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11920 canPassField(newx, newy, move_dir)));
11923 static void CheckGravityMovement(struct PlayerInfo *player)
11925 if (player->gravity && !player->programmed_action)
11927 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11928 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11929 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11930 int jx = player->jx, jy = player->jy;
11931 boolean player_is_moving_to_valid_field =
11932 (!player_is_snapping &&
11933 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11934 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11935 boolean player_can_fall_down = canFallDown(player);
11937 if (player_can_fall_down &&
11938 !player_is_moving_to_valid_field)
11939 player->programmed_action = MV_DOWN;
11943 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11945 return CheckGravityMovement(player);
11947 if (player->gravity && !player->programmed_action)
11949 int jx = player->jx, jy = player->jy;
11950 boolean field_under_player_is_free =
11951 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11952 boolean player_is_standing_on_valid_field =
11953 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11954 (IS_WALKABLE(Feld[jx][jy]) &&
11955 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11957 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11958 player->programmed_action = MV_DOWN;
11963 MovePlayerOneStep()
11964 -----------------------------------------------------------------------------
11965 dx, dy: direction (non-diagonal) to try to move the player to
11966 real_dx, real_dy: direction as read from input device (can be diagonal)
11969 boolean MovePlayerOneStep(struct PlayerInfo *player,
11970 int dx, int dy, int real_dx, int real_dy)
11972 int jx = player->jx, jy = player->jy;
11973 int new_jx = jx + dx, new_jy = jy + dy;
11975 boolean player_can_move = !player->cannot_move;
11977 if (!player->active || (!dx && !dy))
11978 return MP_NO_ACTION;
11980 player->MovDir = (dx < 0 ? MV_LEFT :
11981 dx > 0 ? MV_RIGHT :
11983 dy > 0 ? MV_DOWN : MV_NONE);
11985 if (!IN_LEV_FIELD(new_jx, new_jy))
11986 return MP_NO_ACTION;
11988 if (!player_can_move)
11990 if (player->MovPos == 0)
11992 player->is_moving = FALSE;
11993 player->is_digging = FALSE;
11994 player->is_collecting = FALSE;
11995 player->is_snapping = FALSE;
11996 player->is_pushing = FALSE;
12000 if (!options.network && game.centered_player_nr == -1 &&
12001 !AllPlayersInSight(player, new_jx, new_jy))
12002 return MP_NO_ACTION;
12004 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12005 if (can_move != MP_MOVING)
12008 /* check if DigField() has caused relocation of the player */
12009 if (player->jx != jx || player->jy != jy)
12010 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12012 StorePlayer[jx][jy] = 0;
12013 player->last_jx = jx;
12014 player->last_jy = jy;
12015 player->jx = new_jx;
12016 player->jy = new_jy;
12017 StorePlayer[new_jx][new_jy] = player->element_nr;
12019 if (player->move_delay_value_next != -1)
12021 player->move_delay_value = player->move_delay_value_next;
12022 player->move_delay_value_next = -1;
12026 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12028 player->step_counter++;
12030 PlayerVisit[jx][jy] = FrameCounter;
12032 player->is_moving = TRUE;
12035 /* should better be called in MovePlayer(), but this breaks some tapes */
12036 ScrollPlayer(player, SCROLL_INIT);
12042 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12044 int jx = player->jx, jy = player->jy;
12045 int old_jx = jx, old_jy = jy;
12046 int moved = MP_NO_ACTION;
12048 if (!player->active)
12053 if (player->MovPos == 0)
12055 player->is_moving = FALSE;
12056 player->is_digging = FALSE;
12057 player->is_collecting = FALSE;
12058 player->is_snapping = FALSE;
12059 player->is_pushing = FALSE;
12065 if (player->move_delay > 0)
12068 player->move_delay = -1; /* set to "uninitialized" value */
12070 /* store if player is automatically moved to next field */
12071 player->is_auto_moving = (player->programmed_action != MV_NONE);
12073 /* remove the last programmed player action */
12074 player->programmed_action = 0;
12076 if (player->MovPos)
12078 /* should only happen if pre-1.2 tape recordings are played */
12079 /* this is only for backward compatibility */
12081 int original_move_delay_value = player->move_delay_value;
12084 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12088 /* scroll remaining steps with finest movement resolution */
12089 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12091 while (player->MovPos)
12093 ScrollPlayer(player, SCROLL_GO_ON);
12094 ScrollScreen(NULL, SCROLL_GO_ON);
12096 AdvanceFrameAndPlayerCounters(player->index_nr);
12099 BackToFront_WithFrameDelay(0);
12102 player->move_delay_value = original_move_delay_value;
12105 player->is_active = FALSE;
12107 if (player->last_move_dir & MV_HORIZONTAL)
12109 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12110 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12114 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12115 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12118 if (!moved && !player->is_active)
12120 player->is_moving = FALSE;
12121 player->is_digging = FALSE;
12122 player->is_collecting = FALSE;
12123 player->is_snapping = FALSE;
12124 player->is_pushing = FALSE;
12130 if (moved & MP_MOVING && !ScreenMovPos &&
12131 (player->index_nr == game.centered_player_nr ||
12132 game.centered_player_nr == -1))
12134 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12135 int offset = game.scroll_delay_value;
12137 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12139 /* actual player has left the screen -- scroll in that direction */
12140 if (jx != old_jx) /* player has moved horizontally */
12141 scroll_x += (jx - old_jx);
12142 else /* player has moved vertically */
12143 scroll_y += (jy - old_jy);
12147 if (jx != old_jx) /* player has moved horizontally */
12149 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12150 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12151 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12153 /* don't scroll over playfield boundaries */
12154 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12155 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12157 /* don't scroll more than one field at a time */
12158 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12160 /* don't scroll against the player's moving direction */
12161 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12162 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12163 scroll_x = old_scroll_x;
12165 else /* player has moved vertically */
12167 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12168 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12169 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12171 /* don't scroll over playfield boundaries */
12172 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12173 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12175 /* don't scroll more than one field at a time */
12176 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12178 /* don't scroll against the player's moving direction */
12179 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12180 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12181 scroll_y = old_scroll_y;
12185 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12187 if (!options.network && game.centered_player_nr == -1 &&
12188 !AllPlayersInVisibleScreen())
12190 scroll_x = old_scroll_x;
12191 scroll_y = old_scroll_y;
12195 ScrollScreen(player, SCROLL_INIT);
12196 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12201 player->StepFrame = 0;
12203 if (moved & MP_MOVING)
12205 if (old_jx != jx && old_jy == jy)
12206 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12207 else if (old_jx == jx && old_jy != jy)
12208 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12210 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12212 player->last_move_dir = player->MovDir;
12213 player->is_moving = TRUE;
12214 player->is_snapping = FALSE;
12215 player->is_switching = FALSE;
12216 player->is_dropping = FALSE;
12217 player->is_dropping_pressed = FALSE;
12218 player->drop_pressed_delay = 0;
12221 /* should better be called here than above, but this breaks some tapes */
12222 ScrollPlayer(player, SCROLL_INIT);
12227 CheckGravityMovementWhenNotMoving(player);
12229 player->is_moving = FALSE;
12231 /* at this point, the player is allowed to move, but cannot move right now
12232 (e.g. because of something blocking the way) -- ensure that the player
12233 is also allowed to move in the next frame (in old versions before 3.1.1,
12234 the player was forced to wait again for eight frames before next try) */
12236 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12237 player->move_delay = 0; /* allow direct movement in the next frame */
12240 if (player->move_delay == -1) /* not yet initialized by DigField() */
12241 player->move_delay = player->move_delay_value;
12243 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12245 TestIfPlayerTouchesBadThing(jx, jy);
12246 TestIfPlayerTouchesCustomElement(jx, jy);
12249 if (!player->active)
12250 RemovePlayer(player);
12255 void ScrollPlayer(struct PlayerInfo *player, int mode)
12257 int jx = player->jx, jy = player->jy;
12258 int last_jx = player->last_jx, last_jy = player->last_jy;
12259 int move_stepsize = TILEX / player->move_delay_value;
12261 if (!player->active)
12264 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12267 if (mode == SCROLL_INIT)
12269 player->actual_frame_counter = FrameCounter;
12270 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12272 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12273 Feld[last_jx][last_jy] == EL_EMPTY)
12275 int last_field_block_delay = 0; /* start with no blocking at all */
12276 int block_delay_adjustment = player->block_delay_adjustment;
12278 /* if player blocks last field, add delay for exactly one move */
12279 if (player->block_last_field)
12281 last_field_block_delay += player->move_delay_value;
12283 /* when blocking enabled, prevent moving up despite gravity */
12284 if (player->gravity && player->MovDir == MV_UP)
12285 block_delay_adjustment = -1;
12288 /* add block delay adjustment (also possible when not blocking) */
12289 last_field_block_delay += block_delay_adjustment;
12291 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12292 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12295 if (player->MovPos != 0) /* player has not yet reached destination */
12298 else if (!FrameReached(&player->actual_frame_counter, 1))
12301 if (player->MovPos != 0)
12303 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12304 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12306 /* before DrawPlayer() to draw correct player graphic for this case */
12307 if (player->MovPos == 0)
12308 CheckGravityMovement(player);
12311 if (player->MovPos == 0) /* player reached destination field */
12313 if (player->move_delay_reset_counter > 0)
12315 player->move_delay_reset_counter--;
12317 if (player->move_delay_reset_counter == 0)
12319 /* continue with normal speed after quickly moving through gate */
12320 HALVE_PLAYER_SPEED(player);
12322 /* be able to make the next move without delay */
12323 player->move_delay = 0;
12327 player->last_jx = jx;
12328 player->last_jy = jy;
12330 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12331 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12332 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12333 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12334 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12335 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12336 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12337 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12339 DrawPlayer(player); /* needed here only to cleanup last field */
12340 RemovePlayer(player);
12342 if (local_player->friends_still_needed == 0 ||
12343 IS_SP_ELEMENT(Feld[jx][jy]))
12344 PlayerWins(player);
12347 /* this breaks one level: "machine", level 000 */
12349 int move_direction = player->MovDir;
12350 int enter_side = MV_DIR_OPPOSITE(move_direction);
12351 int leave_side = move_direction;
12352 int old_jx = last_jx;
12353 int old_jy = last_jy;
12354 int old_element = Feld[old_jx][old_jy];
12355 int new_element = Feld[jx][jy];
12357 if (IS_CUSTOM_ELEMENT(old_element))
12358 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12360 player->index_bit, leave_side);
12362 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12363 CE_PLAYER_LEAVES_X,
12364 player->index_bit, leave_side);
12366 if (IS_CUSTOM_ELEMENT(new_element))
12367 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12368 player->index_bit, enter_side);
12370 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12371 CE_PLAYER_ENTERS_X,
12372 player->index_bit, enter_side);
12374 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12375 CE_MOVE_OF_X, move_direction);
12378 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12380 TestIfPlayerTouchesBadThing(jx, jy);
12381 TestIfPlayerTouchesCustomElement(jx, jy);
12383 /* needed because pushed element has not yet reached its destination,
12384 so it would trigger a change event at its previous field location */
12385 if (!player->is_pushing)
12386 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12388 if (!player->active)
12389 RemovePlayer(player);
12392 if (!local_player->LevelSolved && level.use_step_counter)
12402 if (TimeLeft <= 10 && setup.time_limit)
12403 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12405 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12407 DisplayGameControlValues();
12409 if (!TimeLeft && setup.time_limit)
12410 for (i = 0; i < MAX_PLAYERS; i++)
12411 KillPlayer(&stored_player[i]);
12413 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12415 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12417 DisplayGameControlValues();
12421 if (tape.single_step && tape.recording && !tape.pausing &&
12422 !player->programmed_action)
12423 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12425 if (!player->programmed_action)
12426 CheckSaveEngineSnapshot(player);
12430 void ScrollScreen(struct PlayerInfo *player, int mode)
12432 static unsigned int screen_frame_counter = 0;
12434 if (mode == SCROLL_INIT)
12436 /* set scrolling step size according to actual player's moving speed */
12437 ScrollStepSize = TILEX / player->move_delay_value;
12439 screen_frame_counter = FrameCounter;
12440 ScreenMovDir = player->MovDir;
12441 ScreenMovPos = player->MovPos;
12442 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12445 else if (!FrameReached(&screen_frame_counter, 1))
12450 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12451 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12452 redraw_mask |= REDRAW_FIELD;
12455 ScreenMovDir = MV_NONE;
12458 void TestIfPlayerTouchesCustomElement(int x, int y)
12460 static int xy[4][2] =
12467 static int trigger_sides[4][2] =
12469 /* center side border side */
12470 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12471 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12472 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12473 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12475 static int touch_dir[4] =
12477 MV_LEFT | MV_RIGHT,
12482 int center_element = Feld[x][y]; /* should always be non-moving! */
12485 for (i = 0; i < NUM_DIRECTIONS; i++)
12487 int xx = x + xy[i][0];
12488 int yy = y + xy[i][1];
12489 int center_side = trigger_sides[i][0];
12490 int border_side = trigger_sides[i][1];
12491 int border_element;
12493 if (!IN_LEV_FIELD(xx, yy))
12496 if (IS_PLAYER(x, y)) /* player found at center element */
12498 struct PlayerInfo *player = PLAYERINFO(x, y);
12500 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12501 border_element = Feld[xx][yy]; /* may be moving! */
12502 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12503 border_element = Feld[xx][yy];
12504 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12505 border_element = MovingOrBlocked2Element(xx, yy);
12507 continue; /* center and border element do not touch */
12509 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12510 player->index_bit, border_side);
12511 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12512 CE_PLAYER_TOUCHES_X,
12513 player->index_bit, border_side);
12516 /* use player element that is initially defined in the level playfield,
12517 not the player element that corresponds to the runtime player number
12518 (example: a level that contains EL_PLAYER_3 as the only player would
12519 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12520 int player_element = PLAYERINFO(x, y)->initial_element;
12522 CheckElementChangeBySide(xx, yy, border_element, player_element,
12523 CE_TOUCHING_X, border_side);
12526 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12528 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12530 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12532 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12533 continue; /* center and border element do not touch */
12536 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12537 player->index_bit, center_side);
12538 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12539 CE_PLAYER_TOUCHES_X,
12540 player->index_bit, center_side);
12543 /* use player element that is initially defined in the level playfield,
12544 not the player element that corresponds to the runtime player number
12545 (example: a level that contains EL_PLAYER_3 as the only player would
12546 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12547 int player_element = PLAYERINFO(xx, yy)->initial_element;
12549 CheckElementChangeBySide(x, y, center_element, player_element,
12550 CE_TOUCHING_X, center_side);
12558 void TestIfElementTouchesCustomElement(int x, int y)
12560 static int xy[4][2] =
12567 static int trigger_sides[4][2] =
12569 /* center side border side */
12570 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12571 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12572 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12573 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12575 static int touch_dir[4] =
12577 MV_LEFT | MV_RIGHT,
12582 boolean change_center_element = FALSE;
12583 int center_element = Feld[x][y]; /* should always be non-moving! */
12584 int border_element_old[NUM_DIRECTIONS];
12587 for (i = 0; i < NUM_DIRECTIONS; i++)
12589 int xx = x + xy[i][0];
12590 int yy = y + xy[i][1];
12591 int border_element;
12593 border_element_old[i] = -1;
12595 if (!IN_LEV_FIELD(xx, yy))
12598 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12599 border_element = Feld[xx][yy]; /* may be moving! */
12600 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12601 border_element = Feld[xx][yy];
12602 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12603 border_element = MovingOrBlocked2Element(xx, yy);
12605 continue; /* center and border element do not touch */
12607 border_element_old[i] = border_element;
12610 for (i = 0; i < NUM_DIRECTIONS; i++)
12612 int xx = x + xy[i][0];
12613 int yy = y + xy[i][1];
12614 int center_side = trigger_sides[i][0];
12615 int border_element = border_element_old[i];
12617 if (border_element == -1)
12620 /* check for change of border element */
12621 CheckElementChangeBySide(xx, yy, border_element, center_element,
12622 CE_TOUCHING_X, center_side);
12624 /* (center element cannot be player, so we dont have to check this here) */
12627 for (i = 0; i < NUM_DIRECTIONS; i++)
12629 int xx = x + xy[i][0];
12630 int yy = y + xy[i][1];
12631 int border_side = trigger_sides[i][1];
12632 int border_element = border_element_old[i];
12634 if (border_element == -1)
12637 /* check for change of center element (but change it only once) */
12638 if (!change_center_element)
12639 change_center_element =
12640 CheckElementChangeBySide(x, y, center_element, border_element,
12641 CE_TOUCHING_X, border_side);
12643 if (IS_PLAYER(xx, yy))
12645 /* use player element that is initially defined in the level playfield,
12646 not the player element that corresponds to the runtime player number
12647 (example: a level that contains EL_PLAYER_3 as the only player would
12648 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12649 int player_element = PLAYERINFO(xx, yy)->initial_element;
12651 CheckElementChangeBySide(x, y, center_element, player_element,
12652 CE_TOUCHING_X, border_side);
12657 void TestIfElementHitsCustomElement(int x, int y, int direction)
12659 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12660 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12661 int hitx = x + dx, hity = y + dy;
12662 int hitting_element = Feld[x][y];
12663 int touched_element;
12665 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12668 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12669 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12671 if (IN_LEV_FIELD(hitx, hity))
12673 int opposite_direction = MV_DIR_OPPOSITE(direction);
12674 int hitting_side = direction;
12675 int touched_side = opposite_direction;
12676 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12677 MovDir[hitx][hity] != direction ||
12678 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12684 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12685 CE_HITTING_X, touched_side);
12687 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12688 CE_HIT_BY_X, hitting_side);
12690 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12691 CE_HIT_BY_SOMETHING, opposite_direction);
12693 if (IS_PLAYER(hitx, hity))
12695 /* use player element that is initially defined in the level playfield,
12696 not the player element that corresponds to the runtime player number
12697 (example: a level that contains EL_PLAYER_3 as the only player would
12698 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12699 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12701 CheckElementChangeBySide(x, y, hitting_element, player_element,
12702 CE_HITTING_X, touched_side);
12707 /* "hitting something" is also true when hitting the playfield border */
12708 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12709 CE_HITTING_SOMETHING, direction);
12712 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12714 int i, kill_x = -1, kill_y = -1;
12716 int bad_element = -1;
12717 static int test_xy[4][2] =
12724 static int test_dir[4] =
12732 for (i = 0; i < NUM_DIRECTIONS; i++)
12734 int test_x, test_y, test_move_dir, test_element;
12736 test_x = good_x + test_xy[i][0];
12737 test_y = good_y + test_xy[i][1];
12739 if (!IN_LEV_FIELD(test_x, test_y))
12743 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12745 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12747 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12748 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12750 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12751 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12755 bad_element = test_element;
12761 if (kill_x != -1 || kill_y != -1)
12763 if (IS_PLAYER(good_x, good_y))
12765 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12767 if (player->shield_deadly_time_left > 0 &&
12768 !IS_INDESTRUCTIBLE(bad_element))
12769 Bang(kill_x, kill_y);
12770 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12771 KillPlayer(player);
12774 Bang(good_x, good_y);
12778 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12780 int i, kill_x = -1, kill_y = -1;
12781 int bad_element = Feld[bad_x][bad_y];
12782 static int test_xy[4][2] =
12789 static int touch_dir[4] =
12791 MV_LEFT | MV_RIGHT,
12796 static int test_dir[4] =
12804 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12807 for (i = 0; i < NUM_DIRECTIONS; i++)
12809 int test_x, test_y, test_move_dir, test_element;
12811 test_x = bad_x + test_xy[i][0];
12812 test_y = bad_y + test_xy[i][1];
12814 if (!IN_LEV_FIELD(test_x, test_y))
12818 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12820 test_element = Feld[test_x][test_y];
12822 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12823 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12825 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12826 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12828 /* good thing is player or penguin that does not move away */
12829 if (IS_PLAYER(test_x, test_y))
12831 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12833 if (bad_element == EL_ROBOT && player->is_moving)
12834 continue; /* robot does not kill player if he is moving */
12836 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12838 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12839 continue; /* center and border element do not touch */
12847 else if (test_element == EL_PENGUIN)
12857 if (kill_x != -1 || kill_y != -1)
12859 if (IS_PLAYER(kill_x, kill_y))
12861 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12863 if (player->shield_deadly_time_left > 0 &&
12864 !IS_INDESTRUCTIBLE(bad_element))
12865 Bang(bad_x, bad_y);
12866 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12867 KillPlayer(player);
12870 Bang(kill_x, kill_y);
12874 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12876 int bad_element = Feld[bad_x][bad_y];
12877 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12878 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12879 int test_x = bad_x + dx, test_y = bad_y + dy;
12880 int test_move_dir, test_element;
12881 int kill_x = -1, kill_y = -1;
12883 if (!IN_LEV_FIELD(test_x, test_y))
12887 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12889 test_element = Feld[test_x][test_y];
12891 if (test_move_dir != bad_move_dir)
12893 /* good thing can be player or penguin that does not move away */
12894 if (IS_PLAYER(test_x, test_y))
12896 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12898 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12899 player as being hit when he is moving towards the bad thing, because
12900 the "get hit by" condition would be lost after the player stops) */
12901 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12902 return; /* player moves away from bad thing */
12907 else if (test_element == EL_PENGUIN)
12914 if (kill_x != -1 || kill_y != -1)
12916 if (IS_PLAYER(kill_x, kill_y))
12918 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12920 if (player->shield_deadly_time_left > 0 &&
12921 !IS_INDESTRUCTIBLE(bad_element))
12922 Bang(bad_x, bad_y);
12923 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12924 KillPlayer(player);
12927 Bang(kill_x, kill_y);
12931 void TestIfPlayerTouchesBadThing(int x, int y)
12933 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12936 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12938 TestIfGoodThingHitsBadThing(x, y, move_dir);
12941 void TestIfBadThingTouchesPlayer(int x, int y)
12943 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12946 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12948 TestIfBadThingHitsGoodThing(x, y, move_dir);
12951 void TestIfFriendTouchesBadThing(int x, int y)
12953 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12956 void TestIfBadThingTouchesFriend(int x, int y)
12958 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12961 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12963 int i, kill_x = bad_x, kill_y = bad_y;
12964 static int xy[4][2] =
12972 for (i = 0; i < NUM_DIRECTIONS; i++)
12976 x = bad_x + xy[i][0];
12977 y = bad_y + xy[i][1];
12978 if (!IN_LEV_FIELD(x, y))
12981 element = Feld[x][y];
12982 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12983 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12991 if (kill_x != bad_x || kill_y != bad_y)
12992 Bang(bad_x, bad_y);
12995 void KillPlayer(struct PlayerInfo *player)
12997 int jx = player->jx, jy = player->jy;
12999 if (!player->active)
13003 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13004 player->killed, player->active, player->reanimated);
13007 /* the following code was introduced to prevent an infinite loop when calling
13009 -> CheckTriggeredElementChangeExt()
13010 -> ExecuteCustomElementAction()
13012 -> (infinitely repeating the above sequence of function calls)
13013 which occurs when killing the player while having a CE with the setting
13014 "kill player X when explosion of <player X>"; the solution using a new
13015 field "player->killed" was chosen for backwards compatibility, although
13016 clever use of the fields "player->active" etc. would probably also work */
13018 if (player->killed)
13022 player->killed = TRUE;
13024 /* remove accessible field at the player's position */
13025 Feld[jx][jy] = EL_EMPTY;
13027 /* deactivate shield (else Bang()/Explode() would not work right) */
13028 player->shield_normal_time_left = 0;
13029 player->shield_deadly_time_left = 0;
13032 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13033 player->killed, player->active, player->reanimated);
13039 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13040 player->killed, player->active, player->reanimated);
13043 if (player->reanimated) /* killed player may have been reanimated */
13044 player->killed = player->reanimated = FALSE;
13046 BuryPlayer(player);
13049 static void KillPlayerUnlessEnemyProtected(int x, int y)
13051 if (!PLAYER_ENEMY_PROTECTED(x, y))
13052 KillPlayer(PLAYERINFO(x, y));
13055 static void KillPlayerUnlessExplosionProtected(int x, int y)
13057 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13058 KillPlayer(PLAYERINFO(x, y));
13061 void BuryPlayer(struct PlayerInfo *player)
13063 int jx = player->jx, jy = player->jy;
13065 if (!player->active)
13068 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13069 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13071 player->GameOver = TRUE;
13072 RemovePlayer(player);
13075 void RemovePlayer(struct PlayerInfo *player)
13077 int jx = player->jx, jy = player->jy;
13078 int i, found = FALSE;
13080 player->present = FALSE;
13081 player->active = FALSE;
13083 if (!ExplodeField[jx][jy])
13084 StorePlayer[jx][jy] = 0;
13086 if (player->is_moving)
13087 TEST_DrawLevelField(player->last_jx, player->last_jy);
13089 for (i = 0; i < MAX_PLAYERS; i++)
13090 if (stored_player[i].active)
13094 AllPlayersGone = TRUE;
13100 static void setFieldForSnapping(int x, int y, int element, int direction)
13102 struct ElementInfo *ei = &element_info[element];
13103 int direction_bit = MV_DIR_TO_BIT(direction);
13104 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13105 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13106 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13108 Feld[x][y] = EL_ELEMENT_SNAPPING;
13109 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13111 ResetGfxAnimation(x, y);
13113 GfxElement[x][y] = element;
13114 GfxAction[x][y] = action;
13115 GfxDir[x][y] = direction;
13116 GfxFrame[x][y] = -1;
13120 =============================================================================
13121 checkDiagonalPushing()
13122 -----------------------------------------------------------------------------
13123 check if diagonal input device direction results in pushing of object
13124 (by checking if the alternative direction is walkable, diggable, ...)
13125 =============================================================================
13128 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13129 int x, int y, int real_dx, int real_dy)
13131 int jx, jy, dx, dy, xx, yy;
13133 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13136 /* diagonal direction: check alternative direction */
13141 xx = jx + (dx == 0 ? real_dx : 0);
13142 yy = jy + (dy == 0 ? real_dy : 0);
13144 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13148 =============================================================================
13150 -----------------------------------------------------------------------------
13151 x, y: field next to player (non-diagonal) to try to dig to
13152 real_dx, real_dy: direction as read from input device (can be diagonal)
13153 =============================================================================
13156 static int DigField(struct PlayerInfo *player,
13157 int oldx, int oldy, int x, int y,
13158 int real_dx, int real_dy, int mode)
13160 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13161 boolean player_was_pushing = player->is_pushing;
13162 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13163 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13164 int jx = oldx, jy = oldy;
13165 int dx = x - jx, dy = y - jy;
13166 int nextx = x + dx, nexty = y + dy;
13167 int move_direction = (dx == -1 ? MV_LEFT :
13168 dx == +1 ? MV_RIGHT :
13170 dy == +1 ? MV_DOWN : MV_NONE);
13171 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13172 int dig_side = MV_DIR_OPPOSITE(move_direction);
13173 int old_element = Feld[jx][jy];
13174 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13177 if (is_player) /* function can also be called by EL_PENGUIN */
13179 if (player->MovPos == 0)
13181 player->is_digging = FALSE;
13182 player->is_collecting = FALSE;
13185 if (player->MovPos == 0) /* last pushing move finished */
13186 player->is_pushing = FALSE;
13188 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13190 player->is_switching = FALSE;
13191 player->push_delay = -1;
13193 return MP_NO_ACTION;
13197 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13198 old_element = Back[jx][jy];
13200 /* in case of element dropped at player position, check background */
13201 else if (Back[jx][jy] != EL_EMPTY &&
13202 game.engine_version >= VERSION_IDENT(2,2,0,0))
13203 old_element = Back[jx][jy];
13205 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13206 return MP_NO_ACTION; /* field has no opening in this direction */
13208 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13209 return MP_NO_ACTION; /* field has no opening in this direction */
13211 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13215 Feld[jx][jy] = player->artwork_element;
13216 InitMovingField(jx, jy, MV_DOWN);
13217 Store[jx][jy] = EL_ACID;
13218 ContinueMoving(jx, jy);
13219 BuryPlayer(player);
13221 return MP_DONT_RUN_INTO;
13224 if (player_can_move && DONT_RUN_INTO(element))
13226 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13228 return MP_DONT_RUN_INTO;
13231 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13232 return MP_NO_ACTION;
13234 collect_count = element_info[element].collect_count_initial;
13236 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13237 return MP_NO_ACTION;
13239 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13240 player_can_move = player_can_move_or_snap;
13242 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13243 game.engine_version >= VERSION_IDENT(2,2,0,0))
13245 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13246 player->index_bit, dig_side);
13247 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13248 player->index_bit, dig_side);
13250 if (element == EL_DC_LANDMINE)
13253 if (Feld[x][y] != element) /* field changed by snapping */
13256 return MP_NO_ACTION;
13259 if (player->gravity && is_player && !player->is_auto_moving &&
13260 canFallDown(player) && move_direction != MV_DOWN &&
13261 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13262 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13264 if (player_can_move &&
13265 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13267 int sound_element = SND_ELEMENT(element);
13268 int sound_action = ACTION_WALKING;
13270 if (IS_RND_GATE(element))
13272 if (!player->key[RND_GATE_NR(element)])
13273 return MP_NO_ACTION;
13275 else if (IS_RND_GATE_GRAY(element))
13277 if (!player->key[RND_GATE_GRAY_NR(element)])
13278 return MP_NO_ACTION;
13280 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13282 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13283 return MP_NO_ACTION;
13285 else if (element == EL_EXIT_OPEN ||
13286 element == EL_EM_EXIT_OPEN ||
13287 element == EL_EM_EXIT_OPENING ||
13288 element == EL_STEEL_EXIT_OPEN ||
13289 element == EL_EM_STEEL_EXIT_OPEN ||
13290 element == EL_EM_STEEL_EXIT_OPENING ||
13291 element == EL_SP_EXIT_OPEN ||
13292 element == EL_SP_EXIT_OPENING)
13294 sound_action = ACTION_PASSING; /* player is passing exit */
13296 else if (element == EL_EMPTY)
13298 sound_action = ACTION_MOVING; /* nothing to walk on */
13301 /* play sound from background or player, whatever is available */
13302 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13303 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13305 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13307 else if (player_can_move &&
13308 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13310 if (!ACCESS_FROM(element, opposite_direction))
13311 return MP_NO_ACTION; /* field not accessible from this direction */
13313 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13314 return MP_NO_ACTION;
13316 if (IS_EM_GATE(element))
13318 if (!player->key[EM_GATE_NR(element)])
13319 return MP_NO_ACTION;
13321 else if (IS_EM_GATE_GRAY(element))
13323 if (!player->key[EM_GATE_GRAY_NR(element)])
13324 return MP_NO_ACTION;
13326 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13328 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13329 return MP_NO_ACTION;
13331 else if (IS_EMC_GATE(element))
13333 if (!player->key[EMC_GATE_NR(element)])
13334 return MP_NO_ACTION;
13336 else if (IS_EMC_GATE_GRAY(element))
13338 if (!player->key[EMC_GATE_GRAY_NR(element)])
13339 return MP_NO_ACTION;
13341 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13343 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13344 return MP_NO_ACTION;
13346 else if (element == EL_DC_GATE_WHITE ||
13347 element == EL_DC_GATE_WHITE_GRAY ||
13348 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13350 if (player->num_white_keys == 0)
13351 return MP_NO_ACTION;
13353 player->num_white_keys--;
13355 else if (IS_SP_PORT(element))
13357 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13358 element == EL_SP_GRAVITY_PORT_RIGHT ||
13359 element == EL_SP_GRAVITY_PORT_UP ||
13360 element == EL_SP_GRAVITY_PORT_DOWN)
13361 player->gravity = !player->gravity;
13362 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13363 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13364 element == EL_SP_GRAVITY_ON_PORT_UP ||
13365 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13366 player->gravity = TRUE;
13367 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13368 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13369 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13370 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13371 player->gravity = FALSE;
13374 /* automatically move to the next field with double speed */
13375 player->programmed_action = move_direction;
13377 if (player->move_delay_reset_counter == 0)
13379 player->move_delay_reset_counter = 2; /* two double speed steps */
13381 DOUBLE_PLAYER_SPEED(player);
13384 PlayLevelSoundAction(x, y, ACTION_PASSING);
13386 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13390 if (mode != DF_SNAP)
13392 GfxElement[x][y] = GFX_ELEMENT(element);
13393 player->is_digging = TRUE;
13396 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13398 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13399 player->index_bit, dig_side);
13401 if (mode == DF_SNAP)
13403 if (level.block_snap_field)
13404 setFieldForSnapping(x, y, element, move_direction);
13406 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13408 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13409 player->index_bit, dig_side);
13412 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13416 if (is_player && mode != DF_SNAP)
13418 GfxElement[x][y] = element;
13419 player->is_collecting = TRUE;
13422 if (element == EL_SPEED_PILL)
13424 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13426 else if (element == EL_EXTRA_TIME && level.time > 0)
13428 TimeLeft += level.extra_time;
13430 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13432 DisplayGameControlValues();
13434 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13436 player->shield_normal_time_left += level.shield_normal_time;
13437 if (element == EL_SHIELD_DEADLY)
13438 player->shield_deadly_time_left += level.shield_deadly_time;
13440 else if (element == EL_DYNAMITE ||
13441 element == EL_EM_DYNAMITE ||
13442 element == EL_SP_DISK_RED)
13444 if (player->inventory_size < MAX_INVENTORY_SIZE)
13445 player->inventory_element[player->inventory_size++] = element;
13447 DrawGameDoorValues();
13449 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13451 player->dynabomb_count++;
13452 player->dynabombs_left++;
13454 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13456 player->dynabomb_size++;
13458 else if (element == EL_DYNABOMB_INCREASE_POWER)
13460 player->dynabomb_xl = TRUE;
13462 else if (IS_KEY(element))
13464 player->key[KEY_NR(element)] = TRUE;
13466 DrawGameDoorValues();
13468 else if (element == EL_DC_KEY_WHITE)
13470 player->num_white_keys++;
13472 /* display white keys? */
13473 /* DrawGameDoorValues(); */
13475 else if (IS_ENVELOPE(element))
13477 player->show_envelope = element;
13479 else if (element == EL_EMC_LENSES)
13481 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13483 RedrawAllInvisibleElementsForLenses();
13485 else if (element == EL_EMC_MAGNIFIER)
13487 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13489 RedrawAllInvisibleElementsForMagnifier();
13491 else if (IS_DROPPABLE(element) ||
13492 IS_THROWABLE(element)) /* can be collected and dropped */
13496 if (collect_count == 0)
13497 player->inventory_infinite_element = element;
13499 for (i = 0; i < collect_count; i++)
13500 if (player->inventory_size < MAX_INVENTORY_SIZE)
13501 player->inventory_element[player->inventory_size++] = element;
13503 DrawGameDoorValues();
13505 else if (collect_count > 0)
13507 local_player->gems_still_needed -= collect_count;
13508 if (local_player->gems_still_needed < 0)
13509 local_player->gems_still_needed = 0;
13511 game.snapshot.collected_item = TRUE;
13513 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13515 DisplayGameControlValues();
13518 RaiseScoreElement(element);
13519 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13522 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13523 player->index_bit, dig_side);
13525 if (mode == DF_SNAP)
13527 if (level.block_snap_field)
13528 setFieldForSnapping(x, y, element, move_direction);
13530 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13532 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13533 player->index_bit, dig_side);
13536 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13538 if (mode == DF_SNAP && element != EL_BD_ROCK)
13539 return MP_NO_ACTION;
13541 if (CAN_FALL(element) && dy)
13542 return MP_NO_ACTION;
13544 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13545 !(element == EL_SPRING && level.use_spring_bug))
13546 return MP_NO_ACTION;
13548 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13549 ((move_direction & MV_VERTICAL &&
13550 ((element_info[element].move_pattern & MV_LEFT &&
13551 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13552 (element_info[element].move_pattern & MV_RIGHT &&
13553 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13554 (move_direction & MV_HORIZONTAL &&
13555 ((element_info[element].move_pattern & MV_UP &&
13556 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13557 (element_info[element].move_pattern & MV_DOWN &&
13558 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13559 return MP_NO_ACTION;
13561 /* do not push elements already moving away faster than player */
13562 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13563 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13564 return MP_NO_ACTION;
13566 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13568 if (player->push_delay_value == -1 || !player_was_pushing)
13569 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13571 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13573 if (player->push_delay_value == -1)
13574 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13576 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13578 if (!player->is_pushing)
13579 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13582 player->is_pushing = TRUE;
13583 player->is_active = TRUE;
13585 if (!(IN_LEV_FIELD(nextx, nexty) &&
13586 (IS_FREE(nextx, nexty) ||
13587 (IS_SB_ELEMENT(element) &&
13588 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13589 (IS_CUSTOM_ELEMENT(element) &&
13590 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13591 return MP_NO_ACTION;
13593 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13594 return MP_NO_ACTION;
13596 if (player->push_delay == -1) /* new pushing; restart delay */
13597 player->push_delay = 0;
13599 if (player->push_delay < player->push_delay_value &&
13600 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13601 element != EL_SPRING && element != EL_BALLOON)
13603 /* make sure that there is no move delay before next try to push */
13604 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13605 player->move_delay = 0;
13607 return MP_NO_ACTION;
13610 if (IS_CUSTOM_ELEMENT(element) &&
13611 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13613 if (!DigFieldByCE(nextx, nexty, element))
13614 return MP_NO_ACTION;
13617 if (IS_SB_ELEMENT(element))
13619 if (element == EL_SOKOBAN_FIELD_FULL)
13621 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13622 local_player->sokobanfields_still_needed++;
13625 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13627 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13628 local_player->sokobanfields_still_needed--;
13631 Feld[x][y] = EL_SOKOBAN_OBJECT;
13633 if (Back[x][y] == Back[nextx][nexty])
13634 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13635 else if (Back[x][y] != 0)
13636 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13639 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13642 if (local_player->sokobanfields_still_needed == 0 &&
13643 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13645 PlayerWins(player);
13647 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13651 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13653 InitMovingField(x, y, move_direction);
13654 GfxAction[x][y] = ACTION_PUSHING;
13656 if (mode == DF_SNAP)
13657 ContinueMoving(x, y);
13659 MovPos[x][y] = (dx != 0 ? dx : dy);
13661 Pushed[x][y] = TRUE;
13662 Pushed[nextx][nexty] = TRUE;
13664 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13665 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13667 player->push_delay_value = -1; /* get new value later */
13669 /* check for element change _after_ element has been pushed */
13670 if (game.use_change_when_pushing_bug)
13672 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13673 player->index_bit, dig_side);
13674 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13675 player->index_bit, dig_side);
13678 else if (IS_SWITCHABLE(element))
13680 if (PLAYER_SWITCHING(player, x, y))
13682 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13683 player->index_bit, dig_side);
13688 player->is_switching = TRUE;
13689 player->switch_x = x;
13690 player->switch_y = y;
13692 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13694 if (element == EL_ROBOT_WHEEL)
13696 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13700 game.robot_wheel_active = TRUE;
13702 TEST_DrawLevelField(x, y);
13704 else if (element == EL_SP_TERMINAL)
13708 SCAN_PLAYFIELD(xx, yy)
13710 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13714 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13716 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13718 ResetGfxAnimation(xx, yy);
13719 TEST_DrawLevelField(xx, yy);
13723 else if (IS_BELT_SWITCH(element))
13725 ToggleBeltSwitch(x, y);
13727 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13728 element == EL_SWITCHGATE_SWITCH_DOWN ||
13729 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13730 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13732 ToggleSwitchgateSwitch(x, y);
13734 else if (element == EL_LIGHT_SWITCH ||
13735 element == EL_LIGHT_SWITCH_ACTIVE)
13737 ToggleLightSwitch(x, y);
13739 else if (element == EL_TIMEGATE_SWITCH ||
13740 element == EL_DC_TIMEGATE_SWITCH)
13742 ActivateTimegateSwitch(x, y);
13744 else if (element == EL_BALLOON_SWITCH_LEFT ||
13745 element == EL_BALLOON_SWITCH_RIGHT ||
13746 element == EL_BALLOON_SWITCH_UP ||
13747 element == EL_BALLOON_SWITCH_DOWN ||
13748 element == EL_BALLOON_SWITCH_NONE ||
13749 element == EL_BALLOON_SWITCH_ANY)
13751 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13752 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13753 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13754 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13755 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13758 else if (element == EL_LAMP)
13760 Feld[x][y] = EL_LAMP_ACTIVE;
13761 local_player->lights_still_needed--;
13763 ResetGfxAnimation(x, y);
13764 TEST_DrawLevelField(x, y);
13766 else if (element == EL_TIME_ORB_FULL)
13768 Feld[x][y] = EL_TIME_ORB_EMPTY;
13770 if (level.time > 0 || level.use_time_orb_bug)
13772 TimeLeft += level.time_orb_time;
13773 game.no_time_limit = FALSE;
13775 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13777 DisplayGameControlValues();
13780 ResetGfxAnimation(x, y);
13781 TEST_DrawLevelField(x, y);
13783 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13784 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13788 game.ball_state = !game.ball_state;
13790 SCAN_PLAYFIELD(xx, yy)
13792 int e = Feld[xx][yy];
13794 if (game.ball_state)
13796 if (e == EL_EMC_MAGIC_BALL)
13797 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13798 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13799 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13803 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13804 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13805 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13806 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13811 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13812 player->index_bit, dig_side);
13814 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13815 player->index_bit, dig_side);
13817 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13818 player->index_bit, dig_side);
13824 if (!PLAYER_SWITCHING(player, x, y))
13826 player->is_switching = TRUE;
13827 player->switch_x = x;
13828 player->switch_y = y;
13830 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13831 player->index_bit, dig_side);
13832 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13833 player->index_bit, dig_side);
13835 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13836 player->index_bit, dig_side);
13837 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13838 player->index_bit, dig_side);
13841 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13842 player->index_bit, dig_side);
13843 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13844 player->index_bit, dig_side);
13846 return MP_NO_ACTION;
13849 player->push_delay = -1;
13851 if (is_player) /* function can also be called by EL_PENGUIN */
13853 if (Feld[x][y] != element) /* really digged/collected something */
13855 player->is_collecting = !player->is_digging;
13856 player->is_active = TRUE;
13863 static boolean DigFieldByCE(int x, int y, int digging_element)
13865 int element = Feld[x][y];
13867 if (!IS_FREE(x, y))
13869 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13870 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13873 /* no element can dig solid indestructible elements */
13874 if (IS_INDESTRUCTIBLE(element) &&
13875 !IS_DIGGABLE(element) &&
13876 !IS_COLLECTIBLE(element))
13879 if (AmoebaNr[x][y] &&
13880 (element == EL_AMOEBA_FULL ||
13881 element == EL_BD_AMOEBA ||
13882 element == EL_AMOEBA_GROWING))
13884 AmoebaCnt[AmoebaNr[x][y]]--;
13885 AmoebaCnt2[AmoebaNr[x][y]]--;
13888 if (IS_MOVING(x, y))
13889 RemoveMovingField(x, y);
13893 TEST_DrawLevelField(x, y);
13896 /* if digged element was about to explode, prevent the explosion */
13897 ExplodeField[x][y] = EX_TYPE_NONE;
13899 PlayLevelSoundAction(x, y, action);
13902 Store[x][y] = EL_EMPTY;
13904 /* this makes it possible to leave the removed element again */
13905 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13906 Store[x][y] = element;
13911 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13913 int jx = player->jx, jy = player->jy;
13914 int x = jx + dx, y = jy + dy;
13915 int snap_direction = (dx == -1 ? MV_LEFT :
13916 dx == +1 ? MV_RIGHT :
13918 dy == +1 ? MV_DOWN : MV_NONE);
13919 boolean can_continue_snapping = (level.continuous_snapping &&
13920 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13922 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13925 if (!player->active || !IN_LEV_FIELD(x, y))
13933 if (player->MovPos == 0)
13934 player->is_pushing = FALSE;
13936 player->is_snapping = FALSE;
13938 if (player->MovPos == 0)
13940 player->is_moving = FALSE;
13941 player->is_digging = FALSE;
13942 player->is_collecting = FALSE;
13948 /* prevent snapping with already pressed snap key when not allowed */
13949 if (player->is_snapping && !can_continue_snapping)
13952 player->MovDir = snap_direction;
13954 if (player->MovPos == 0)
13956 player->is_moving = FALSE;
13957 player->is_digging = FALSE;
13958 player->is_collecting = FALSE;
13961 player->is_dropping = FALSE;
13962 player->is_dropping_pressed = FALSE;
13963 player->drop_pressed_delay = 0;
13965 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13968 player->is_snapping = TRUE;
13969 player->is_active = TRUE;
13971 if (player->MovPos == 0)
13973 player->is_moving = FALSE;
13974 player->is_digging = FALSE;
13975 player->is_collecting = FALSE;
13978 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13979 TEST_DrawLevelField(player->last_jx, player->last_jy);
13981 TEST_DrawLevelField(x, y);
13986 static boolean DropElement(struct PlayerInfo *player)
13988 int old_element, new_element;
13989 int dropx = player->jx, dropy = player->jy;
13990 int drop_direction = player->MovDir;
13991 int drop_side = drop_direction;
13992 int drop_element = get_next_dropped_element(player);
13994 /* do not drop an element on top of another element; when holding drop key
13995 pressed without moving, dropped element must move away before the next
13996 element can be dropped (this is especially important if the next element
13997 is dynamite, which can be placed on background for historical reasons) */
13998 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14001 if (IS_THROWABLE(drop_element))
14003 dropx += GET_DX_FROM_DIR(drop_direction);
14004 dropy += GET_DY_FROM_DIR(drop_direction);
14006 if (!IN_LEV_FIELD(dropx, dropy))
14010 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14011 new_element = drop_element; /* default: no change when dropping */
14013 /* check if player is active, not moving and ready to drop */
14014 if (!player->active || player->MovPos || player->drop_delay > 0)
14017 /* check if player has anything that can be dropped */
14018 if (new_element == EL_UNDEFINED)
14021 /* only set if player has anything that can be dropped */
14022 player->is_dropping_pressed = TRUE;
14024 /* check if drop key was pressed long enough for EM style dynamite */
14025 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14028 /* check if anything can be dropped at the current position */
14029 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14032 /* collected custom elements can only be dropped on empty fields */
14033 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14036 if (old_element != EL_EMPTY)
14037 Back[dropx][dropy] = old_element; /* store old element on this field */
14039 ResetGfxAnimation(dropx, dropy);
14040 ResetRandomAnimationValue(dropx, dropy);
14042 if (player->inventory_size > 0 ||
14043 player->inventory_infinite_element != EL_UNDEFINED)
14045 if (player->inventory_size > 0)
14047 player->inventory_size--;
14049 DrawGameDoorValues();
14051 if (new_element == EL_DYNAMITE)
14052 new_element = EL_DYNAMITE_ACTIVE;
14053 else if (new_element == EL_EM_DYNAMITE)
14054 new_element = EL_EM_DYNAMITE_ACTIVE;
14055 else if (new_element == EL_SP_DISK_RED)
14056 new_element = EL_SP_DISK_RED_ACTIVE;
14059 Feld[dropx][dropy] = new_element;
14061 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14062 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14063 el2img(Feld[dropx][dropy]), 0);
14065 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14067 /* needed if previous element just changed to "empty" in the last frame */
14068 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14070 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14071 player->index_bit, drop_side);
14072 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14074 player->index_bit, drop_side);
14076 TestIfElementTouchesCustomElement(dropx, dropy);
14078 else /* player is dropping a dyna bomb */
14080 player->dynabombs_left--;
14082 Feld[dropx][dropy] = new_element;
14084 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14085 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14086 el2img(Feld[dropx][dropy]), 0);
14088 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14091 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14092 InitField_WithBug1(dropx, dropy, FALSE);
14094 new_element = Feld[dropx][dropy]; /* element might have changed */
14096 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14097 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14099 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14100 MovDir[dropx][dropy] = drop_direction;
14102 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14104 /* do not cause impact style collision by dropping elements that can fall */
14105 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14108 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14109 player->is_dropping = TRUE;
14111 player->drop_pressed_delay = 0;
14112 player->is_dropping_pressed = FALSE;
14114 player->drop_x = dropx;
14115 player->drop_y = dropy;
14120 /* ------------------------------------------------------------------------- */
14121 /* game sound playing functions */
14122 /* ------------------------------------------------------------------------- */
14124 static int *loop_sound_frame = NULL;
14125 static int *loop_sound_volume = NULL;
14127 void InitPlayLevelSound()
14129 int num_sounds = getSoundListSize();
14131 checked_free(loop_sound_frame);
14132 checked_free(loop_sound_volume);
14134 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14135 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14138 static void PlayLevelSound(int x, int y, int nr)
14140 int sx = SCREENX(x), sy = SCREENY(y);
14141 int volume, stereo_position;
14142 int max_distance = 8;
14143 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14145 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14146 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14149 if (!IN_LEV_FIELD(x, y) ||
14150 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14151 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14154 volume = SOUND_MAX_VOLUME;
14156 if (!IN_SCR_FIELD(sx, sy))
14158 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14159 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14161 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14164 stereo_position = (SOUND_MAX_LEFT +
14165 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14166 (SCR_FIELDX + 2 * max_distance));
14168 if (IS_LOOP_SOUND(nr))
14170 /* This assures that quieter loop sounds do not overwrite louder ones,
14171 while restarting sound volume comparison with each new game frame. */
14173 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14176 loop_sound_volume[nr] = volume;
14177 loop_sound_frame[nr] = FrameCounter;
14180 PlaySoundExt(nr, volume, stereo_position, type);
14183 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14185 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14186 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14187 y < LEVELY(BY1) ? LEVELY(BY1) :
14188 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14192 static void PlayLevelSoundAction(int x, int y, int action)
14194 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14197 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14199 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14201 if (sound_effect != SND_UNDEFINED)
14202 PlayLevelSound(x, y, sound_effect);
14205 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14208 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14210 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14211 PlayLevelSound(x, y, sound_effect);
14214 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14216 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14218 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14219 PlayLevelSound(x, y, sound_effect);
14222 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14224 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14226 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14227 StopSound(sound_effect);
14230 static void PlayLevelMusic()
14232 if (levelset.music[level_nr] != MUS_UNDEFINED)
14233 PlayMusic(levelset.music[level_nr]); /* from config file */
14235 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14238 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14240 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14241 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14242 int x = xx - 1 - offset;
14243 int y = yy - 1 - offset;
14248 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14252 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14256 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14260 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14264 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14268 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14272 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14275 case SAMPLE_android_clone:
14276 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14279 case SAMPLE_android_move:
14280 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14283 case SAMPLE_spring:
14284 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14288 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14292 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14295 case SAMPLE_eater_eat:
14296 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14300 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14303 case SAMPLE_collect:
14304 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14307 case SAMPLE_diamond:
14308 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14311 case SAMPLE_squash:
14312 /* !!! CHECK THIS !!! */
14314 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14316 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14320 case SAMPLE_wonderfall:
14321 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14325 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14329 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14333 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14337 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14341 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14345 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14348 case SAMPLE_wonder:
14349 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14353 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14356 case SAMPLE_exit_open:
14357 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14360 case SAMPLE_exit_leave:
14361 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14364 case SAMPLE_dynamite:
14365 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14369 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14373 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14377 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14381 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14385 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14389 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14393 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14398 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14400 int element = map_element_SP_to_RND(element_sp);
14401 int action = map_action_SP_to_RND(action_sp);
14402 int offset = (setup.sp_show_border_elements ? 0 : 1);
14403 int x = xx - offset;
14404 int y = yy - offset;
14406 PlayLevelSoundElementAction(x, y, element, action);
14409 void RaiseScore(int value)
14411 local_player->score += value;
14413 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14415 DisplayGameControlValues();
14418 void RaiseScoreElement(int element)
14423 case EL_BD_DIAMOND:
14424 case EL_EMERALD_YELLOW:
14425 case EL_EMERALD_RED:
14426 case EL_EMERALD_PURPLE:
14427 case EL_SP_INFOTRON:
14428 RaiseScore(level.score[SC_EMERALD]);
14431 RaiseScore(level.score[SC_DIAMOND]);
14434 RaiseScore(level.score[SC_CRYSTAL]);
14437 RaiseScore(level.score[SC_PEARL]);
14440 case EL_BD_BUTTERFLY:
14441 case EL_SP_ELECTRON:
14442 RaiseScore(level.score[SC_BUG]);
14445 case EL_BD_FIREFLY:
14446 case EL_SP_SNIKSNAK:
14447 RaiseScore(level.score[SC_SPACESHIP]);
14450 case EL_DARK_YAMYAM:
14451 RaiseScore(level.score[SC_YAMYAM]);
14454 RaiseScore(level.score[SC_ROBOT]);
14457 RaiseScore(level.score[SC_PACMAN]);
14460 RaiseScore(level.score[SC_NUT]);
14463 case EL_EM_DYNAMITE:
14464 case EL_SP_DISK_RED:
14465 case EL_DYNABOMB_INCREASE_NUMBER:
14466 case EL_DYNABOMB_INCREASE_SIZE:
14467 case EL_DYNABOMB_INCREASE_POWER:
14468 RaiseScore(level.score[SC_DYNAMITE]);
14470 case EL_SHIELD_NORMAL:
14471 case EL_SHIELD_DEADLY:
14472 RaiseScore(level.score[SC_SHIELD]);
14474 case EL_EXTRA_TIME:
14475 RaiseScore(level.extra_time_score);
14489 case EL_DC_KEY_WHITE:
14490 RaiseScore(level.score[SC_KEY]);
14493 RaiseScore(element_info[element].collect_score);
14498 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14500 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14502 /* closing door required in case of envelope style request dialogs */
14504 CloseDoor(DOOR_CLOSE_1);
14506 #if defined(NETWORK_AVALIABLE)
14507 if (options.network)
14508 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14513 FadeSkipNextFadeIn();
14515 SetGameStatus(GAME_MODE_MAIN);
14520 else /* continue playing the game */
14522 if (tape.playing && tape.deactivate_display)
14523 TapeDeactivateDisplayOff(TRUE);
14525 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14527 if (tape.playing && tape.deactivate_display)
14528 TapeDeactivateDisplayOn();
14532 void RequestQuitGame(boolean ask_if_really_quit)
14534 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14535 boolean skip_request = AllPlayersGone || quick_quit;
14537 RequestQuitGameExt(skip_request, quick_quit,
14538 "Do you really want to quit the game?");
14542 /* ------------------------------------------------------------------------- */
14543 /* random generator functions */
14544 /* ------------------------------------------------------------------------- */
14546 unsigned int InitEngineRandom_RND(int seed)
14548 game.num_random_calls = 0;
14550 return InitEngineRandom(seed);
14553 unsigned int RND(int max)
14557 game.num_random_calls++;
14559 return GetEngineRandom(max);
14566 /* ------------------------------------------------------------------------- */
14567 /* game engine snapshot handling functions */
14568 /* ------------------------------------------------------------------------- */
14570 struct EngineSnapshotInfo
14572 /* runtime values for custom element collect score */
14573 int collect_score[NUM_CUSTOM_ELEMENTS];
14575 /* runtime values for group element choice position */
14576 int choice_pos[NUM_GROUP_ELEMENTS];
14578 /* runtime values for belt position animations */
14579 int belt_graphic[4][NUM_BELT_PARTS];
14580 int belt_anim_mode[4][NUM_BELT_PARTS];
14583 static struct EngineSnapshotInfo engine_snapshot_rnd;
14584 static char *snapshot_level_identifier = NULL;
14585 static int snapshot_level_nr = -1;
14587 static void SaveEngineSnapshotValues_RND()
14589 static int belt_base_active_element[4] =
14591 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14592 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14593 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14594 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14598 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14600 int element = EL_CUSTOM_START + i;
14602 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14605 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14607 int element = EL_GROUP_START + i;
14609 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14612 for (i = 0; i < 4; i++)
14614 for (j = 0; j < NUM_BELT_PARTS; j++)
14616 int element = belt_base_active_element[i] + j;
14617 int graphic = el2img(element);
14618 int anim_mode = graphic_info[graphic].anim_mode;
14620 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14621 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14626 static void LoadEngineSnapshotValues_RND()
14628 unsigned int num_random_calls = game.num_random_calls;
14631 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14633 int element = EL_CUSTOM_START + i;
14635 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14638 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14640 int element = EL_GROUP_START + i;
14642 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14645 for (i = 0; i < 4; i++)
14647 for (j = 0; j < NUM_BELT_PARTS; j++)
14649 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14650 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14652 graphic_info[graphic].anim_mode = anim_mode;
14656 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14658 InitRND(tape.random_seed);
14659 for (i = 0; i < num_random_calls; i++)
14663 if (game.num_random_calls != num_random_calls)
14665 Error(ERR_INFO, "number of random calls out of sync");
14666 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14667 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14668 Error(ERR_EXIT, "this should not happen -- please debug");
14672 void FreeEngineSnapshotSingle()
14674 FreeSnapshotSingle();
14676 setString(&snapshot_level_identifier, NULL);
14677 snapshot_level_nr = -1;
14680 void FreeEngineSnapshotList()
14682 FreeSnapshotList();
14685 ListNode *SaveEngineSnapshotBuffers()
14687 ListNode *buffers = NULL;
14689 /* copy some special values to a structure better suited for the snapshot */
14691 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14692 SaveEngineSnapshotValues_RND();
14693 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14694 SaveEngineSnapshotValues_EM();
14695 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14696 SaveEngineSnapshotValues_SP(&buffers);
14698 /* save values stored in special snapshot structure */
14700 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14701 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14702 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14703 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14704 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14705 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14707 /* save further RND engine values */
14709 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14710 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14711 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14713 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14714 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14715 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14716 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14718 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14719 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14720 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14721 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14722 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14724 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14725 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14726 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14728 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14730 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14732 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14733 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14735 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14736 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14737 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14738 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14739 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14740 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14741 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14742 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14743 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14744 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14745 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14746 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14747 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14748 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14749 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14750 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14751 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14752 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14754 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14755 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14757 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14758 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14759 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14761 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14762 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14764 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14765 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14766 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14767 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14768 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14770 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14771 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14774 ListNode *node = engine_snapshot_list_rnd;
14777 while (node != NULL)
14779 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14784 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14790 void SaveEngineSnapshotSingle()
14792 ListNode *buffers = SaveEngineSnapshotBuffers();
14794 /* finally save all snapshot buffers to single snapshot */
14795 SaveSnapshotSingle(buffers);
14797 /* save level identification information */
14798 setString(&snapshot_level_identifier, leveldir_current->identifier);
14799 snapshot_level_nr = level_nr;
14802 boolean CheckSaveEngineSnapshotToList()
14804 boolean save_snapshot =
14805 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14806 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14807 game.snapshot.changed_action) ||
14808 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14809 game.snapshot.collected_item));
14811 game.snapshot.changed_action = FALSE;
14812 game.snapshot.collected_item = FALSE;
14813 game.snapshot.save_snapshot = save_snapshot;
14815 return save_snapshot;
14818 void SaveEngineSnapshotToList()
14820 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14824 ListNode *buffers = SaveEngineSnapshotBuffers();
14826 /* finally save all snapshot buffers to snapshot list */
14827 SaveSnapshotToList(buffers);
14830 void SaveEngineSnapshotToListInitial()
14832 FreeEngineSnapshotList();
14834 SaveEngineSnapshotToList();
14837 void LoadEngineSnapshotValues()
14839 /* restore special values from snapshot structure */
14841 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14842 LoadEngineSnapshotValues_RND();
14843 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14844 LoadEngineSnapshotValues_EM();
14845 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14846 LoadEngineSnapshotValues_SP();
14849 void LoadEngineSnapshotSingle()
14851 LoadSnapshotSingle();
14853 LoadEngineSnapshotValues();
14856 void LoadEngineSnapshot_Undo(int steps)
14858 LoadSnapshotFromList_Older(steps);
14860 LoadEngineSnapshotValues();
14863 void LoadEngineSnapshot_Redo(int steps)
14865 LoadSnapshotFromList_Newer(steps);
14867 LoadEngineSnapshotValues();
14870 boolean CheckEngineSnapshotSingle()
14872 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14873 snapshot_level_nr == level_nr);
14876 boolean CheckEngineSnapshotList()
14878 return CheckSnapshotList();
14882 /* ---------- new game button stuff ---------------------------------------- */
14890 } gamebutton_info[NUM_GAME_BUTTONS] =
14893 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
14894 GAME_CTRL_ID_STOP, "stop game"
14897 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
14898 GAME_CTRL_ID_PAUSE, "pause game"
14901 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
14902 GAME_CTRL_ID_PLAY, "play game"
14905 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
14906 GAME_CTRL_ID_UNDO, "undo step"
14909 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
14910 GAME_CTRL_ID_REDO, "redo step"
14913 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
14914 GAME_CTRL_ID_SAVE, "save game"
14917 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
14918 GAME_CTRL_ID_PAUSE2, "pause game"
14921 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
14922 GAME_CTRL_ID_LOAD, "load game"
14925 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
14926 SOUND_CTRL_ID_MUSIC, "background music on/off"
14929 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
14930 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14933 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
14934 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14938 void CreateGameButtons()
14942 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14944 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14945 struct XY *pos = gamebutton_info[i].pos;
14946 struct GadgetInfo *gi;
14949 unsigned int event_mask;
14950 int base_x = (tape.show_game_buttons ? VX : DX);
14951 int base_y = (tape.show_game_buttons ? VY : DY);
14952 int gd_x = gfx->src_x;
14953 int gd_y = gfx->src_y;
14954 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14955 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14956 int gd_xa = gfx->src_x + gfx->active_xoffset;
14957 int gd_ya = gfx->src_y + gfx->active_yoffset;
14958 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14959 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14962 if (gfx->bitmap == NULL)
14964 game_gadget[id] = NULL;
14969 if (id == GAME_CTRL_ID_STOP ||
14970 id == GAME_CTRL_ID_PLAY ||
14971 id == GAME_CTRL_ID_SAVE ||
14972 id == GAME_CTRL_ID_LOAD)
14974 button_type = GD_TYPE_NORMAL_BUTTON;
14976 event_mask = GD_EVENT_RELEASED;
14978 else if (id == GAME_CTRL_ID_UNDO ||
14979 id == GAME_CTRL_ID_REDO)
14981 button_type = GD_TYPE_NORMAL_BUTTON;
14983 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14987 button_type = GD_TYPE_CHECK_BUTTON;
14989 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14990 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14991 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14992 event_mask = GD_EVENT_PRESSED;
14995 gi = CreateGadget(GDI_CUSTOM_ID, id,
14996 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14997 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14998 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14999 GDI_WIDTH, gfx->width,
15000 GDI_HEIGHT, gfx->height,
15001 GDI_TYPE, button_type,
15002 GDI_STATE, GD_BUTTON_UNPRESSED,
15003 GDI_CHECKED, checked,
15004 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15005 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15006 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15007 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15008 GDI_DIRECT_DRAW, FALSE,
15009 GDI_EVENT_MASK, event_mask,
15010 GDI_CALLBACK_ACTION, HandleGameButtons,
15014 Error(ERR_EXIT, "cannot create gadget");
15016 game_gadget[id] = gi;
15020 void FreeGameButtons()
15024 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15025 FreeGadget(game_gadget[i]);
15028 static void UnmapGameButtonsAtSamePosition(int id)
15032 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15034 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15035 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15036 UnmapGadget(game_gadget[i]);
15039 static void UnmapGameButtonsAtSamePosition_All()
15041 if (setup.show_snapshot_buttons)
15043 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15044 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15045 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15049 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15050 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15051 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15055 static void MapGameButtonsAtSamePosition(int id)
15059 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15061 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15062 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15063 MapGadget(game_gadget[i]);
15065 UnmapGameButtonsAtSamePosition_All();
15068 void MapUndoRedoButtons()
15070 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15071 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15073 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15074 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15076 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15079 void UnmapUndoRedoButtons()
15081 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15082 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15084 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15085 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15087 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15090 void MapGameButtons()
15094 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15095 if (i != GAME_CTRL_ID_UNDO &&
15096 i != GAME_CTRL_ID_REDO)
15097 MapGadget(game_gadget[i]);
15099 UnmapGameButtonsAtSamePosition_All();
15101 RedrawGameButtons();
15104 void UnmapGameButtons()
15108 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15109 UnmapGadget(game_gadget[i]);
15112 void RedrawGameButtons()
15116 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15117 RedrawGadget(game_gadget[i]);
15119 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15120 redraw_mask &= ~REDRAW_ALL;
15123 void GameUndoRedoExt()
15125 ClearPlayerAction();
15127 tape.pausing = TRUE;
15130 UpdateAndDisplayGameControlValues();
15132 DrawCompleteVideoDisplay();
15133 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15134 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15135 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15140 void GameUndo(int steps)
15142 if (!CheckEngineSnapshotList())
15145 LoadEngineSnapshot_Undo(steps);
15150 void GameRedo(int steps)
15152 if (!CheckEngineSnapshotList())
15155 LoadEngineSnapshot_Redo(steps);
15160 static void HandleGameButtonsExt(int id, int button)
15162 static boolean game_undo_executed = FALSE;
15163 int steps = BUTTON_STEPSIZE(button);
15164 boolean handle_game_buttons =
15165 (game_status == GAME_MODE_PLAYING ||
15166 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15168 if (!handle_game_buttons)
15173 case GAME_CTRL_ID_STOP:
15174 if (game_status == GAME_MODE_MAIN)
15180 RequestQuitGame(TRUE);
15184 case GAME_CTRL_ID_PAUSE:
15185 case GAME_CTRL_ID_PAUSE2:
15186 if (options.network && game_status == GAME_MODE_PLAYING)
15188 #if defined(NETWORK_AVALIABLE)
15190 SendToServer_ContinuePlaying();
15192 SendToServer_PausePlaying();
15196 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15198 game_undo_executed = FALSE;
15202 case GAME_CTRL_ID_PLAY:
15203 if (game_status == GAME_MODE_MAIN)
15205 StartGameActions(options.network, setup.autorecord, level.random_seed);
15207 else if (tape.pausing)
15209 #if defined(NETWORK_AVALIABLE)
15210 if (options.network)
15211 SendToServer_ContinuePlaying();
15214 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15218 case GAME_CTRL_ID_UNDO:
15219 // Important: When using "save snapshot when collecting an item" mode,
15220 // load last (current) snapshot for first "undo" after pressing "pause"
15221 // (else the last-but-one snapshot would be loaded, because the snapshot
15222 // pointer already points to the last snapshot when pressing "pause",
15223 // which is fine for "every step/move" mode, but not for "every collect")
15224 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15225 !game_undo_executed)
15228 game_undo_executed = TRUE;
15233 case GAME_CTRL_ID_REDO:
15237 case GAME_CTRL_ID_SAVE:
15241 case GAME_CTRL_ID_LOAD:
15245 case SOUND_CTRL_ID_MUSIC:
15246 if (setup.sound_music)
15248 setup.sound_music = FALSE;
15252 else if (audio.music_available)
15254 setup.sound = setup.sound_music = TRUE;
15256 SetAudioMode(setup.sound);
15262 case SOUND_CTRL_ID_LOOPS:
15263 if (setup.sound_loops)
15264 setup.sound_loops = FALSE;
15265 else if (audio.loops_available)
15267 setup.sound = setup.sound_loops = TRUE;
15269 SetAudioMode(setup.sound);
15273 case SOUND_CTRL_ID_SIMPLE:
15274 if (setup.sound_simple)
15275 setup.sound_simple = FALSE;
15276 else if (audio.sound_available)
15278 setup.sound = setup.sound_simple = TRUE;
15280 SetAudioMode(setup.sound);
15289 static void HandleGameButtons(struct GadgetInfo *gi)
15291 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15294 void HandleSoundButtonKeys(Key key)
15297 if (key == setup.shortcut.sound_simple)
15298 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15299 else if (key == setup.shortcut.sound_loops)
15300 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15301 else if (key == setup.shortcut.sound_music)
15302 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);