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_TIME_ANIM 35
126 #define GAME_PANEL_HEALTH 36
127 #define GAME_PANEL_HEALTH_ANIM 37
128 #define GAME_PANEL_FRAME 38
129 #define GAME_PANEL_SHIELD_NORMAL 39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME 40
131 #define GAME_PANEL_SHIELD_DEADLY 41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME 42
133 #define GAME_PANEL_EXIT 43
134 #define GAME_PANEL_EMC_MAGIC_BALL 44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 45
136 #define GAME_PANEL_LIGHT_SWITCH 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME 47
138 #define GAME_PANEL_TIMEGATE_SWITCH 48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 49
140 #define GAME_PANEL_SWITCHGATE_SWITCH 50
141 #define GAME_PANEL_EMC_LENSES 51
142 #define GAME_PANEL_EMC_LENSES_TIME 52
143 #define GAME_PANEL_EMC_MAGNIFIER 53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME 54
145 #define GAME_PANEL_BALLOON_SWITCH 55
146 #define GAME_PANEL_DYNABOMB_NUMBER 56
147 #define GAME_PANEL_DYNABOMB_SIZE 57
148 #define GAME_PANEL_DYNABOMB_POWER 58
149 #define GAME_PANEL_PENGUINS 59
150 #define GAME_PANEL_SOKOBAN_OBJECTS 60
151 #define GAME_PANEL_SOKOBAN_FIELDS 61
152 #define GAME_PANEL_ROBOT_WHEEL 62
153 #define GAME_PANEL_CONVEYOR_BELT_1 63
154 #define GAME_PANEL_CONVEYOR_BELT_2 64
155 #define GAME_PANEL_CONVEYOR_BELT_3 65
156 #define GAME_PANEL_CONVEYOR_BELT_4 66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 70
161 #define GAME_PANEL_MAGIC_WALL 71
162 #define GAME_PANEL_MAGIC_WALL_TIME 72
163 #define GAME_PANEL_GRAVITY_STATE 73
164 #define GAME_PANEL_GRAPHIC_1 74
165 #define GAME_PANEL_GRAPHIC_2 75
166 #define GAME_PANEL_GRAPHIC_3 76
167 #define GAME_PANEL_GRAPHIC_4 77
168 #define GAME_PANEL_GRAPHIC_5 78
169 #define GAME_PANEL_GRAPHIC_6 79
170 #define GAME_PANEL_GRAPHIC_7 80
171 #define GAME_PANEL_GRAPHIC_8 81
172 #define GAME_PANEL_ELEMENT_1 82
173 #define GAME_PANEL_ELEMENT_2 83
174 #define GAME_PANEL_ELEMENT_3 84
175 #define GAME_PANEL_ELEMENT_4 85
176 #define GAME_PANEL_ELEMENT_5 86
177 #define GAME_PANEL_ELEMENT_6 87
178 #define GAME_PANEL_ELEMENT_7 88
179 #define GAME_PANEL_ELEMENT_8 89
180 #define GAME_PANEL_ELEMENT_COUNT_1 90
181 #define GAME_PANEL_ELEMENT_COUNT_2 91
182 #define GAME_PANEL_ELEMENT_COUNT_3 92
183 #define GAME_PANEL_ELEMENT_COUNT_4 93
184 #define GAME_PANEL_ELEMENT_COUNT_5 94
185 #define GAME_PANEL_ELEMENT_COUNT_6 95
186 #define GAME_PANEL_ELEMENT_COUNT_7 96
187 #define GAME_PANEL_ELEMENT_COUNT_8 97
188 #define GAME_PANEL_CE_SCORE_1 98
189 #define GAME_PANEL_CE_SCORE_2 99
190 #define GAME_PANEL_CE_SCORE_3 100
191 #define GAME_PANEL_CE_SCORE_4 101
192 #define GAME_PANEL_CE_SCORE_5 102
193 #define GAME_PANEL_CE_SCORE_6 103
194 #define GAME_PANEL_CE_SCORE_7 104
195 #define GAME_PANEL_CE_SCORE_8 105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT 110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT 111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT 112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT 113
204 #define GAME_PANEL_PLAYER_NAME 114
205 #define GAME_PANEL_LEVEL_NAME 115
206 #define GAME_PANEL_LEVEL_AUTHOR 116
208 #define NUM_GAME_PANEL_CONTROLS 117
210 struct GamePanelOrderInfo
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
218 struct GamePanelControlInfo
222 struct TextPosInfo *pos;
225 int graphic, graphic_active;
227 int value, last_value;
228 int frame, last_frame;
233 static struct GamePanelControlInfo game_panel_controls[] =
236 GAME_PANEL_LEVEL_NUMBER,
237 &game.panel.level_number,
246 GAME_PANEL_INVENTORY_COUNT,
247 &game.panel.inventory_count,
251 GAME_PANEL_INVENTORY_FIRST_1,
252 &game.panel.inventory_first[0],
256 GAME_PANEL_INVENTORY_FIRST_2,
257 &game.panel.inventory_first[1],
261 GAME_PANEL_INVENTORY_FIRST_3,
262 &game.panel.inventory_first[2],
266 GAME_PANEL_INVENTORY_FIRST_4,
267 &game.panel.inventory_first[3],
271 GAME_PANEL_INVENTORY_FIRST_5,
272 &game.panel.inventory_first[4],
276 GAME_PANEL_INVENTORY_FIRST_6,
277 &game.panel.inventory_first[5],
281 GAME_PANEL_INVENTORY_FIRST_7,
282 &game.panel.inventory_first[6],
286 GAME_PANEL_INVENTORY_FIRST_8,
287 &game.panel.inventory_first[7],
291 GAME_PANEL_INVENTORY_LAST_1,
292 &game.panel.inventory_last[0],
296 GAME_PANEL_INVENTORY_LAST_2,
297 &game.panel.inventory_last[1],
301 GAME_PANEL_INVENTORY_LAST_3,
302 &game.panel.inventory_last[2],
306 GAME_PANEL_INVENTORY_LAST_4,
307 &game.panel.inventory_last[3],
311 GAME_PANEL_INVENTORY_LAST_5,
312 &game.panel.inventory_last[4],
316 GAME_PANEL_INVENTORY_LAST_6,
317 &game.panel.inventory_last[5],
321 GAME_PANEL_INVENTORY_LAST_7,
322 &game.panel.inventory_last[6],
326 GAME_PANEL_INVENTORY_LAST_8,
327 &game.panel.inventory_last[7],
371 GAME_PANEL_KEY_WHITE,
372 &game.panel.key_white,
376 GAME_PANEL_KEY_WHITE_COUNT,
377 &game.panel.key_white_count,
386 GAME_PANEL_HIGHSCORE,
387 &game.panel.highscore,
411 GAME_PANEL_TIME_ANIM,
412 &game.panel.time_anim,
415 IMG_GFX_GAME_PANEL_TIME_ANIM,
416 IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
424 GAME_PANEL_HEALTH_ANIM,
425 &game.panel.health_anim,
428 IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429 IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
437 GAME_PANEL_SHIELD_NORMAL,
438 &game.panel.shield_normal,
442 GAME_PANEL_SHIELD_NORMAL_TIME,
443 &game.panel.shield_normal_time,
447 GAME_PANEL_SHIELD_DEADLY,
448 &game.panel.shield_deadly,
452 GAME_PANEL_SHIELD_DEADLY_TIME,
453 &game.panel.shield_deadly_time,
462 GAME_PANEL_EMC_MAGIC_BALL,
463 &game.panel.emc_magic_ball,
467 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468 &game.panel.emc_magic_ball_switch,
472 GAME_PANEL_LIGHT_SWITCH,
473 &game.panel.light_switch,
477 GAME_PANEL_LIGHT_SWITCH_TIME,
478 &game.panel.light_switch_time,
482 GAME_PANEL_TIMEGATE_SWITCH,
483 &game.panel.timegate_switch,
487 GAME_PANEL_TIMEGATE_SWITCH_TIME,
488 &game.panel.timegate_switch_time,
492 GAME_PANEL_SWITCHGATE_SWITCH,
493 &game.panel.switchgate_switch,
497 GAME_PANEL_EMC_LENSES,
498 &game.panel.emc_lenses,
502 GAME_PANEL_EMC_LENSES_TIME,
503 &game.panel.emc_lenses_time,
507 GAME_PANEL_EMC_MAGNIFIER,
508 &game.panel.emc_magnifier,
512 GAME_PANEL_EMC_MAGNIFIER_TIME,
513 &game.panel.emc_magnifier_time,
517 GAME_PANEL_BALLOON_SWITCH,
518 &game.panel.balloon_switch,
522 GAME_PANEL_DYNABOMB_NUMBER,
523 &game.panel.dynabomb_number,
527 GAME_PANEL_DYNABOMB_SIZE,
528 &game.panel.dynabomb_size,
532 GAME_PANEL_DYNABOMB_POWER,
533 &game.panel.dynabomb_power,
538 &game.panel.penguins,
542 GAME_PANEL_SOKOBAN_OBJECTS,
543 &game.panel.sokoban_objects,
547 GAME_PANEL_SOKOBAN_FIELDS,
548 &game.panel.sokoban_fields,
552 GAME_PANEL_ROBOT_WHEEL,
553 &game.panel.robot_wheel,
557 GAME_PANEL_CONVEYOR_BELT_1,
558 &game.panel.conveyor_belt[0],
562 GAME_PANEL_CONVEYOR_BELT_2,
563 &game.panel.conveyor_belt[1],
567 GAME_PANEL_CONVEYOR_BELT_3,
568 &game.panel.conveyor_belt[2],
572 GAME_PANEL_CONVEYOR_BELT_4,
573 &game.panel.conveyor_belt[3],
577 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578 &game.panel.conveyor_belt_switch[0],
582 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583 &game.panel.conveyor_belt_switch[1],
587 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588 &game.panel.conveyor_belt_switch[2],
592 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593 &game.panel.conveyor_belt_switch[3],
597 GAME_PANEL_MAGIC_WALL,
598 &game.panel.magic_wall,
602 GAME_PANEL_MAGIC_WALL_TIME,
603 &game.panel.magic_wall_time,
607 GAME_PANEL_GRAVITY_STATE,
608 &game.panel.gravity_state,
612 GAME_PANEL_GRAPHIC_1,
613 &game.panel.graphic[0],
617 GAME_PANEL_GRAPHIC_2,
618 &game.panel.graphic[1],
622 GAME_PANEL_GRAPHIC_3,
623 &game.panel.graphic[2],
627 GAME_PANEL_GRAPHIC_4,
628 &game.panel.graphic[3],
632 GAME_PANEL_GRAPHIC_5,
633 &game.panel.graphic[4],
637 GAME_PANEL_GRAPHIC_6,
638 &game.panel.graphic[5],
642 GAME_PANEL_GRAPHIC_7,
643 &game.panel.graphic[6],
647 GAME_PANEL_GRAPHIC_8,
648 &game.panel.graphic[7],
652 GAME_PANEL_ELEMENT_1,
653 &game.panel.element[0],
657 GAME_PANEL_ELEMENT_2,
658 &game.panel.element[1],
662 GAME_PANEL_ELEMENT_3,
663 &game.panel.element[2],
667 GAME_PANEL_ELEMENT_4,
668 &game.panel.element[3],
672 GAME_PANEL_ELEMENT_5,
673 &game.panel.element[4],
677 GAME_PANEL_ELEMENT_6,
678 &game.panel.element[5],
682 GAME_PANEL_ELEMENT_7,
683 &game.panel.element[6],
687 GAME_PANEL_ELEMENT_8,
688 &game.panel.element[7],
692 GAME_PANEL_ELEMENT_COUNT_1,
693 &game.panel.element_count[0],
697 GAME_PANEL_ELEMENT_COUNT_2,
698 &game.panel.element_count[1],
702 GAME_PANEL_ELEMENT_COUNT_3,
703 &game.panel.element_count[2],
707 GAME_PANEL_ELEMENT_COUNT_4,
708 &game.panel.element_count[3],
712 GAME_PANEL_ELEMENT_COUNT_5,
713 &game.panel.element_count[4],
717 GAME_PANEL_ELEMENT_COUNT_6,
718 &game.panel.element_count[5],
722 GAME_PANEL_ELEMENT_COUNT_7,
723 &game.panel.element_count[6],
727 GAME_PANEL_ELEMENT_COUNT_8,
728 &game.panel.element_count[7],
732 GAME_PANEL_CE_SCORE_1,
733 &game.panel.ce_score[0],
737 GAME_PANEL_CE_SCORE_2,
738 &game.panel.ce_score[1],
742 GAME_PANEL_CE_SCORE_3,
743 &game.panel.ce_score[2],
747 GAME_PANEL_CE_SCORE_4,
748 &game.panel.ce_score[3],
752 GAME_PANEL_CE_SCORE_5,
753 &game.panel.ce_score[4],
757 GAME_PANEL_CE_SCORE_6,
758 &game.panel.ce_score[5],
762 GAME_PANEL_CE_SCORE_7,
763 &game.panel.ce_score[6],
767 GAME_PANEL_CE_SCORE_8,
768 &game.panel.ce_score[7],
772 GAME_PANEL_CE_SCORE_1_ELEMENT,
773 &game.panel.ce_score_element[0],
777 GAME_PANEL_CE_SCORE_2_ELEMENT,
778 &game.panel.ce_score_element[1],
782 GAME_PANEL_CE_SCORE_3_ELEMENT,
783 &game.panel.ce_score_element[2],
787 GAME_PANEL_CE_SCORE_4_ELEMENT,
788 &game.panel.ce_score_element[3],
792 GAME_PANEL_CE_SCORE_5_ELEMENT,
793 &game.panel.ce_score_element[4],
797 GAME_PANEL_CE_SCORE_6_ELEMENT,
798 &game.panel.ce_score_element[5],
802 GAME_PANEL_CE_SCORE_7_ELEMENT,
803 &game.panel.ce_score_element[6],
807 GAME_PANEL_CE_SCORE_8_ELEMENT,
808 &game.panel.ce_score_element[7],
812 GAME_PANEL_PLAYER_NAME,
813 &game.panel.player_name,
817 GAME_PANEL_LEVEL_NAME,
818 &game.panel.level_name,
822 GAME_PANEL_LEVEL_AUTHOR,
823 &game.panel.level_author,
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING 3
836 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION 2
838 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF -1
842 #define INITIAL_MOVE_DELAY_ON 0
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED 32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED 4
848 #define MOVE_DELAY_MAX_SPEED 1
850 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
853 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
858 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
860 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 #define MM_HEALTH(x) (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP 0
1009 #define GAME_CTRL_ID_PAUSE 1
1010 #define GAME_CTRL_ID_PLAY 2
1011 #define GAME_CTRL_ID_UNDO 3
1012 #define GAME_CTRL_ID_REDO 4
1013 #define GAME_CTRL_ID_SAVE 5
1014 #define GAME_CTRL_ID_PAUSE2 6
1015 #define GAME_CTRL_ID_LOAD 7
1016 #define GAME_CTRL_ID_PANEL_STOP 8
1017 #define GAME_CTRL_ID_PANEL_PAUSE 9
1018 #define GAME_CTRL_ID_PANEL_PLAY 10
1019 #define SOUND_CTRL_ID_MUSIC 11
1020 #define SOUND_CTRL_ID_LOOPS 12
1021 #define SOUND_CTRL_ID_SIMPLE 13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC 14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS 15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE 16
1026 #define NUM_GAME_BUTTONS 17
1029 /* forward declaration for internal use */
1031 static void CreateField(int, int, int);
1033 static void ResetGfxAnimation(int, int);
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev) \
1065 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1067 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1069 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1071 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev) \
1075 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1077 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1079 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1091 static void HandleGameButtons(struct GadgetInfo *);
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1126 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1128 if (recursion_loop_detected) \
1131 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1133 recursion_loop_detected = TRUE; \
1134 recursion_loop_element = (e); \
1137 recursion_loop_depth++; \
1140 #define RECURSION_LOOP_DETECTION_END() \
1142 recursion_loop_depth--; \
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1149 static int map_player_action[MAX_PLAYERS];
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after */
1154 /* a specified time, eventually calling a function when changing */
1155 /* ------------------------------------------------------------------------- */
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1175 struct ChangingElementInfo
1180 void (*pre_change_function)(int x, int y);
1181 void (*change_function)(int x, int y);
1182 void (*post_change_function)(int x, int y);
1185 static struct ChangingElementInfo change_delay_list[] =
1220 EL_STEEL_EXIT_OPENING,
1228 EL_STEEL_EXIT_CLOSING,
1229 EL_STEEL_EXIT_CLOSED,
1252 EL_EM_STEEL_EXIT_OPENING,
1253 EL_EM_STEEL_EXIT_OPEN,
1260 EL_EM_STEEL_EXIT_CLOSING,
1284 EL_SWITCHGATE_OPENING,
1292 EL_SWITCHGATE_CLOSING,
1293 EL_SWITCHGATE_CLOSED,
1300 EL_TIMEGATE_OPENING,
1308 EL_TIMEGATE_CLOSING,
1317 EL_ACID_SPLASH_LEFT,
1325 EL_ACID_SPLASH_RIGHT,
1334 EL_SP_BUGGY_BASE_ACTIVATING,
1341 EL_SP_BUGGY_BASE_ACTIVATING,
1342 EL_SP_BUGGY_BASE_ACTIVE,
1349 EL_SP_BUGGY_BASE_ACTIVE,
1373 EL_ROBOT_WHEEL_ACTIVE,
1381 EL_TIMEGATE_SWITCH_ACTIVE,
1389 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390 EL_DC_TIMEGATE_SWITCH,
1397 EL_EMC_MAGIC_BALL_ACTIVE,
1398 EL_EMC_MAGIC_BALL_ACTIVE,
1405 EL_EMC_SPRING_BUMPER_ACTIVE,
1406 EL_EMC_SPRING_BUMPER,
1413 EL_DIAGONAL_SHRINKING,
1421 EL_DIAGONAL_GROWING,
1442 int push_delay_fixed, push_delay_random;
1446 { EL_SPRING, 0, 0 },
1447 { EL_BALLOON, 0, 0 },
1449 { EL_SOKOBAN_OBJECT, 2, 0 },
1450 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1451 { EL_SATELLITE, 2, 0 },
1452 { EL_SP_DISK_YELLOW, 2, 0 },
1454 { EL_UNDEFINED, 0, 0 },
1462 move_stepsize_list[] =
1464 { EL_AMOEBA_DROP, 2 },
1465 { EL_AMOEBA_DROPPING, 2 },
1466 { EL_QUICKSAND_FILLING, 1 },
1467 { EL_QUICKSAND_EMPTYING, 1 },
1468 { EL_QUICKSAND_FAST_FILLING, 2 },
1469 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470 { EL_MAGIC_WALL_FILLING, 2 },
1471 { EL_MAGIC_WALL_EMPTYING, 2 },
1472 { EL_BD_MAGIC_WALL_FILLING, 2 },
1473 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1474 { EL_DC_MAGIC_WALL_FILLING, 2 },
1475 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1477 { EL_UNDEFINED, 0 },
1485 collect_count_list[] =
1488 { EL_BD_DIAMOND, 1 },
1489 { EL_EMERALD_YELLOW, 1 },
1490 { EL_EMERALD_RED, 1 },
1491 { EL_EMERALD_PURPLE, 1 },
1493 { EL_SP_INFOTRON, 1 },
1497 { EL_UNDEFINED, 0 },
1505 access_direction_list[] =
1507 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1509 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1510 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1511 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1512 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1513 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1514 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1515 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1516 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1517 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1519 { EL_SP_PORT_LEFT, MV_RIGHT },
1520 { EL_SP_PORT_RIGHT, MV_LEFT },
1521 { EL_SP_PORT_UP, MV_DOWN },
1522 { EL_SP_PORT_DOWN, MV_UP },
1523 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1524 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1525 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1527 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1528 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1529 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1530 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1531 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1532 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1533 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1534 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1535 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1536 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1537 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1539 { EL_UNDEFINED, MV_NONE }
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1544 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1547 IS_JUST_CHANGING(x, y))
1549 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1557 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1558 (y) >= 0 && (y) <= lev_fieldy - 1; \
1559 (y) += playfield_scan_delta_y) \
1560 for ((x) = playfield_scan_start_x; \
1561 (x) >= 0 && (x) <= lev_fieldx - 1; \
1562 (x) += playfield_scan_delta_x)
1565 void DEBUG_SetMaximumDynamite()
1569 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571 local_player->inventory_element[local_player->inventory_size++] =
1576 static void InitPlayfieldScanModeVars()
1578 if (game.use_reverse_scan_direction)
1580 playfield_scan_start_x = lev_fieldx - 1;
1581 playfield_scan_start_y = lev_fieldy - 1;
1583 playfield_scan_delta_x = -1;
1584 playfield_scan_delta_y = -1;
1588 playfield_scan_start_x = 0;
1589 playfield_scan_start_y = 0;
1591 playfield_scan_delta_x = 1;
1592 playfield_scan_delta_y = 1;
1596 static void InitPlayfieldScanMode(int mode)
1598 game.use_reverse_scan_direction =
1599 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1601 InitPlayfieldScanModeVars();
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1607 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1609 /* make sure that stepsize value is always a power of 2 */
1610 move_stepsize = (1 << log_2(move_stepsize));
1612 return TILEX / move_stepsize;
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618 int player_nr = player->index_nr;
1619 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1622 /* do no immediately change move delay -- the player might just be moving */
1623 player->move_delay_value_next = move_delay;
1625 /* information if player can move must be set separately */
1626 player->cannot_move = cannot_move;
1630 player->move_delay = game.initial_move_delay[player_nr];
1631 player->move_delay_value = game.initial_move_delay_value[player_nr];
1633 player->move_delay_value_next = -1;
1635 player->move_delay_reset_counter = 0;
1639 void GetPlayerConfig()
1641 GameFrameDelay = setup.game_frame_delay;
1643 if (!audio.sound_available)
1644 setup.sound_simple = FALSE;
1646 if (!audio.loops_available)
1647 setup.sound_loops = FALSE;
1649 if (!audio.music_available)
1650 setup.sound_music = FALSE;
1652 if (!video.fullscreen_available)
1653 setup.fullscreen = FALSE;
1655 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1657 SetAudioMode(setup.sound);
1660 int GetElementFromGroupElement(int element)
1662 if (IS_GROUP_ELEMENT(element))
1664 struct ElementGroupInfo *group = element_info[element].group;
1665 int last_anim_random_frame = gfx.anim_random_frame;
1668 if (group->choice_mode == ANIM_RANDOM)
1669 gfx.anim_random_frame = RND(group->num_elements_resolved);
1671 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672 group->choice_mode, 0,
1675 if (group->choice_mode == ANIM_RANDOM)
1676 gfx.anim_random_frame = last_anim_random_frame;
1678 group->choice_pos++;
1680 element = group->element_resolved[element_pos];
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1688 if (element == EL_SP_MURPHY)
1692 if (stored_player[0].present)
1694 Feld[x][y] = EL_SP_MURPHY_CLONE;
1700 stored_player[0].initial_element = element;
1701 stored_player[0].use_murphy = TRUE;
1703 if (!level.use_artwork_element[0])
1704 stored_player[0].artwork_element = EL_SP_MURPHY;
1707 Feld[x][y] = EL_PLAYER_1;
1713 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714 int jx = player->jx, jy = player->jy;
1716 player->present = TRUE;
1718 player->block_last_field = (element == EL_SP_MURPHY ?
1719 level.sp_block_last_field :
1720 level.block_last_field);
1722 /* ---------- initialize player's last field block delay --------------- */
1724 /* always start with reliable default value (no adjustment needed) */
1725 player->block_delay_adjustment = 0;
1727 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728 if (player->block_last_field && element == EL_SP_MURPHY)
1729 player->block_delay_adjustment = 1;
1731 /* special case 2: in game engines before 3.1.1, blocking was different */
1732 if (game.use_block_last_field_bug)
1733 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1735 if (!network.enabled || player->connected_network)
1737 player->active = TRUE;
1739 /* remove potentially duplicate players */
1740 if (StorePlayer[jx][jy] == Feld[x][y])
1741 StorePlayer[jx][jy] = 0;
1743 StorePlayer[x][y] = Feld[x][y];
1745 #if DEBUG_INIT_PLAYER
1748 printf("- player element %d activated", player->element_nr);
1749 printf(" (local player is %d and currently %s)\n",
1750 local_player->element_nr,
1751 local_player->active ? "active" : "not active");
1756 Feld[x][y] = EL_EMPTY;
1758 player->jx = player->last_jx = x;
1759 player->jy = player->last_jy = y;
1764 int player_nr = GET_PLAYER_NR(element);
1765 struct PlayerInfo *player = &stored_player[player_nr];
1767 if (player->active && player->killed)
1768 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1772 static void InitField(int x, int y, boolean init_game)
1774 int element = Feld[x][y];
1783 InitPlayerField(x, y, element, init_game);
1786 case EL_SOKOBAN_FIELD_PLAYER:
1787 element = Feld[x][y] = EL_PLAYER_1;
1788 InitField(x, y, init_game);
1790 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791 InitField(x, y, init_game);
1794 case EL_SOKOBAN_FIELD_EMPTY:
1795 local_player->sokobanfields_still_needed++;
1799 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1817 case EL_SPACESHIP_RIGHT:
1818 case EL_SPACESHIP_UP:
1819 case EL_SPACESHIP_LEFT:
1820 case EL_SPACESHIP_DOWN:
1821 case EL_BD_BUTTERFLY:
1822 case EL_BD_BUTTERFLY_RIGHT:
1823 case EL_BD_BUTTERFLY_UP:
1824 case EL_BD_BUTTERFLY_LEFT:
1825 case EL_BD_BUTTERFLY_DOWN:
1827 case EL_BD_FIREFLY_RIGHT:
1828 case EL_BD_FIREFLY_UP:
1829 case EL_BD_FIREFLY_LEFT:
1830 case EL_BD_FIREFLY_DOWN:
1831 case EL_PACMAN_RIGHT:
1833 case EL_PACMAN_LEFT:
1834 case EL_PACMAN_DOWN:
1836 case EL_YAMYAM_LEFT:
1837 case EL_YAMYAM_RIGHT:
1839 case EL_YAMYAM_DOWN:
1840 case EL_DARK_YAMYAM:
1843 case EL_SP_SNIKSNAK:
1844 case EL_SP_ELECTRON:
1853 case EL_AMOEBA_FULL:
1858 case EL_AMOEBA_DROP:
1859 if (y == lev_fieldy - 1)
1861 Feld[x][y] = EL_AMOEBA_GROWING;
1862 Store[x][y] = EL_AMOEBA_WET;
1866 case EL_DYNAMITE_ACTIVE:
1867 case EL_SP_DISK_RED_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872 MovDelay[x][y] = 96;
1875 case EL_EM_DYNAMITE_ACTIVE:
1876 MovDelay[x][y] = 32;
1880 local_player->lights_still_needed++;
1884 local_player->friends_still_needed++;
1889 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1892 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1906 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1910 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1912 game.belt_dir[belt_nr] = belt_dir;
1913 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1915 else /* more than one switch -- set it like the first switch */
1917 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1922 case EL_LIGHT_SWITCH_ACTIVE:
1924 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1927 case EL_INVISIBLE_STEELWALL:
1928 case EL_INVISIBLE_WALL:
1929 case EL_INVISIBLE_SAND:
1930 if (game.light_time_left > 0 ||
1931 game.lenses_time_left > 0)
1932 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1935 case EL_EMC_MAGIC_BALL:
1936 if (game.ball_state)
1937 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1940 case EL_EMC_MAGIC_BALL_SWITCH:
1941 if (game.ball_state)
1942 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1945 case EL_TRIGGER_PLAYER:
1946 case EL_TRIGGER_ELEMENT:
1947 case EL_TRIGGER_CE_VALUE:
1948 case EL_TRIGGER_CE_SCORE:
1950 case EL_ANY_ELEMENT:
1951 case EL_CURRENT_CE_VALUE:
1952 case EL_CURRENT_CE_SCORE:
1969 /* reference elements should not be used on the playfield */
1970 Feld[x][y] = EL_EMPTY;
1974 if (IS_CUSTOM_ELEMENT(element))
1976 if (CAN_MOVE(element))
1979 if (!element_info[element].use_last_ce_value || init_game)
1980 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1982 else if (IS_GROUP_ELEMENT(element))
1984 Feld[x][y] = GetElementFromGroupElement(element);
1986 InitField(x, y, init_game);
1993 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1998 InitField(x, y, init_game);
2000 /* not needed to call InitMovDir() -- already done by InitField()! */
2001 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002 CAN_MOVE(Feld[x][y]))
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2008 int old_element = Feld[x][y];
2010 InitField(x, y, init_game);
2012 /* not needed to call InitMovDir() -- already done by InitField()! */
2013 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014 CAN_MOVE(old_element) &&
2015 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2018 /* this case is in fact a combination of not less than three bugs:
2019 first, it calls InitMovDir() for elements that can move, although this is
2020 already done by InitField(); then, it checks the element that was at this
2021 field _before_ the call to InitField() (which can change it); lastly, it
2022 was not called for "mole with direction" elements, which were treated as
2023 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2027 static int get_key_element_from_nr(int key_nr)
2029 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031 EL_EM_KEY_1 : EL_KEY_1);
2033 return key_base_element + key_nr;
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2038 return (player->inventory_size > 0 ?
2039 player->inventory_element[player->inventory_size - 1] :
2040 player->inventory_infinite_element != EL_UNDEFINED ?
2041 player->inventory_infinite_element :
2042 player->dynabombs_left > 0 ?
2043 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2049 /* pos >= 0: get element from bottom of the stack;
2050 pos < 0: get element from top of the stack */
2054 int min_inventory_size = -pos;
2055 int inventory_pos = player->inventory_size - min_inventory_size;
2056 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2058 return (player->inventory_size >= min_inventory_size ?
2059 player->inventory_element[inventory_pos] :
2060 player->inventory_infinite_element != EL_UNDEFINED ?
2061 player->inventory_infinite_element :
2062 player->dynabombs_left >= min_dynabombs_left ?
2063 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2068 int min_dynabombs_left = pos + 1;
2069 int min_inventory_size = pos + 1 - player->dynabombs_left;
2070 int inventory_pos = pos - player->dynabombs_left;
2072 return (player->inventory_infinite_element != EL_UNDEFINED ?
2073 player->inventory_infinite_element :
2074 player->dynabombs_left >= min_dynabombs_left ?
2075 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076 player->inventory_size >= min_inventory_size ?
2077 player->inventory_element[inventory_pos] :
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2084 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2088 if (gpo1->sort_priority != gpo2->sort_priority)
2089 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2091 compare_result = gpo1->nr - gpo2->nr;
2093 return compare_result;
2096 int getPlayerInventorySize(int player_nr)
2098 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099 return level.native_em_level->ply[player_nr]->dynamite;
2100 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101 return level.native_sp_level->game_sp->red_disk_count;
2103 return stored_player[player_nr].inventory_size;
2106 void InitGameControlValues()
2110 for (i = 0; game_panel_controls[i].nr != -1; i++)
2112 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114 struct TextPosInfo *pos = gpc->pos;
2116 int type = gpc->type;
2120 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121 Error(ERR_EXIT, "this should not happen -- please debug");
2124 /* force update of game controls after initialization */
2125 gpc->value = gpc->last_value = -1;
2126 gpc->frame = gpc->last_frame = -1;
2127 gpc->gfx_frame = -1;
2129 /* determine panel value width for later calculation of alignment */
2130 if (type == TYPE_INTEGER || type == TYPE_STRING)
2132 pos->width = pos->size * getFontWidth(pos->font);
2133 pos->height = getFontHeight(pos->font);
2135 else if (type == TYPE_ELEMENT)
2137 pos->width = pos->size;
2138 pos->height = pos->size;
2141 /* fill structure for game panel draw order */
2143 gpo->sort_priority = pos->sort_priority;
2146 /* sort game panel controls according to sort_priority and control number */
2147 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2151 void UpdatePlayfieldElementCount()
2153 boolean use_element_count = FALSE;
2156 /* first check if it is needed at all to calculate playfield element count */
2157 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159 use_element_count = TRUE;
2161 if (!use_element_count)
2164 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165 element_info[i].element_count = 0;
2167 SCAN_PLAYFIELD(x, y)
2169 element_info[Feld[x][y]].element_count++;
2172 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174 if (IS_IN_GROUP(j, i))
2175 element_info[EL_GROUP_START + i].element_count +=
2176 element_info[j].element_count;
2179 void UpdateGameControlValues()
2182 int time = (local_player->LevelSolved ?
2183 local_player->LevelSolved_CountingTime :
2184 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185 level.native_em_level->lev->time :
2186 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187 level.native_sp_level->game_sp->time_played :
2188 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189 game_mm.energy_left :
2190 game.no_time_limit ? TimePlayed : TimeLeft);
2191 int score = (local_player->LevelSolved ?
2192 local_player->LevelSolved_CountingScore :
2193 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194 level.native_em_level->lev->score :
2195 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196 level.native_sp_level->game_sp->score :
2197 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2199 local_player->score);
2200 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201 level.native_em_level->lev->required :
2202 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203 level.native_sp_level->game_sp->infotrons_still_needed :
2204 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205 game_mm.kettles_still_needed :
2206 local_player->gems_still_needed);
2207 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208 level.native_em_level->lev->required > 0 :
2209 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212 game_mm.kettles_still_needed > 0 ||
2213 game_mm.lights_still_needed > 0 :
2214 local_player->gems_still_needed > 0 ||
2215 local_player->sokobanfields_still_needed > 0 ||
2216 local_player->lights_still_needed > 0);
2217 int health = (local_player->LevelSolved ?
2218 local_player->LevelSolved_CountingHealth :
2219 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220 MM_HEALTH(game_mm.laser_overload_value) :
2221 local_player->health);
2223 UpdatePlayfieldElementCount();
2225 /* update game panel control values */
2227 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2228 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2230 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2231 for (i = 0; i < MAX_NUM_KEYS; i++)
2232 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2233 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2234 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2236 if (game.centered_player_nr == -1)
2238 for (i = 0; i < MAX_PLAYERS; i++)
2240 /* only one player in Supaplex game engine */
2241 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2244 for (k = 0; k < MAX_NUM_KEYS; k++)
2246 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248 if (level.native_em_level->ply[i]->keys & (1 << k))
2249 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2250 get_key_element_from_nr(k);
2252 else if (stored_player[i].key[k])
2253 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2254 get_key_element_from_nr(k);
2257 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2258 getPlayerInventorySize(i);
2260 if (stored_player[i].num_white_keys > 0)
2261 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2264 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2265 stored_player[i].num_white_keys;
2270 int player_nr = game.centered_player_nr;
2272 for (k = 0; k < MAX_NUM_KEYS; k++)
2274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2277 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2278 get_key_element_from_nr(k);
2280 else if (stored_player[player_nr].key[k])
2281 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282 get_key_element_from_nr(k);
2285 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2286 getPlayerInventorySize(player_nr);
2288 if (stored_player[player_nr].num_white_keys > 0)
2289 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2292 stored_player[player_nr].num_white_keys;
2295 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2298 get_inventory_element_from_pos(local_player, i);
2299 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2300 get_inventory_element_from_pos(local_player, -i - 1);
2303 game_panel_controls[GAME_PANEL_SCORE].value = score;
2304 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306 game_panel_controls[GAME_PANEL_TIME].value = time;
2308 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2309 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2310 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312 if (level.time == 0)
2313 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2315 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2317 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2318 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2320 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2323 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2326 local_player->shield_normal_time_left;
2327 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2328 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2331 local_player->shield_deadly_time_left;
2333 game_panel_controls[GAME_PANEL_EXIT].value =
2334 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2337 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2338 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2339 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2340 EL_EMC_MAGIC_BALL_SWITCH);
2342 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2343 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2344 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2345 game.light_time_left;
2347 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2348 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2349 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2350 game.timegate_time_left;
2352 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2353 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2356 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2357 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2358 game.lenses_time_left;
2360 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2361 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2362 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2363 game.magnify_time_left;
2365 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2366 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2367 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2368 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2369 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2370 EL_BALLOON_SWITCH_NONE);
2372 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2373 local_player->dynabomb_count;
2374 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2375 local_player->dynabomb_size;
2376 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2377 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379 game_panel_controls[GAME_PANEL_PENGUINS].value =
2380 local_player->friends_still_needed;
2382 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2383 local_player->sokobanfields_still_needed;
2384 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2385 local_player->sokobanfields_still_needed;
2387 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2388 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390 for (i = 0; i < NUM_BELTS; i++)
2392 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2393 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2394 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2395 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2396 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2399 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2400 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2401 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2402 game.magic_wall_time_left;
2404 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2405 local_player->gravity;
2407 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2408 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2410 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2411 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2412 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2413 game.panel.element[i].id : EL_UNDEFINED);
2415 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2416 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2417 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2418 element_info[game.panel.element_count[i].id].element_count : 0);
2420 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2421 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2422 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2423 element_info[game.panel.ce_score[i].id].collect_score : 0);
2425 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2426 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2427 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2428 element_info[game.panel.ce_score_element[i].id].collect_score :
2431 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2432 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2433 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2435 /* update game panel control frames */
2437 for (i = 0; game_panel_controls[i].nr != -1; i++)
2439 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2441 if (gpc->type == TYPE_ELEMENT)
2443 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2445 int last_anim_random_frame = gfx.anim_random_frame;
2446 int element = gpc->value;
2447 int graphic = el2panelimg(element);
2449 if (gpc->value != gpc->last_value)
2452 gpc->gfx_random = INIT_GFX_RANDOM();
2458 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2459 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2460 gpc->gfx_random = INIT_GFX_RANDOM();
2463 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2464 gfx.anim_random_frame = gpc->gfx_random;
2466 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2467 gpc->gfx_frame = element_info[element].collect_score;
2469 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2472 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2473 gfx.anim_random_frame = last_anim_random_frame;
2476 else if (gpc->type == TYPE_GRAPHIC)
2478 if (gpc->graphic != IMG_UNDEFINED)
2480 int last_anim_random_frame = gfx.anim_random_frame;
2481 int graphic = gpc->graphic;
2483 if (gpc->value != gpc->last_value)
2486 gpc->gfx_random = INIT_GFX_RANDOM();
2492 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2493 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2494 gpc->gfx_random = INIT_GFX_RANDOM();
2497 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2498 gfx.anim_random_frame = gpc->gfx_random;
2500 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2502 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503 gfx.anim_random_frame = last_anim_random_frame;
2509 void DisplayGameControlValues()
2511 boolean redraw_panel = FALSE;
2514 for (i = 0; game_panel_controls[i].nr != -1; i++)
2516 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2518 if (PANEL_DEACTIVATED(gpc->pos))
2521 if (gpc->value == gpc->last_value &&
2522 gpc->frame == gpc->last_frame)
2525 redraw_panel = TRUE;
2531 /* copy default game door content to main double buffer */
2533 /* !!! CHECK AGAIN !!! */
2534 SetPanelBackground();
2535 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2536 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2538 /* redraw game control buttons */
2539 RedrawGameButtons();
2541 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2543 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2545 int nr = game_panel_order[i].nr;
2546 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2547 struct TextPosInfo *pos = gpc->pos;
2548 int type = gpc->type;
2549 int value = gpc->value;
2550 int frame = gpc->frame;
2551 int size = pos->size;
2552 int font = pos->font;
2553 boolean draw_masked = pos->draw_masked;
2554 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2556 if (PANEL_DEACTIVATED(pos))
2559 gpc->last_value = value;
2560 gpc->last_frame = frame;
2562 if (type == TYPE_INTEGER)
2564 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2565 nr == GAME_PANEL_TIME)
2567 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2569 if (use_dynamic_size) /* use dynamic number of digits */
2571 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2572 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2573 int size2 = size1 + 1;
2574 int font1 = pos->font;
2575 int font2 = pos->font_alt;
2577 size = (value < value_change ? size1 : size2);
2578 font = (value < value_change ? font1 : font2);
2582 /* correct text size if "digits" is zero or less */
2584 size = strlen(int2str(value, size));
2586 /* dynamically correct text alignment */
2587 pos->width = size * getFontWidth(font);
2589 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2590 int2str(value, size), font, mask_mode);
2592 else if (type == TYPE_ELEMENT)
2594 int element, graphic;
2598 int dst_x = PANEL_XPOS(pos);
2599 int dst_y = PANEL_YPOS(pos);
2601 if (value != EL_UNDEFINED && value != EL_EMPTY)
2604 graphic = el2panelimg(value);
2606 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2608 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2611 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2614 width = graphic_info[graphic].width * size / TILESIZE;
2615 height = graphic_info[graphic].height * size / TILESIZE;
2618 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2621 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2625 else if (type == TYPE_GRAPHIC)
2627 int graphic = gpc->graphic;
2628 int graphic_active = gpc->graphic_active;
2632 int dst_x = PANEL_XPOS(pos);
2633 int dst_y = PANEL_YPOS(pos);
2634 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2635 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2637 if (graphic != IMG_UNDEFINED && !skip)
2639 if (pos->style == STYLE_REVERSE)
2640 value = 100 - value;
2642 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2644 if (pos->direction & MV_HORIZONTAL)
2646 width = graphic_info[graphic_active].width * value / 100;
2647 height = graphic_info[graphic_active].height;
2649 if (pos->direction == MV_LEFT)
2651 src_x += graphic_info[graphic_active].width - width;
2652 dst_x += graphic_info[graphic_active].width - width;
2657 width = graphic_info[graphic_active].width;
2658 height = graphic_info[graphic_active].height * value / 100;
2660 if (pos->direction == MV_UP)
2662 src_y += graphic_info[graphic_active].height - height;
2663 dst_y += graphic_info[graphic_active].height - height;
2668 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2671 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2674 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2676 if (pos->direction & MV_HORIZONTAL)
2678 if (pos->direction == MV_RIGHT)
2685 dst_x = PANEL_XPOS(pos);
2688 width = graphic_info[graphic].width - width;
2692 if (pos->direction == MV_DOWN)
2699 dst_y = PANEL_YPOS(pos);
2702 height = graphic_info[graphic].height - height;
2706 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2709 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2713 else if (type == TYPE_STRING)
2715 boolean active = (value != 0);
2716 char *state_normal = "off";
2717 char *state_active = "on";
2718 char *state = (active ? state_active : state_normal);
2719 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2720 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2721 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2722 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2724 if (nr == GAME_PANEL_GRAVITY_STATE)
2726 int font1 = pos->font; /* (used for normal state) */
2727 int font2 = pos->font_alt; /* (used for active state) */
2729 font = (active ? font2 : font1);
2738 /* don't truncate output if "chars" is zero or less */
2741 /* dynamically correct text alignment */
2742 pos->width = size * getFontWidth(font);
2745 s_cut = getStringCopyN(s, size);
2747 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2748 s_cut, font, mask_mode);
2754 redraw_mask |= REDRAW_DOOR_1;
2757 SetGameStatus(GAME_MODE_PLAYING);
2760 void UpdateAndDisplayGameControlValues()
2762 if (tape.deactivate_display)
2765 UpdateGameControlValues();
2766 DisplayGameControlValues();
2769 void UpdateGameDoorValues()
2771 UpdateGameControlValues();
2774 void DrawGameDoorValues()
2776 DisplayGameControlValues();
2781 =============================================================================
2783 -----------------------------------------------------------------------------
2784 initialize game engine due to level / tape version number
2785 =============================================================================
2788 static void InitGameEngine()
2790 int i, j, k, l, x, y;
2792 /* set game engine from tape file when re-playing, else from level file */
2793 game.engine_version = (tape.playing ? tape.engine_version :
2794 level.game_version);
2796 /* set single or multi-player game mode (needed for re-playing tapes) */
2797 game.team_mode = setup.team_mode;
2801 int num_players = 0;
2803 for (i = 0; i < MAX_PLAYERS; i++)
2804 if (tape.player_participates[i])
2807 /* multi-player tapes contain input data for more than one player */
2808 game.team_mode = (num_players > 1);
2811 /* ---------------------------------------------------------------------- */
2812 /* set flags for bugs and changes according to active game engine version */
2813 /* ---------------------------------------------------------------------- */
2816 Summary of bugfix/change:
2817 Fixed handling for custom elements that change when pushed by the player.
2819 Fixed/changed in version:
2823 Before 3.1.0, custom elements that "change when pushing" changed directly
2824 after the player started pushing them (until then handled in "DigField()").
2825 Since 3.1.0, these custom elements are not changed until the "pushing"
2826 move of the element is finished (now handled in "ContinueMoving()").
2828 Affected levels/tapes:
2829 The first condition is generally needed for all levels/tapes before version
2830 3.1.0, which might use the old behaviour before it was changed; known tapes
2831 that are affected are some tapes from the level set "Walpurgis Gardens" by
2833 The second condition is an exception from the above case and is needed for
2834 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2835 above (including some development versions of 3.1.0), but before it was
2836 known that this change would break tapes like the above and was fixed in
2837 3.1.1, so that the changed behaviour was active although the engine version
2838 while recording maybe was before 3.1.0. There is at least one tape that is
2839 affected by this exception, which is the tape for the one-level set "Bug
2840 Machine" by Juergen Bonhagen.
2843 game.use_change_when_pushing_bug =
2844 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2846 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2847 tape.game_version < VERSION_IDENT(3,1,1,0)));
2850 Summary of bugfix/change:
2851 Fixed handling for blocking the field the player leaves when moving.
2853 Fixed/changed in version:
2857 Before 3.1.1, when "block last field when moving" was enabled, the field
2858 the player is leaving when moving was blocked for the time of the move,
2859 and was directly unblocked afterwards. This resulted in the last field
2860 being blocked for exactly one less than the number of frames of one player
2861 move. Additionally, even when blocking was disabled, the last field was
2862 blocked for exactly one frame.
2863 Since 3.1.1, due to changes in player movement handling, the last field
2864 is not blocked at all when blocking is disabled. When blocking is enabled,
2865 the last field is blocked for exactly the number of frames of one player
2866 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2867 last field is blocked for exactly one more than the number of frames of
2870 Affected levels/tapes:
2871 (!!! yet to be determined -- probably many !!!)
2874 game.use_block_last_field_bug =
2875 (game.engine_version < VERSION_IDENT(3,1,1,0));
2877 game_em.use_single_button =
2878 (game.engine_version > VERSION_IDENT(4,0,0,2));
2880 game_em.use_snap_key_bug =
2881 (game.engine_version < VERSION_IDENT(4,0,1,0));
2883 /* ---------------------------------------------------------------------- */
2885 /* set maximal allowed number of custom element changes per game frame */
2886 game.max_num_changes_per_frame = 1;
2888 /* default scan direction: scan playfield from top/left to bottom/right */
2889 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2891 /* dynamically adjust element properties according to game engine version */
2892 InitElementPropertiesEngine(game.engine_version);
2895 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2896 printf(" tape version == %06d [%s] [file: %06d]\n",
2897 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2899 printf(" => game.engine_version == %06d\n", game.engine_version);
2902 /* ---------- initialize player's initial move delay --------------------- */
2904 /* dynamically adjust player properties according to level information */
2905 for (i = 0; i < MAX_PLAYERS; i++)
2906 game.initial_move_delay_value[i] =
2907 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2909 /* dynamically adjust player properties according to game engine version */
2910 for (i = 0; i < MAX_PLAYERS; i++)
2911 game.initial_move_delay[i] =
2912 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2913 game.initial_move_delay_value[i] : 0);
2915 /* ---------- initialize player's initial push delay --------------------- */
2917 /* dynamically adjust player properties according to game engine version */
2918 game.initial_push_delay_value =
2919 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2921 /* ---------- initialize changing elements ------------------------------- */
2923 /* initialize changing elements information */
2924 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2926 struct ElementInfo *ei = &element_info[i];
2928 /* this pointer might have been changed in the level editor */
2929 ei->change = &ei->change_page[0];
2931 if (!IS_CUSTOM_ELEMENT(i))
2933 ei->change->target_element = EL_EMPTY_SPACE;
2934 ei->change->delay_fixed = 0;
2935 ei->change->delay_random = 0;
2936 ei->change->delay_frames = 1;
2939 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2941 ei->has_change_event[j] = FALSE;
2943 ei->event_page_nr[j] = 0;
2944 ei->event_page[j] = &ei->change_page[0];
2948 /* add changing elements from pre-defined list */
2949 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2951 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2952 struct ElementInfo *ei = &element_info[ch_delay->element];
2954 ei->change->target_element = ch_delay->target_element;
2955 ei->change->delay_fixed = ch_delay->change_delay;
2957 ei->change->pre_change_function = ch_delay->pre_change_function;
2958 ei->change->change_function = ch_delay->change_function;
2959 ei->change->post_change_function = ch_delay->post_change_function;
2961 ei->change->can_change = TRUE;
2962 ei->change->can_change_or_has_action = TRUE;
2964 ei->has_change_event[CE_DELAY] = TRUE;
2966 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2967 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2970 /* ---------- initialize internal run-time variables --------------------- */
2972 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2974 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2976 for (j = 0; j < ei->num_change_pages; j++)
2978 ei->change_page[j].can_change_or_has_action =
2979 (ei->change_page[j].can_change |
2980 ei->change_page[j].has_action);
2984 /* add change events from custom element configuration */
2985 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2987 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2989 for (j = 0; j < ei->num_change_pages; j++)
2991 if (!ei->change_page[j].can_change_or_has_action)
2994 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2996 /* only add event page for the first page found with this event */
2997 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2999 ei->has_change_event[k] = TRUE;
3001 ei->event_page_nr[k] = j;
3002 ei->event_page[k] = &ei->change_page[j];
3008 /* ---------- initialize reference elements in change conditions --------- */
3010 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3012 int element = EL_CUSTOM_START + i;
3013 struct ElementInfo *ei = &element_info[element];
3015 for (j = 0; j < ei->num_change_pages; j++)
3017 int trigger_element = ei->change_page[j].initial_trigger_element;
3019 if (trigger_element >= EL_PREV_CE_8 &&
3020 trigger_element <= EL_NEXT_CE_8)
3021 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3023 ei->change_page[j].trigger_element = trigger_element;
3027 /* ---------- initialize run-time trigger player and element ------------- */
3029 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3031 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3033 for (j = 0; j < ei->num_change_pages; j++)
3035 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3036 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3037 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3038 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3039 ei->change_page[j].actual_trigger_ce_value = 0;
3040 ei->change_page[j].actual_trigger_ce_score = 0;
3044 /* ---------- initialize trigger events ---------------------------------- */
3046 /* initialize trigger events information */
3047 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3048 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3049 trigger_events[i][j] = FALSE;
3051 /* add trigger events from element change event properties */
3052 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3054 struct ElementInfo *ei = &element_info[i];
3056 for (j = 0; j < ei->num_change_pages; j++)
3058 if (!ei->change_page[j].can_change_or_has_action)
3061 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3063 int trigger_element = ei->change_page[j].trigger_element;
3065 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3067 if (ei->change_page[j].has_event[k])
3069 if (IS_GROUP_ELEMENT(trigger_element))
3071 struct ElementGroupInfo *group =
3072 element_info[trigger_element].group;
3074 for (l = 0; l < group->num_elements_resolved; l++)
3075 trigger_events[group->element_resolved[l]][k] = TRUE;
3077 else if (trigger_element == EL_ANY_ELEMENT)
3078 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3079 trigger_events[l][k] = TRUE;
3081 trigger_events[trigger_element][k] = TRUE;
3088 /* ---------- initialize push delay -------------------------------------- */
3090 /* initialize push delay values to default */
3091 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3093 if (!IS_CUSTOM_ELEMENT(i))
3095 /* set default push delay values (corrected since version 3.0.7-1) */
3096 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3098 element_info[i].push_delay_fixed = 2;
3099 element_info[i].push_delay_random = 8;
3103 element_info[i].push_delay_fixed = 8;
3104 element_info[i].push_delay_random = 8;
3109 /* set push delay value for certain elements from pre-defined list */
3110 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3112 int e = push_delay_list[i].element;
3114 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3115 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3118 /* set push delay value for Supaplex elements for newer engine versions */
3119 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3121 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3123 if (IS_SP_ELEMENT(i))
3125 /* set SP push delay to just enough to push under a falling zonk */
3126 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3128 element_info[i].push_delay_fixed = delay;
3129 element_info[i].push_delay_random = 0;
3134 /* ---------- initialize move stepsize ----------------------------------- */
3136 /* initialize move stepsize values to default */
3137 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3138 if (!IS_CUSTOM_ELEMENT(i))
3139 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3141 /* set move stepsize value for certain elements from pre-defined list */
3142 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3144 int e = move_stepsize_list[i].element;
3146 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3149 /* ---------- initialize collect score ----------------------------------- */
3151 /* initialize collect score values for custom elements from initial value */
3152 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153 if (IS_CUSTOM_ELEMENT(i))
3154 element_info[i].collect_score = element_info[i].collect_score_initial;
3156 /* ---------- initialize collect count ----------------------------------- */
3158 /* initialize collect count values for non-custom elements */
3159 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3160 if (!IS_CUSTOM_ELEMENT(i))
3161 element_info[i].collect_count_initial = 0;
3163 /* add collect count values for all elements from pre-defined list */
3164 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3165 element_info[collect_count_list[i].element].collect_count_initial =
3166 collect_count_list[i].count;
3168 /* ---------- initialize access direction -------------------------------- */
3170 /* initialize access direction values to default (access from every side) */
3171 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3172 if (!IS_CUSTOM_ELEMENT(i))
3173 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3175 /* set access direction value for certain elements from pre-defined list */
3176 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3177 element_info[access_direction_list[i].element].access_direction =
3178 access_direction_list[i].direction;
3180 /* ---------- initialize explosion content ------------------------------- */
3181 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183 if (IS_CUSTOM_ELEMENT(i))
3186 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3188 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3190 element_info[i].content.e[x][y] =
3191 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3192 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3193 i == EL_PLAYER_3 ? EL_EMERALD :
3194 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3195 i == EL_MOLE ? EL_EMERALD_RED :
3196 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3197 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3198 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3199 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3200 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3201 i == EL_WALL_EMERALD ? EL_EMERALD :
3202 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3203 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3204 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3205 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3206 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3207 i == EL_WALL_PEARL ? EL_PEARL :
3208 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3213 /* ---------- initialize recursion detection ------------------------------ */
3214 recursion_loop_depth = 0;
3215 recursion_loop_detected = FALSE;
3216 recursion_loop_element = EL_UNDEFINED;
3218 /* ---------- initialize graphics engine ---------------------------------- */
3219 game.scroll_delay_value =
3220 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3221 setup.scroll_delay ? setup.scroll_delay_value : 0);
3222 game.scroll_delay_value =
3223 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3225 /* ---------- initialize game engine snapshots ---------------------------- */
3226 for (i = 0; i < MAX_PLAYERS; i++)
3227 game.snapshot.last_action[i] = 0;
3228 game.snapshot.changed_action = FALSE;
3229 game.snapshot.collected_item = FALSE;
3230 game.snapshot.mode =
3231 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3232 SNAPSHOT_MODE_EVERY_STEP :
3233 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3234 SNAPSHOT_MODE_EVERY_MOVE :
3235 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3236 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3237 game.snapshot.save_snapshot = FALSE;
3239 /* ---------- initialize level time for Supaplex engine ------------------- */
3240 /* Supaplex levels with time limit currently unsupported -- should be added */
3241 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3245 int get_num_special_action(int element, int action_first, int action_last)
3247 int num_special_action = 0;
3250 for (i = action_first; i <= action_last; i++)
3252 boolean found = FALSE;
3254 for (j = 0; j < NUM_DIRECTIONS; j++)
3255 if (el_act_dir2img(element, i, j) !=
3256 el_act_dir2img(element, ACTION_DEFAULT, j))
3260 num_special_action++;
3265 return num_special_action;
3270 =============================================================================
3272 -----------------------------------------------------------------------------
3273 initialize and start new game
3274 =============================================================================
3277 #if DEBUG_INIT_PLAYER
3278 static void DebugPrintPlayerStatus(char *message)
3285 printf("%s:\n", message);
3287 for (i = 0; i < MAX_PLAYERS; i++)
3289 struct PlayerInfo *player = &stored_player[i];
3291 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3295 player->connected_locally,
3296 player->connected_network,
3299 if (local_player == player)
3300 printf(" (local player)");
3309 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3310 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3311 int fade_mask = REDRAW_FIELD;
3313 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3314 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3315 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3316 int initial_move_dir = MV_DOWN;
3319 // required here to update video display before fading (FIX THIS)
3320 DrawMaskedBorder(REDRAW_DOOR_2);
3322 if (!game.restart_level)
3323 CloseDoor(DOOR_CLOSE_1);
3325 SetGameStatus(GAME_MODE_PLAYING);
3327 if (level_editor_test_game)
3328 FadeSkipNextFadeIn();
3330 FadeSetEnterScreen();
3332 if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3333 fade_mask = REDRAW_ALL;
3335 FadeLevelSoundsAndMusic();
3337 ExpireSoundLoops(TRUE);
3339 if (!level_editor_test_game)
3342 /* needed if different viewport properties defined for playing */
3343 ChangeViewportPropertiesIfNeeded();
3347 DrawCompleteVideoDisplay();
3349 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3352 InitGameControlValues();
3354 /* don't play tapes over network */
3355 network_playing = (network.enabled && !tape.playing);
3357 for (i = 0; i < MAX_PLAYERS; i++)
3359 struct PlayerInfo *player = &stored_player[i];
3361 player->index_nr = i;
3362 player->index_bit = (1 << i);
3363 player->element_nr = EL_PLAYER_1 + i;
3365 player->present = FALSE;
3366 player->active = FALSE;
3367 player->mapped = FALSE;
3369 player->killed = FALSE;
3370 player->reanimated = FALSE;
3373 player->effective_action = 0;
3374 player->programmed_action = 0;
3376 player->mouse_action.lx = 0;
3377 player->mouse_action.ly = 0;
3378 player->mouse_action.button = 0;
3379 player->mouse_action.button_hint = 0;
3381 player->effective_mouse_action.lx = 0;
3382 player->effective_mouse_action.ly = 0;
3383 player->effective_mouse_action.button = 0;
3384 player->effective_mouse_action.button_hint = 0;
3387 player->score_final = 0;
3389 player->health = MAX_HEALTH;
3390 player->health_final = MAX_HEALTH;
3392 player->gems_still_needed = level.gems_needed;
3393 player->sokobanfields_still_needed = 0;
3394 player->lights_still_needed = 0;
3395 player->players_still_needed = 0;
3396 player->friends_still_needed = 0;
3398 for (j = 0; j < MAX_NUM_KEYS; j++)
3399 player->key[j] = FALSE;
3401 player->num_white_keys = 0;
3403 player->dynabomb_count = 0;
3404 player->dynabomb_size = 1;
3405 player->dynabombs_left = 0;
3406 player->dynabomb_xl = FALSE;
3408 player->MovDir = initial_move_dir;
3411 player->GfxDir = initial_move_dir;
3412 player->GfxAction = ACTION_DEFAULT;
3414 player->StepFrame = 0;
3416 player->initial_element = player->element_nr;
3417 player->artwork_element =
3418 (level.use_artwork_element[i] ? level.artwork_element[i] :
3419 player->element_nr);
3420 player->use_murphy = FALSE;
3422 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3423 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3425 player->gravity = level.initial_player_gravity[i];
3427 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3429 player->actual_frame_counter = 0;
3431 player->step_counter = 0;
3433 player->last_move_dir = initial_move_dir;
3435 player->is_active = FALSE;
3437 player->is_waiting = FALSE;
3438 player->is_moving = FALSE;
3439 player->is_auto_moving = FALSE;
3440 player->is_digging = FALSE;
3441 player->is_snapping = FALSE;
3442 player->is_collecting = FALSE;
3443 player->is_pushing = FALSE;
3444 player->is_switching = FALSE;
3445 player->is_dropping = FALSE;
3446 player->is_dropping_pressed = FALSE;
3448 player->is_bored = FALSE;
3449 player->is_sleeping = FALSE;
3451 player->was_waiting = TRUE;
3452 player->was_moving = FALSE;
3453 player->was_snapping = FALSE;
3454 player->was_dropping = FALSE;
3456 player->force_dropping = FALSE;
3458 player->frame_counter_bored = -1;
3459 player->frame_counter_sleeping = -1;
3461 player->anim_delay_counter = 0;
3462 player->post_delay_counter = 0;
3464 player->dir_waiting = initial_move_dir;
3465 player->action_waiting = ACTION_DEFAULT;
3466 player->last_action_waiting = ACTION_DEFAULT;
3467 player->special_action_bored = ACTION_DEFAULT;
3468 player->special_action_sleeping = ACTION_DEFAULT;
3470 player->switch_x = -1;
3471 player->switch_y = -1;
3473 player->drop_x = -1;
3474 player->drop_y = -1;
3476 player->show_envelope = 0;
3478 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3480 player->push_delay = -1; /* initialized when pushing starts */
3481 player->push_delay_value = game.initial_push_delay_value;
3483 player->drop_delay = 0;
3484 player->drop_pressed_delay = 0;
3486 player->last_jx = -1;
3487 player->last_jy = -1;
3491 player->shield_normal_time_left = 0;
3492 player->shield_deadly_time_left = 0;
3494 player->inventory_infinite_element = EL_UNDEFINED;
3495 player->inventory_size = 0;
3497 if (level.use_initial_inventory[i])
3499 for (j = 0; j < level.initial_inventory_size[i]; j++)
3501 int element = level.initial_inventory_content[i][j];
3502 int collect_count = element_info[element].collect_count_initial;
3505 if (!IS_CUSTOM_ELEMENT(element))
3508 if (collect_count == 0)
3509 player->inventory_infinite_element = element;
3511 for (k = 0; k < collect_count; k++)
3512 if (player->inventory_size < MAX_INVENTORY_SIZE)
3513 player->inventory_element[player->inventory_size++] = element;
3517 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3518 SnapField(player, 0, 0);
3520 player->LevelSolved = FALSE;
3521 player->GameOver = FALSE;
3523 player->LevelSolved_GameWon = FALSE;
3524 player->LevelSolved_GameEnd = FALSE;
3525 player->LevelSolved_PanelOff = FALSE;
3526 player->LevelSolved_SaveTape = FALSE;
3527 player->LevelSolved_SaveScore = FALSE;
3529 player->LevelSolved_CountingTime = 0;
3530 player->LevelSolved_CountingScore = 0;
3531 player->LevelSolved_CountingHealth = 0;
3533 map_player_action[i] = i;
3536 network_player_action_received = FALSE;
3538 /* initial null action */
3539 if (network_playing)
3540 SendToServer_MovePlayer(MV_NONE);
3548 TimeLeft = level.time;
3551 ScreenMovDir = MV_NONE;
3555 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3557 AllPlayersGone = FALSE;
3559 game.no_time_limit = (level.time == 0);
3561 game.yamyam_content_nr = 0;
3562 game.robot_wheel_active = FALSE;
3563 game.magic_wall_active = FALSE;
3564 game.magic_wall_time_left = 0;
3565 game.light_time_left = 0;
3566 game.timegate_time_left = 0;
3567 game.switchgate_pos = 0;
3568 game.wind_direction = level.wind_direction_initial;
3570 game.lenses_time_left = 0;
3571 game.magnify_time_left = 0;
3573 game.ball_state = level.ball_state_initial;
3574 game.ball_content_nr = 0;
3576 game.envelope_active = FALSE;
3578 for (i = 0; i < NUM_BELTS; i++)
3580 game.belt_dir[i] = MV_NONE;
3581 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3584 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3585 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3587 #if DEBUG_INIT_PLAYER
3588 DebugPrintPlayerStatus("Player status at level initialization");
3591 SCAN_PLAYFIELD(x, y)
3593 Feld[x][y] = level.field[x][y];
3594 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3595 ChangeDelay[x][y] = 0;
3596 ChangePage[x][y] = -1;
3597 CustomValue[x][y] = 0; /* initialized in InitField() */
3598 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3600 WasJustMoving[x][y] = 0;
3601 WasJustFalling[x][y] = 0;
3602 CheckCollision[x][y] = 0;
3603 CheckImpact[x][y] = 0;
3605 Pushed[x][y] = FALSE;
3607 ChangeCount[x][y] = 0;
3608 ChangeEvent[x][y] = -1;
3610 ExplodePhase[x][y] = 0;
3611 ExplodeDelay[x][y] = 0;
3612 ExplodeField[x][y] = EX_TYPE_NONE;
3614 RunnerVisit[x][y] = 0;
3615 PlayerVisit[x][y] = 0;
3618 GfxRandom[x][y] = INIT_GFX_RANDOM();
3619 GfxElement[x][y] = EL_UNDEFINED;
3620 GfxAction[x][y] = ACTION_DEFAULT;
3621 GfxDir[x][y] = MV_NONE;
3622 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3625 SCAN_PLAYFIELD(x, y)
3627 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3629 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3631 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3634 InitField(x, y, TRUE);
3636 ResetGfxAnimation(x, y);
3641 for (i = 0; i < MAX_PLAYERS; i++)
3643 struct PlayerInfo *player = &stored_player[i];
3645 /* set number of special actions for bored and sleeping animation */
3646 player->num_special_action_bored =
3647 get_num_special_action(player->artwork_element,
3648 ACTION_BORING_1, ACTION_BORING_LAST);
3649 player->num_special_action_sleeping =
3650 get_num_special_action(player->artwork_element,
3651 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3654 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3655 emulate_sb ? EMU_SOKOBAN :
3656 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3658 /* initialize type of slippery elements */
3659 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3661 if (!IS_CUSTOM_ELEMENT(i))
3663 /* default: elements slip down either to the left or right randomly */
3664 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3666 /* SP style elements prefer to slip down on the left side */
3667 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3668 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3670 /* BD style elements prefer to slip down on the left side */
3671 if (game.emulation == EMU_BOULDERDASH)
3672 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3676 /* initialize explosion and ignition delay */
3677 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3679 if (!IS_CUSTOM_ELEMENT(i))
3682 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3683 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3684 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3685 int last_phase = (num_phase + 1) * delay;
3686 int half_phase = (num_phase / 2) * delay;
3688 element_info[i].explosion_delay = last_phase - 1;
3689 element_info[i].ignition_delay = half_phase;
3691 if (i == EL_BLACK_ORB)
3692 element_info[i].ignition_delay = 1;
3696 /* correct non-moving belts to start moving left */
3697 for (i = 0; i < NUM_BELTS; i++)
3698 if (game.belt_dir[i] == MV_NONE)
3699 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3701 #if USE_NEW_PLAYER_ASSIGNMENTS
3702 for (i = 0; i < MAX_PLAYERS; i++)
3704 stored_player[i].connected = FALSE;
3706 /* in network game mode, the local player might not be the first player */
3707 if (stored_player[i].connected_locally)
3708 local_player = &stored_player[i];
3711 if (!network.enabled)
3712 local_player->connected = TRUE;
3716 for (i = 0; i < MAX_PLAYERS; i++)
3717 stored_player[i].connected = tape.player_participates[i];
3719 else if (network.enabled)
3721 /* add team mode players connected over the network (needed for correct
3722 assignment of player figures from level to locally playing players) */
3724 for (i = 0; i < MAX_PLAYERS; i++)
3725 if (stored_player[i].connected_network)
3726 stored_player[i].connected = TRUE;
3728 else if (game.team_mode)
3730 /* try to guess locally connected team mode players (needed for correct
3731 assignment of player figures from level to locally playing players) */
3733 for (i = 0; i < MAX_PLAYERS; i++)
3734 if (setup.input[i].use_joystick ||
3735 setup.input[i].key.left != KSYM_UNDEFINED)
3736 stored_player[i].connected = TRUE;
3739 #if DEBUG_INIT_PLAYER
3740 DebugPrintPlayerStatus("Player status after level initialization");
3743 #if DEBUG_INIT_PLAYER
3745 printf("Reassigning players ...\n");
3748 /* check if any connected player was not found in playfield */
3749 for (i = 0; i < MAX_PLAYERS; i++)
3751 struct PlayerInfo *player = &stored_player[i];
3753 if (player->connected && !player->present)
3755 struct PlayerInfo *field_player = NULL;
3757 #if DEBUG_INIT_PLAYER
3759 printf("- looking for field player for player %d ...\n", i + 1);
3762 /* assign first free player found that is present in the playfield */
3764 /* first try: look for unmapped playfield player that is not connected */
3765 for (j = 0; j < MAX_PLAYERS; j++)
3766 if (field_player == NULL &&
3767 stored_player[j].present &&
3768 !stored_player[j].mapped &&
3769 !stored_player[j].connected)
3770 field_player = &stored_player[j];
3772 /* second try: look for *any* unmapped playfield player */
3773 for (j = 0; j < MAX_PLAYERS; j++)
3774 if (field_player == NULL &&
3775 stored_player[j].present &&
3776 !stored_player[j].mapped)
3777 field_player = &stored_player[j];
3779 if (field_player != NULL)
3781 int jx = field_player->jx, jy = field_player->jy;
3783 #if DEBUG_INIT_PLAYER
3785 printf("- found player %d\n", field_player->index_nr + 1);
3788 player->present = FALSE;
3789 player->active = FALSE;
3791 field_player->present = TRUE;
3792 field_player->active = TRUE;
3795 player->initial_element = field_player->initial_element;
3796 player->artwork_element = field_player->artwork_element;
3798 player->block_last_field = field_player->block_last_field;
3799 player->block_delay_adjustment = field_player->block_delay_adjustment;
3802 StorePlayer[jx][jy] = field_player->element_nr;
3804 field_player->jx = field_player->last_jx = jx;
3805 field_player->jy = field_player->last_jy = jy;
3807 if (local_player == player)
3808 local_player = field_player;
3810 map_player_action[field_player->index_nr] = i;
3812 field_player->mapped = TRUE;
3814 #if DEBUG_INIT_PLAYER
3816 printf("- map_player_action[%d] == %d\n",
3817 field_player->index_nr + 1, i + 1);
3822 if (player->connected && player->present)
3823 player->mapped = TRUE;
3826 #if DEBUG_INIT_PLAYER
3827 DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3832 /* check if any connected player was not found in playfield */
3833 for (i = 0; i < MAX_PLAYERS; i++)
3835 struct PlayerInfo *player = &stored_player[i];
3837 if (player->connected && !player->present)
3839 for (j = 0; j < MAX_PLAYERS; j++)
3841 struct PlayerInfo *field_player = &stored_player[j];
3842 int jx = field_player->jx, jy = field_player->jy;
3844 /* assign first free player found that is present in the playfield */
3845 if (field_player->present && !field_player->connected)
3847 player->present = TRUE;
3848 player->active = TRUE;
3850 field_player->present = FALSE;
3851 field_player->active = FALSE;
3853 player->initial_element = field_player->initial_element;
3854 player->artwork_element = field_player->artwork_element;
3856 player->block_last_field = field_player->block_last_field;
3857 player->block_delay_adjustment = field_player->block_delay_adjustment;
3859 StorePlayer[jx][jy] = player->element_nr;
3861 player->jx = player->last_jx = jx;
3862 player->jy = player->last_jy = jy;
3872 printf("::: local_player->present == %d\n", local_player->present);
3875 /* set focus to local player for network games, else to all players */
3876 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3877 game.centered_player_nr_next = game.centered_player_nr;
3878 game.set_centered_player = FALSE;
3880 if (network_playing && tape.recording)
3882 /* store client dependent player focus when recording network games */
3883 tape.centered_player_nr_next = game.centered_player_nr_next;
3884 tape.set_centered_player = TRUE;
3889 /* when playing a tape, eliminate all players who do not participate */
3891 #if USE_NEW_PLAYER_ASSIGNMENTS
3893 if (!game.team_mode)
3895 for (i = 0; i < MAX_PLAYERS; i++)
3897 if (stored_player[i].active &&
3898 !tape.player_participates[map_player_action[i]])
3900 struct PlayerInfo *player = &stored_player[i];
3901 int jx = player->jx, jy = player->jy;
3903 #if DEBUG_INIT_PLAYER
3905 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3908 player->active = FALSE;
3909 StorePlayer[jx][jy] = 0;
3910 Feld[jx][jy] = EL_EMPTY;
3917 for (i = 0; i < MAX_PLAYERS; i++)
3919 if (stored_player[i].active &&
3920 !tape.player_participates[i])
3922 struct PlayerInfo *player = &stored_player[i];
3923 int jx = player->jx, jy = player->jy;
3925 player->active = FALSE;
3926 StorePlayer[jx][jy] = 0;
3927 Feld[jx][jy] = EL_EMPTY;
3932 else if (!network.enabled && !game.team_mode) /* && !tape.playing */
3934 /* when in single player mode, eliminate all but the local player */
3936 for (i = 0; i < MAX_PLAYERS; i++)
3938 struct PlayerInfo *player = &stored_player[i];
3940 if (player->active && player != local_player)
3942 int jx = player->jx, jy = player->jy;
3944 player->active = FALSE;
3945 player->present = FALSE;
3947 StorePlayer[jx][jy] = 0;
3948 Feld[jx][jy] = EL_EMPTY;
3953 for (i = 0; i < MAX_PLAYERS; i++)
3954 if (stored_player[i].active)
3955 local_player->players_still_needed++;
3957 /* when recording the game, store which players take part in the game */
3960 #if USE_NEW_PLAYER_ASSIGNMENTS
3961 for (i = 0; i < MAX_PLAYERS; i++)
3962 if (stored_player[i].connected)
3963 tape.player_participates[i] = TRUE;
3965 for (i = 0; i < MAX_PLAYERS; i++)
3966 if (stored_player[i].active)
3967 tape.player_participates[i] = TRUE;
3971 #if DEBUG_INIT_PLAYER
3972 DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3975 if (BorderElement == EL_EMPTY)
3978 SBX_Right = lev_fieldx - SCR_FIELDX;
3980 SBY_Lower = lev_fieldy - SCR_FIELDY;
3985 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3987 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3990 if (full_lev_fieldx <= SCR_FIELDX)
3991 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3992 if (full_lev_fieldy <= SCR_FIELDY)
3993 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3995 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3997 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4000 /* if local player not found, look for custom element that might create
4001 the player (make some assumptions about the right custom element) */
4002 if (!local_player->present)
4004 int start_x = 0, start_y = 0;
4005 int found_rating = 0;
4006 int found_element = EL_UNDEFINED;
4007 int player_nr = local_player->index_nr;
4009 SCAN_PLAYFIELD(x, y)
4011 int element = Feld[x][y];
4016 if (level.use_start_element[player_nr] &&
4017 level.start_element[player_nr] == element &&
4024 found_element = element;
4027 if (!IS_CUSTOM_ELEMENT(element))
4030 if (CAN_CHANGE(element))
4032 for (i = 0; i < element_info[element].num_change_pages; i++)
4034 /* check for player created from custom element as single target */
4035 content = element_info[element].change_page[i].target_element;
4036 is_player = ELEM_IS_PLAYER(content);
4038 if (is_player && (found_rating < 3 ||
4039 (found_rating == 3 && element < found_element)))
4045 found_element = element;
4050 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4052 /* check for player created from custom element as explosion content */
4053 content = element_info[element].content.e[xx][yy];
4054 is_player = ELEM_IS_PLAYER(content);
4056 if (is_player && (found_rating < 2 ||
4057 (found_rating == 2 && element < found_element)))
4059 start_x = x + xx - 1;
4060 start_y = y + yy - 1;
4063 found_element = element;
4066 if (!CAN_CHANGE(element))
4069 for (i = 0; i < element_info[element].num_change_pages; i++)
4071 /* check for player created from custom element as extended target */
4073 element_info[element].change_page[i].target_content.e[xx][yy];
4075 is_player = ELEM_IS_PLAYER(content);
4077 if (is_player && (found_rating < 1 ||
4078 (found_rating == 1 && element < found_element)))
4080 start_x = x + xx - 1;
4081 start_y = y + yy - 1;
4084 found_element = element;
4090 scroll_x = SCROLL_POSITION_X(start_x);
4091 scroll_y = SCROLL_POSITION_Y(start_y);
4095 scroll_x = SCROLL_POSITION_X(local_player->jx);
4096 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4099 /* !!! FIX THIS (START) !!! */
4100 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4102 InitGameEngine_EM();
4104 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4106 InitGameEngine_SP();
4108 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4110 InitGameEngine_MM();
4114 DrawLevel(REDRAW_FIELD);
4117 /* after drawing the level, correct some elements */
4118 if (game.timegate_time_left == 0)
4119 CloseAllOpenTimegates();
4122 /* blit playfield from scroll buffer to normal back buffer for fading in */
4123 BlitScreenToBitmap(backbuffer);
4124 /* !!! FIX THIS (END) !!! */
4126 DrawMaskedBorder(fade_mask);
4131 // full screen redraw is required at this point in the following cases:
4132 // - special editor door undrawn when game was started from level editor
4133 // - drawing area (playfield) was changed and has to be removed completely
4134 redraw_mask = REDRAW_ALL;
4138 if (!game.restart_level)
4140 /* copy default game door content to main double buffer */
4142 /* !!! CHECK AGAIN !!! */
4143 SetPanelBackground();
4144 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4145 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4148 SetPanelBackground();
4149 SetDrawBackgroundMask(REDRAW_DOOR_1);
4151 UpdateAndDisplayGameControlValues();
4153 if (!game.restart_level)
4159 CreateGameButtons();
4164 /* copy actual game door content to door double buffer for OpenDoor() */
4165 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4167 OpenDoor(DOOR_OPEN_ALL);
4169 KeyboardAutoRepeatOffUnlessAutoplay();
4171 #if DEBUG_INIT_PLAYER
4172 DebugPrintPlayerStatus("Player status (final)");
4181 if (!game.restart_level && !tape.playing)
4183 LevelStats_incPlayed(level_nr);
4185 SaveLevelSetup_SeriesInfo();
4188 game.restart_level = FALSE;
4189 game.restart_game_message = NULL;
4191 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4192 InitGameActions_MM();
4194 SaveEngineSnapshotToListInitial();
4196 if (!game.restart_level)
4198 PlaySound(SND_GAME_STARTING);
4200 if (setup.sound_music)
4205 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4206 int actual_player_x, int actual_player_y)
4208 /* this is used for non-R'n'D game engines to update certain engine values */
4210 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4212 actual_player_x = correctLevelPosX_EM(actual_player_x);
4213 actual_player_y = correctLevelPosY_EM(actual_player_y);
4216 /* needed to determine if sounds are played within the visible screen area */
4217 scroll_x = actual_scroll_x;
4218 scroll_y = actual_scroll_y;
4220 /* needed to get player position for "follow finger" playing input method */
4221 local_player->jx = actual_player_x;
4222 local_player->jy = actual_player_y;
4225 void InitMovDir(int x, int y)
4227 int i, element = Feld[x][y];
4228 static int xy[4][2] =
4235 static int direction[3][4] =
4237 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4238 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4239 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4248 Feld[x][y] = EL_BUG;
4249 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4252 case EL_SPACESHIP_RIGHT:
4253 case EL_SPACESHIP_UP:
4254 case EL_SPACESHIP_LEFT:
4255 case EL_SPACESHIP_DOWN:
4256 Feld[x][y] = EL_SPACESHIP;
4257 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4260 case EL_BD_BUTTERFLY_RIGHT:
4261 case EL_BD_BUTTERFLY_UP:
4262 case EL_BD_BUTTERFLY_LEFT:
4263 case EL_BD_BUTTERFLY_DOWN:
4264 Feld[x][y] = EL_BD_BUTTERFLY;
4265 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4268 case EL_BD_FIREFLY_RIGHT:
4269 case EL_BD_FIREFLY_UP:
4270 case EL_BD_FIREFLY_LEFT:
4271 case EL_BD_FIREFLY_DOWN:
4272 Feld[x][y] = EL_BD_FIREFLY;
4273 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4276 case EL_PACMAN_RIGHT:
4278 case EL_PACMAN_LEFT:
4279 case EL_PACMAN_DOWN:
4280 Feld[x][y] = EL_PACMAN;
4281 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4284 case EL_YAMYAM_LEFT:
4285 case EL_YAMYAM_RIGHT:
4287 case EL_YAMYAM_DOWN:
4288 Feld[x][y] = EL_YAMYAM;
4289 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4292 case EL_SP_SNIKSNAK:
4293 MovDir[x][y] = MV_UP;
4296 case EL_SP_ELECTRON:
4297 MovDir[x][y] = MV_LEFT;
4304 Feld[x][y] = EL_MOLE;
4305 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4309 if (IS_CUSTOM_ELEMENT(element))
4311 struct ElementInfo *ei = &element_info[element];
4312 int move_direction_initial = ei->move_direction_initial;
4313 int move_pattern = ei->move_pattern;
4315 if (move_direction_initial == MV_START_PREVIOUS)
4317 if (MovDir[x][y] != MV_NONE)
4320 move_direction_initial = MV_START_AUTOMATIC;
4323 if (move_direction_initial == MV_START_RANDOM)
4324 MovDir[x][y] = 1 << RND(4);
4325 else if (move_direction_initial & MV_ANY_DIRECTION)
4326 MovDir[x][y] = move_direction_initial;
4327 else if (move_pattern == MV_ALL_DIRECTIONS ||
4328 move_pattern == MV_TURNING_LEFT ||
4329 move_pattern == MV_TURNING_RIGHT ||
4330 move_pattern == MV_TURNING_LEFT_RIGHT ||
4331 move_pattern == MV_TURNING_RIGHT_LEFT ||
4332 move_pattern == MV_TURNING_RANDOM)
4333 MovDir[x][y] = 1 << RND(4);
4334 else if (move_pattern == MV_HORIZONTAL)
4335 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4336 else if (move_pattern == MV_VERTICAL)
4337 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4338 else if (move_pattern & MV_ANY_DIRECTION)
4339 MovDir[x][y] = element_info[element].move_pattern;
4340 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4341 move_pattern == MV_ALONG_RIGHT_SIDE)
4343 /* use random direction as default start direction */
4344 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4345 MovDir[x][y] = 1 << RND(4);
4347 for (i = 0; i < NUM_DIRECTIONS; i++)
4349 int x1 = x + xy[i][0];
4350 int y1 = y + xy[i][1];
4352 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4354 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4355 MovDir[x][y] = direction[0][i];
4357 MovDir[x][y] = direction[1][i];
4366 MovDir[x][y] = 1 << RND(4);
4368 if (element != EL_BUG &&
4369 element != EL_SPACESHIP &&
4370 element != EL_BD_BUTTERFLY &&
4371 element != EL_BD_FIREFLY)
4374 for (i = 0; i < NUM_DIRECTIONS; i++)
4376 int x1 = x + xy[i][0];
4377 int y1 = y + xy[i][1];
4379 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4381 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4383 MovDir[x][y] = direction[0][i];
4386 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4387 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4389 MovDir[x][y] = direction[1][i];
4398 GfxDir[x][y] = MovDir[x][y];
4401 void InitAmoebaNr(int x, int y)
4404 int group_nr = AmoebeNachbarNr(x, y);
4408 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4410 if (AmoebaCnt[i] == 0)
4418 AmoebaNr[x][y] = group_nr;
4419 AmoebaCnt[group_nr]++;
4420 AmoebaCnt2[group_nr]++;
4423 static void PlayerWins(struct PlayerInfo *player)
4425 if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4426 local_player->players_still_needed > 0)
4429 player->LevelSolved = TRUE;
4430 player->GameOver = TRUE;
4432 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4433 level.native_em_level->lev->score :
4434 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4437 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4438 MM_HEALTH(game_mm.laser_overload_value) :
4441 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4443 player->LevelSolved_CountingScore = player->score_final;
4444 player->LevelSolved_CountingHealth = player->health_final;
4449 static int time_count_steps;
4450 static int time, time_final;
4451 static int score, score_final;
4452 static int health, health_final;
4453 static int game_over_delay_1 = 0;
4454 static int game_over_delay_2 = 0;
4455 static int game_over_delay_3 = 0;
4456 int game_over_delay_value_1 = 50;
4457 int game_over_delay_value_2 = 25;
4458 int game_over_delay_value_3 = 50;
4460 if (!local_player->LevelSolved_GameWon)
4464 /* do not start end game actions before the player stops moving (to exit) */
4465 if (local_player->MovPos)
4468 local_player->LevelSolved_GameWon = TRUE;
4469 local_player->LevelSolved_SaveTape = tape.recording;
4470 local_player->LevelSolved_SaveScore = !tape.playing;
4474 LevelStats_incSolved(level_nr);
4476 SaveLevelSetup_SeriesInfo();
4479 if (tape.auto_play) /* tape might already be stopped here */
4480 tape.auto_play_level_solved = TRUE;
4484 game_over_delay_1 = 0;
4485 game_over_delay_2 = 0;
4486 game_over_delay_3 = game_over_delay_value_3;
4488 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4489 score = score_final = local_player->score_final;
4490 health = health_final = local_player->health_final;
4492 if (level.score[SC_TIME_BONUS] > 0)
4497 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4499 else if (game.no_time_limit && TimePlayed < 999)
4502 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4505 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4507 game_over_delay_1 = game_over_delay_value_1;
4509 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4512 score_final += health * level.score[SC_TIME_BONUS];
4514 game_over_delay_2 = game_over_delay_value_2;
4517 local_player->score_final = score_final;
4518 local_player->health_final = health_final;
4521 if (level_editor_test_game)
4524 score = score_final;
4526 local_player->LevelSolved_CountingTime = time;
4527 local_player->LevelSolved_CountingScore = score;
4529 game_panel_controls[GAME_PANEL_TIME].value = time;
4530 game_panel_controls[GAME_PANEL_SCORE].value = score;
4532 DisplayGameControlValues();
4535 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4537 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4539 /* close exit door after last player */
4540 if ((AllPlayersGone &&
4541 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4542 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4543 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4544 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4545 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4547 int element = Feld[ExitX][ExitY];
4549 Feld[ExitX][ExitY] =
4550 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4551 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4552 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4553 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4554 EL_EM_STEEL_EXIT_CLOSING);
4556 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4559 /* player disappears */
4560 DrawLevelField(ExitX, ExitY);
4563 for (i = 0; i < MAX_PLAYERS; i++)
4565 struct PlayerInfo *player = &stored_player[i];
4567 if (player->present)
4569 RemovePlayer(player);
4571 /* player disappears */
4572 DrawLevelField(player->jx, player->jy);
4577 PlaySound(SND_GAME_WINNING);
4580 if (game_over_delay_1 > 0)
4582 game_over_delay_1--;
4587 if (time != time_final)
4589 int time_to_go = ABS(time_final - time);
4590 int time_count_dir = (time < time_final ? +1 : -1);
4592 if (time_to_go < time_count_steps)
4593 time_count_steps = 1;
4595 time += time_count_steps * time_count_dir;
4596 score += time_count_steps * level.score[SC_TIME_BONUS];
4598 local_player->LevelSolved_CountingTime = time;
4599 local_player->LevelSolved_CountingScore = score;
4601 game_panel_controls[GAME_PANEL_TIME].value = time;
4602 game_panel_controls[GAME_PANEL_SCORE].value = score;
4604 DisplayGameControlValues();
4606 if (time == time_final)
4607 StopSound(SND_GAME_LEVELTIME_BONUS);
4608 else if (setup.sound_loops)
4609 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4611 PlaySound(SND_GAME_LEVELTIME_BONUS);
4616 if (game_over_delay_2 > 0)
4618 game_over_delay_2--;
4623 if (health != health_final)
4625 int health_count_dir = (health < health_final ? +1 : -1);
4627 health += health_count_dir;
4628 score += level.score[SC_TIME_BONUS];
4630 local_player->LevelSolved_CountingHealth = health;
4631 local_player->LevelSolved_CountingScore = score;
4633 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4634 game_panel_controls[GAME_PANEL_SCORE].value = score;
4636 DisplayGameControlValues();
4638 if (health == health_final)
4639 StopSound(SND_GAME_LEVELTIME_BONUS);
4640 else if (setup.sound_loops)
4641 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4643 PlaySound(SND_GAME_LEVELTIME_BONUS);
4648 local_player->LevelSolved_PanelOff = TRUE;
4650 if (game_over_delay_3 > 0)
4652 game_over_delay_3--;
4663 int last_level_nr = level_nr;
4665 local_player->LevelSolved_GameEnd = TRUE;
4667 if (local_player->LevelSolved_SaveTape)
4669 /* make sure that request dialog to save tape does not open door again */
4670 if (!global.use_envelope_request)
4671 CloseDoor(DOOR_CLOSE_1);
4673 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4676 /* if no tape is to be saved, close both doors simultaneously */
4677 CloseDoor(DOOR_CLOSE_ALL);
4679 if (level_editor_test_game)
4681 SetGameStatus(GAME_MODE_MAIN);
4688 if (!local_player->LevelSolved_SaveScore)
4690 SetGameStatus(GAME_MODE_MAIN);
4697 if (level_nr == leveldir_current->handicap_level)
4699 leveldir_current->handicap_level++;
4701 SaveLevelSetup_SeriesInfo();
4704 if (setup.increment_levels &&
4705 level_nr < leveldir_current->last_level)
4707 level_nr++; /* advance to next level */
4708 TapeErase(); /* start with empty tape */
4710 if (setup.auto_play_next_level)
4712 LoadLevel(level_nr);
4714 SaveLevelSetup_SeriesInfo();
4718 hi_pos = NewHiScore(last_level_nr);
4720 if (hi_pos >= 0 && !setup.skip_scores_after_game)
4722 SetGameStatus(GAME_MODE_SCORES);
4724 DrawHallOfFame(last_level_nr, hi_pos);
4726 else if (!setup.auto_play_next_level || !setup.increment_levels)
4728 SetGameStatus(GAME_MODE_MAIN);
4734 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4738 int NewHiScore(int level_nr)
4742 boolean one_score_entry_per_name = !program.many_scores_per_name;
4744 LoadScore(level_nr);
4746 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4747 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4750 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4752 if (local_player->score_final > highscore[k].Score)
4754 /* player has made it to the hall of fame */
4756 if (k < MAX_SCORE_ENTRIES - 1)
4758 int m = MAX_SCORE_ENTRIES - 1;
4760 if (one_score_entry_per_name)
4762 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4763 if (strEqual(setup.player_name, highscore[l].Name))
4766 if (m == k) /* player's new highscore overwrites his old one */
4770 for (l = m; l > k; l--)
4772 strcpy(highscore[l].Name, highscore[l - 1].Name);
4773 highscore[l].Score = highscore[l - 1].Score;
4779 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4780 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4781 highscore[k].Score = local_player->score_final;
4786 else if (one_score_entry_per_name &&
4787 !strncmp(setup.player_name, highscore[k].Name,
4788 MAX_PLAYER_NAME_LEN))
4789 break; /* player already there with a higher score */
4793 SaveScore(level_nr);
4798 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4800 int element = Feld[x][y];
4801 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4802 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4803 int horiz_move = (dx != 0);
4804 int sign = (horiz_move ? dx : dy);
4805 int step = sign * element_info[element].move_stepsize;
4807 /* special values for move stepsize for spring and things on conveyor belt */
4810 if (CAN_FALL(element) &&
4811 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4812 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4813 else if (element == EL_SPRING)
4814 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4820 inline static int getElementMoveStepsize(int x, int y)
4822 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4825 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4827 if (player->GfxAction != action || player->GfxDir != dir)
4829 player->GfxAction = action;
4830 player->GfxDir = dir;
4832 player->StepFrame = 0;
4836 static void ResetGfxFrame(int x, int y)
4838 // profiling showed that "autotest" spends 10~20% of its time in this function
4839 if (DrawingDeactivatedField())
4842 int element = Feld[x][y];
4843 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4845 if (graphic_info[graphic].anim_global_sync)
4846 GfxFrame[x][y] = FrameCounter;
4847 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4848 GfxFrame[x][y] = CustomValue[x][y];
4849 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4850 GfxFrame[x][y] = element_info[element].collect_score;
4851 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4852 GfxFrame[x][y] = ChangeDelay[x][y];
4855 static void ResetGfxAnimation(int x, int y)
4857 GfxAction[x][y] = ACTION_DEFAULT;
4858 GfxDir[x][y] = MovDir[x][y];
4861 ResetGfxFrame(x, y);
4864 static void ResetRandomAnimationValue(int x, int y)
4866 GfxRandom[x][y] = INIT_GFX_RANDOM();
4869 void InitMovingField(int x, int y, int direction)
4871 int element = Feld[x][y];
4872 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4873 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4876 boolean is_moving_before, is_moving_after;
4878 /* check if element was/is moving or being moved before/after mode change */
4879 is_moving_before = (WasJustMoving[x][y] != 0);
4880 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4882 /* reset animation only for moving elements which change direction of moving
4883 or which just started or stopped moving
4884 (else CEs with property "can move" / "not moving" are reset each frame) */
4885 if (is_moving_before != is_moving_after ||
4886 direction != MovDir[x][y])
4887 ResetGfxAnimation(x, y);
4889 MovDir[x][y] = direction;
4890 GfxDir[x][y] = direction;
4892 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4893 direction == MV_DOWN && CAN_FALL(element) ?
4894 ACTION_FALLING : ACTION_MOVING);
4896 /* this is needed for CEs with property "can move" / "not moving" */
4898 if (is_moving_after)
4900 if (Feld[newx][newy] == EL_EMPTY)
4901 Feld[newx][newy] = EL_BLOCKED;
4903 MovDir[newx][newy] = MovDir[x][y];
4905 CustomValue[newx][newy] = CustomValue[x][y];
4907 GfxFrame[newx][newy] = GfxFrame[x][y];
4908 GfxRandom[newx][newy] = GfxRandom[x][y];
4909 GfxAction[newx][newy] = GfxAction[x][y];
4910 GfxDir[newx][newy] = GfxDir[x][y];
4914 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4916 int direction = MovDir[x][y];
4917 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4918 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4924 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4926 int oldx = x, oldy = y;
4927 int direction = MovDir[x][y];
4929 if (direction == MV_LEFT)
4931 else if (direction == MV_RIGHT)
4933 else if (direction == MV_UP)
4935 else if (direction == MV_DOWN)
4938 *comes_from_x = oldx;
4939 *comes_from_y = oldy;
4942 int MovingOrBlocked2Element(int x, int y)
4944 int element = Feld[x][y];
4946 if (element == EL_BLOCKED)
4950 Blocked2Moving(x, y, &oldx, &oldy);
4951 return Feld[oldx][oldy];
4957 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4959 /* like MovingOrBlocked2Element(), but if element is moving
4960 and (x,y) is the field the moving element is just leaving,
4961 return EL_BLOCKED instead of the element value */
4962 int element = Feld[x][y];
4964 if (IS_MOVING(x, y))
4966 if (element == EL_BLOCKED)
4970 Blocked2Moving(x, y, &oldx, &oldy);
4971 return Feld[oldx][oldy];
4980 static void RemoveField(int x, int y)
4982 Feld[x][y] = EL_EMPTY;
4988 CustomValue[x][y] = 0;
4991 ChangeDelay[x][y] = 0;
4992 ChangePage[x][y] = -1;
4993 Pushed[x][y] = FALSE;
4995 GfxElement[x][y] = EL_UNDEFINED;
4996 GfxAction[x][y] = ACTION_DEFAULT;
4997 GfxDir[x][y] = MV_NONE;
5000 void RemoveMovingField(int x, int y)
5002 int oldx = x, oldy = y, newx = x, newy = y;
5003 int element = Feld[x][y];
5004 int next_element = EL_UNDEFINED;
5006 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5009 if (IS_MOVING(x, y))
5011 Moving2Blocked(x, y, &newx, &newy);
5013 if (Feld[newx][newy] != EL_BLOCKED)
5015 /* element is moving, but target field is not free (blocked), but
5016 already occupied by something different (example: acid pool);
5017 in this case, only remove the moving field, but not the target */
5019 RemoveField(oldx, oldy);
5021 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5023 TEST_DrawLevelField(oldx, oldy);
5028 else if (element == EL_BLOCKED)
5030 Blocked2Moving(x, y, &oldx, &oldy);
5031 if (!IS_MOVING(oldx, oldy))
5035 if (element == EL_BLOCKED &&
5036 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5037 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5038 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5039 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5040 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5041 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5042 next_element = get_next_element(Feld[oldx][oldy]);
5044 RemoveField(oldx, oldy);
5045 RemoveField(newx, newy);
5047 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5049 if (next_element != EL_UNDEFINED)
5050 Feld[oldx][oldy] = next_element;
5052 TEST_DrawLevelField(oldx, oldy);
5053 TEST_DrawLevelField(newx, newy);
5056 void DrawDynamite(int x, int y)
5058 int sx = SCREENX(x), sy = SCREENY(y);
5059 int graphic = el2img(Feld[x][y]);
5062 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5065 if (IS_WALKABLE_INSIDE(Back[x][y]))
5069 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5070 else if (Store[x][y])
5071 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5073 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5075 if (Back[x][y] || Store[x][y])
5076 DrawGraphicThruMask(sx, sy, graphic, frame);
5078 DrawGraphic(sx, sy, graphic, frame);
5081 void CheckDynamite(int x, int y)
5083 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5087 if (MovDelay[x][y] != 0)
5090 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5096 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5101 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5103 boolean num_checked_players = 0;
5106 for (i = 0; i < MAX_PLAYERS; i++)
5108 if (stored_player[i].active)
5110 int sx = stored_player[i].jx;
5111 int sy = stored_player[i].jy;
5113 if (num_checked_players == 0)
5120 *sx1 = MIN(*sx1, sx);
5121 *sy1 = MIN(*sy1, sy);
5122 *sx2 = MAX(*sx2, sx);
5123 *sy2 = MAX(*sy2, sy);
5126 num_checked_players++;
5131 static boolean checkIfAllPlayersFitToScreen_RND()
5133 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5135 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5137 return (sx2 - sx1 < SCR_FIELDX &&
5138 sy2 - sy1 < SCR_FIELDY);
5141 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5143 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5145 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5147 *sx = (sx1 + sx2) / 2;
5148 *sy = (sy1 + sy2) / 2;
5151 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5152 boolean center_screen, boolean quick_relocation)
5154 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5155 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5156 boolean no_delay = (tape.warp_forward);
5157 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5158 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5159 int new_scroll_x, new_scroll_y;
5161 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5163 /* case 1: quick relocation inside visible screen (without scrolling) */
5170 if (!level.shifted_relocation || center_screen)
5172 /* relocation _with_ centering of screen */
5174 new_scroll_x = SCROLL_POSITION_X(x);
5175 new_scroll_y = SCROLL_POSITION_Y(y);
5179 /* relocation _without_ centering of screen */
5181 int center_scroll_x = SCROLL_POSITION_X(old_x);
5182 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5183 int offset_x = x + (scroll_x - center_scroll_x);
5184 int offset_y = y + (scroll_y - center_scroll_y);
5186 /* for new screen position, apply previous offset to center position */
5187 new_scroll_x = SCROLL_POSITION_X(offset_x);
5188 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5191 if (quick_relocation)
5193 /* case 2: quick relocation (redraw without visible scrolling) */
5195 scroll_x = new_scroll_x;
5196 scroll_y = new_scroll_y;
5203 /* case 3: visible relocation (with scrolling to new position) */
5205 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5207 SetVideoFrameDelay(wait_delay_value);
5209 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5212 int fx = FX, fy = FY;
5214 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5215 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5217 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5223 fx += dx * TILEX / 2;
5224 fy += dy * TILEY / 2;
5226 ScrollLevel(dx, dy);
5229 /* scroll in two steps of half tile size to make things smoother */
5230 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5232 /* scroll second step to align at full tile size */
5233 BlitScreenToBitmap(window);
5239 SetVideoFrameDelay(frame_delay_value_old);
5242 void RelocatePlayer(int jx, int jy, int el_player_raw)
5244 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5245 int player_nr = GET_PLAYER_NR(el_player);
5246 struct PlayerInfo *player = &stored_player[player_nr];
5247 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5248 boolean no_delay = (tape.warp_forward);
5249 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5250 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5251 int old_jx = player->jx;
5252 int old_jy = player->jy;
5253 int old_element = Feld[old_jx][old_jy];
5254 int element = Feld[jx][jy];
5255 boolean player_relocated = (old_jx != jx || old_jy != jy);
5257 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5258 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5259 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5260 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5261 int leave_side_horiz = move_dir_horiz;
5262 int leave_side_vert = move_dir_vert;
5263 int enter_side = enter_side_horiz | enter_side_vert;
5264 int leave_side = leave_side_horiz | leave_side_vert;
5266 if (player->GameOver) /* do not reanimate dead player */
5269 if (!player_relocated) /* no need to relocate the player */
5272 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5274 RemoveField(jx, jy); /* temporarily remove newly placed player */
5275 DrawLevelField(jx, jy);
5278 if (player->present)
5280 while (player->MovPos)
5282 ScrollPlayer(player, SCROLL_GO_ON);
5283 ScrollScreen(NULL, SCROLL_GO_ON);
5285 AdvanceFrameAndPlayerCounters(player->index_nr);
5289 BackToFront_WithFrameDelay(wait_delay_value);
5292 DrawPlayer(player); /* needed here only to cleanup last field */
5293 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5295 player->is_moving = FALSE;
5298 if (IS_CUSTOM_ELEMENT(old_element))
5299 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5301 player->index_bit, leave_side);
5303 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5305 player->index_bit, leave_side);
5307 Feld[jx][jy] = el_player;
5308 InitPlayerField(jx, jy, el_player, TRUE);
5310 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5311 possible that the relocation target field did not contain a player element,
5312 but a walkable element, to which the new player was relocated -- in this
5313 case, restore that (already initialized!) element on the player field */
5314 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5316 Feld[jx][jy] = element; /* restore previously existing element */
5319 /* only visually relocate centered player */
5320 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5321 FALSE, level.instant_relocation);
5323 TestIfPlayerTouchesBadThing(jx, jy);
5324 TestIfPlayerTouchesCustomElement(jx, jy);
5326 if (IS_CUSTOM_ELEMENT(element))
5327 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5328 player->index_bit, enter_side);
5330 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5331 player->index_bit, enter_side);
5333 if (player->is_switching)
5335 /* ensure that relocation while still switching an element does not cause
5336 a new element to be treated as also switched directly after relocation
5337 (this is important for teleporter switches that teleport the player to
5338 a place where another teleporter switch is in the same direction, which
5339 would then incorrectly be treated as immediately switched before the
5340 direction key that caused the switch was released) */
5342 player->switch_x += jx - old_jx;
5343 player->switch_y += jy - old_jy;
5347 void Explode(int ex, int ey, int phase, int mode)
5353 /* !!! eliminate this variable !!! */
5354 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5356 if (game.explosions_delayed)
5358 ExplodeField[ex][ey] = mode;
5362 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5364 int center_element = Feld[ex][ey];
5365 int artwork_element, explosion_element; /* set these values later */
5367 /* remove things displayed in background while burning dynamite */
5368 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5371 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5373 /* put moving element to center field (and let it explode there) */
5374 center_element = MovingOrBlocked2Element(ex, ey);
5375 RemoveMovingField(ex, ey);
5376 Feld[ex][ey] = center_element;
5379 /* now "center_element" is finally determined -- set related values now */
5380 artwork_element = center_element; /* for custom player artwork */
5381 explosion_element = center_element; /* for custom player artwork */
5383 if (IS_PLAYER(ex, ey))
5385 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5387 artwork_element = stored_player[player_nr].artwork_element;
5389 if (level.use_explosion_element[player_nr])
5391 explosion_element = level.explosion_element[player_nr];
5392 artwork_element = explosion_element;
5396 if (mode == EX_TYPE_NORMAL ||
5397 mode == EX_TYPE_CENTER ||
5398 mode == EX_TYPE_CROSS)
5399 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5401 last_phase = element_info[explosion_element].explosion_delay + 1;
5403 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5405 int xx = x - ex + 1;
5406 int yy = y - ey + 1;
5409 if (!IN_LEV_FIELD(x, y) ||
5410 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5411 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5414 element = Feld[x][y];
5416 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5418 element = MovingOrBlocked2Element(x, y);
5420 if (!IS_EXPLOSION_PROOF(element))
5421 RemoveMovingField(x, y);
5424 /* indestructible elements can only explode in center (but not flames) */
5425 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5426 mode == EX_TYPE_BORDER)) ||
5427 element == EL_FLAMES)
5430 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5431 behaviour, for example when touching a yamyam that explodes to rocks
5432 with active deadly shield, a rock is created under the player !!! */
5433 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5435 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5436 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5437 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5439 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5442 if (IS_ACTIVE_BOMB(element))
5444 /* re-activate things under the bomb like gate or penguin */
5445 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5452 /* save walkable background elements while explosion on same tile */
5453 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5454 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5455 Back[x][y] = element;
5457 /* ignite explodable elements reached by other explosion */
5458 if (element == EL_EXPLOSION)
5459 element = Store2[x][y];
5461 if (AmoebaNr[x][y] &&
5462 (element == EL_AMOEBA_FULL ||
5463 element == EL_BD_AMOEBA ||
5464 element == EL_AMOEBA_GROWING))
5466 AmoebaCnt[AmoebaNr[x][y]]--;
5467 AmoebaCnt2[AmoebaNr[x][y]]--;
5472 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5474 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5476 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5478 if (PLAYERINFO(ex, ey)->use_murphy)
5479 Store[x][y] = EL_EMPTY;
5482 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5483 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5484 else if (ELEM_IS_PLAYER(center_element))
5485 Store[x][y] = EL_EMPTY;
5486 else if (center_element == EL_YAMYAM)
5487 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5488 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5489 Store[x][y] = element_info[center_element].content.e[xx][yy];
5491 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5492 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5493 otherwise) -- FIX THIS !!! */
5494 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5495 Store[x][y] = element_info[element].content.e[1][1];
5497 else if (!CAN_EXPLODE(element))
5498 Store[x][y] = element_info[element].content.e[1][1];
5501 Store[x][y] = EL_EMPTY;
5503 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5504 center_element == EL_AMOEBA_TO_DIAMOND)
5505 Store2[x][y] = element;
5507 Feld[x][y] = EL_EXPLOSION;
5508 GfxElement[x][y] = artwork_element;
5510 ExplodePhase[x][y] = 1;
5511 ExplodeDelay[x][y] = last_phase;
5516 if (center_element == EL_YAMYAM)
5517 game.yamyam_content_nr =
5518 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5530 GfxFrame[x][y] = 0; /* restart explosion animation */
5532 last_phase = ExplodeDelay[x][y];
5534 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5536 /* this can happen if the player leaves an explosion just in time */
5537 if (GfxElement[x][y] == EL_UNDEFINED)
5538 GfxElement[x][y] = EL_EMPTY;
5540 border_element = Store2[x][y];
5541 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5542 border_element = StorePlayer[x][y];
5544 if (phase == element_info[border_element].ignition_delay ||
5545 phase == last_phase)
5547 boolean border_explosion = FALSE;
5549 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5550 !PLAYER_EXPLOSION_PROTECTED(x, y))
5552 KillPlayerUnlessExplosionProtected(x, y);
5553 border_explosion = TRUE;
5555 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5557 Feld[x][y] = Store2[x][y];
5560 border_explosion = TRUE;
5562 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5564 AmoebeUmwandeln(x, y);
5566 border_explosion = TRUE;
5569 /* if an element just explodes due to another explosion (chain-reaction),
5570 do not immediately end the new explosion when it was the last frame of
5571 the explosion (as it would be done in the following "if"-statement!) */
5572 if (border_explosion && phase == last_phase)
5576 if (phase == last_phase)
5580 element = Feld[x][y] = Store[x][y];
5581 Store[x][y] = Store2[x][y] = 0;
5582 GfxElement[x][y] = EL_UNDEFINED;
5584 /* player can escape from explosions and might therefore be still alive */
5585 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5586 element <= EL_PLAYER_IS_EXPLODING_4)
5588 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5589 int explosion_element = EL_PLAYER_1 + player_nr;
5590 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5591 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5593 if (level.use_explosion_element[player_nr])
5594 explosion_element = level.explosion_element[player_nr];
5596 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5597 element_info[explosion_element].content.e[xx][yy]);
5600 /* restore probably existing indestructible background element */
5601 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5602 element = Feld[x][y] = Back[x][y];
5605 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5606 GfxDir[x][y] = MV_NONE;
5607 ChangeDelay[x][y] = 0;
5608 ChangePage[x][y] = -1;
5610 CustomValue[x][y] = 0;
5612 InitField_WithBug2(x, y, FALSE);
5614 TEST_DrawLevelField(x, y);
5616 TestIfElementTouchesCustomElement(x, y);
5618 if (GFX_CRUMBLED(element))
5619 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5621 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5622 StorePlayer[x][y] = 0;
5624 if (ELEM_IS_PLAYER(element))
5625 RelocatePlayer(x, y, element);
5627 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5629 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5630 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5633 TEST_DrawLevelFieldCrumbled(x, y);
5635 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5637 DrawLevelElement(x, y, Back[x][y]);
5638 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5640 else if (IS_WALKABLE_UNDER(Back[x][y]))
5642 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5643 DrawLevelElementThruMask(x, y, Back[x][y]);
5645 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5646 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5650 void DynaExplode(int ex, int ey)
5653 int dynabomb_element = Feld[ex][ey];
5654 int dynabomb_size = 1;
5655 boolean dynabomb_xl = FALSE;
5656 struct PlayerInfo *player;
5657 static int xy[4][2] =
5665 if (IS_ACTIVE_BOMB(dynabomb_element))
5667 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5668 dynabomb_size = player->dynabomb_size;
5669 dynabomb_xl = player->dynabomb_xl;
5670 player->dynabombs_left++;
5673 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5675 for (i = 0; i < NUM_DIRECTIONS; i++)
5677 for (j = 1; j <= dynabomb_size; j++)
5679 int x = ex + j * xy[i][0];
5680 int y = ey + j * xy[i][1];
5683 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5686 element = Feld[x][y];
5688 /* do not restart explosions of fields with active bombs */
5689 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5692 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5694 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5695 !IS_DIGGABLE(element) && !dynabomb_xl)
5701 void Bang(int x, int y)
5703 int element = MovingOrBlocked2Element(x, y);
5704 int explosion_type = EX_TYPE_NORMAL;
5706 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5708 struct PlayerInfo *player = PLAYERINFO(x, y);
5710 element = Feld[x][y] = player->initial_element;
5712 if (level.use_explosion_element[player->index_nr])
5714 int explosion_element = level.explosion_element[player->index_nr];
5716 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5717 explosion_type = EX_TYPE_CROSS;
5718 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5719 explosion_type = EX_TYPE_CENTER;
5727 case EL_BD_BUTTERFLY:
5730 case EL_DARK_YAMYAM:
5734 RaiseScoreElement(element);
5737 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5738 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5739 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5740 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5741 case EL_DYNABOMB_INCREASE_NUMBER:
5742 case EL_DYNABOMB_INCREASE_SIZE:
5743 case EL_DYNABOMB_INCREASE_POWER:
5744 explosion_type = EX_TYPE_DYNA;
5747 case EL_DC_LANDMINE:
5748 explosion_type = EX_TYPE_CENTER;
5753 case EL_LAMP_ACTIVE:
5754 case EL_AMOEBA_TO_DIAMOND:
5755 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5756 explosion_type = EX_TYPE_CENTER;
5760 if (element_info[element].explosion_type == EXPLODES_CROSS)
5761 explosion_type = EX_TYPE_CROSS;
5762 else if (element_info[element].explosion_type == EXPLODES_1X1)
5763 explosion_type = EX_TYPE_CENTER;
5767 if (explosion_type == EX_TYPE_DYNA)
5770 Explode(x, y, EX_PHASE_START, explosion_type);
5772 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5775 void SplashAcid(int x, int y)
5777 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5778 (!IN_LEV_FIELD(x - 1, y - 2) ||
5779 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5780 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5782 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5783 (!IN_LEV_FIELD(x + 1, y - 2) ||
5784 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5785 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5787 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5790 static void InitBeltMovement()
5792 static int belt_base_element[4] =
5794 EL_CONVEYOR_BELT_1_LEFT,
5795 EL_CONVEYOR_BELT_2_LEFT,
5796 EL_CONVEYOR_BELT_3_LEFT,
5797 EL_CONVEYOR_BELT_4_LEFT
5799 static int belt_base_active_element[4] =
5801 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5802 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5803 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5804 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5809 /* set frame order for belt animation graphic according to belt direction */
5810 for (i = 0; i < NUM_BELTS; i++)
5814 for (j = 0; j < NUM_BELT_PARTS; j++)
5816 int element = belt_base_active_element[belt_nr] + j;
5817 int graphic_1 = el2img(element);
5818 int graphic_2 = el2panelimg(element);
5820 if (game.belt_dir[i] == MV_LEFT)
5822 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5823 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5827 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5828 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5833 SCAN_PLAYFIELD(x, y)
5835 int element = Feld[x][y];
5837 for (i = 0; i < NUM_BELTS; i++)
5839 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5841 int e_belt_nr = getBeltNrFromBeltElement(element);
5844 if (e_belt_nr == belt_nr)
5846 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5848 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5855 static void ToggleBeltSwitch(int x, int y)
5857 static int belt_base_element[4] =
5859 EL_CONVEYOR_BELT_1_LEFT,
5860 EL_CONVEYOR_BELT_2_LEFT,
5861 EL_CONVEYOR_BELT_3_LEFT,
5862 EL_CONVEYOR_BELT_4_LEFT
5864 static int belt_base_active_element[4] =
5866 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5867 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5868 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5869 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5871 static int belt_base_switch_element[4] =
5873 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5874 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5875 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5876 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5878 static int belt_move_dir[4] =
5886 int element = Feld[x][y];
5887 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5888 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5889 int belt_dir = belt_move_dir[belt_dir_nr];
5892 if (!IS_BELT_SWITCH(element))
5895 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5896 game.belt_dir[belt_nr] = belt_dir;
5898 if (belt_dir_nr == 3)
5901 /* set frame order for belt animation graphic according to belt direction */
5902 for (i = 0; i < NUM_BELT_PARTS; i++)
5904 int element = belt_base_active_element[belt_nr] + i;
5905 int graphic_1 = el2img(element);
5906 int graphic_2 = el2panelimg(element);
5908 if (belt_dir == MV_LEFT)
5910 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5911 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5915 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5916 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5920 SCAN_PLAYFIELD(xx, yy)
5922 int element = Feld[xx][yy];
5924 if (IS_BELT_SWITCH(element))
5926 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5928 if (e_belt_nr == belt_nr)
5930 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5931 TEST_DrawLevelField(xx, yy);
5934 else if (IS_BELT(element) && belt_dir != MV_NONE)
5936 int e_belt_nr = getBeltNrFromBeltElement(element);
5938 if (e_belt_nr == belt_nr)
5940 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5942 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5943 TEST_DrawLevelField(xx, yy);
5946 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5948 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5950 if (e_belt_nr == belt_nr)
5952 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5954 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5955 TEST_DrawLevelField(xx, yy);
5961 static void ToggleSwitchgateSwitch(int x, int y)
5965 game.switchgate_pos = !game.switchgate_pos;
5967 SCAN_PLAYFIELD(xx, yy)
5969 int element = Feld[xx][yy];
5971 if (element == EL_SWITCHGATE_SWITCH_UP)
5973 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5974 TEST_DrawLevelField(xx, yy);
5976 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5978 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5979 TEST_DrawLevelField(xx, yy);
5981 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5983 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5984 TEST_DrawLevelField(xx, yy);
5986 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5988 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5989 TEST_DrawLevelField(xx, yy);
5991 else if (element == EL_SWITCHGATE_OPEN ||
5992 element == EL_SWITCHGATE_OPENING)
5994 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5996 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5998 else if (element == EL_SWITCHGATE_CLOSED ||
5999 element == EL_SWITCHGATE_CLOSING)
6001 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6003 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6008 static int getInvisibleActiveFromInvisibleElement(int element)
6010 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6011 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6012 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6016 static int getInvisibleFromInvisibleActiveElement(int element)
6018 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6019 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6020 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6024 static void RedrawAllLightSwitchesAndInvisibleElements()
6028 SCAN_PLAYFIELD(x, y)
6030 int element = Feld[x][y];
6032 if (element == EL_LIGHT_SWITCH &&
6033 game.light_time_left > 0)
6035 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6036 TEST_DrawLevelField(x, y);
6038 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6039 game.light_time_left == 0)
6041 Feld[x][y] = EL_LIGHT_SWITCH;
6042 TEST_DrawLevelField(x, y);
6044 else if (element == EL_EMC_DRIPPER &&
6045 game.light_time_left > 0)
6047 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6048 TEST_DrawLevelField(x, y);
6050 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6051 game.light_time_left == 0)
6053 Feld[x][y] = EL_EMC_DRIPPER;
6054 TEST_DrawLevelField(x, y);
6056 else if (element == EL_INVISIBLE_STEELWALL ||
6057 element == EL_INVISIBLE_WALL ||
6058 element == EL_INVISIBLE_SAND)
6060 if (game.light_time_left > 0)
6061 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6063 TEST_DrawLevelField(x, y);
6065 /* uncrumble neighbour fields, if needed */
6066 if (element == EL_INVISIBLE_SAND)
6067 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6069 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6070 element == EL_INVISIBLE_WALL_ACTIVE ||
6071 element == EL_INVISIBLE_SAND_ACTIVE)
6073 if (game.light_time_left == 0)
6074 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6076 TEST_DrawLevelField(x, y);
6078 /* re-crumble neighbour fields, if needed */
6079 if (element == EL_INVISIBLE_SAND)
6080 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6085 static void RedrawAllInvisibleElementsForLenses()
6089 SCAN_PLAYFIELD(x, y)
6091 int element = Feld[x][y];
6093 if (element == EL_EMC_DRIPPER &&
6094 game.lenses_time_left > 0)
6096 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6097 TEST_DrawLevelField(x, y);
6099 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6100 game.lenses_time_left == 0)
6102 Feld[x][y] = EL_EMC_DRIPPER;
6103 TEST_DrawLevelField(x, y);
6105 else if (element == EL_INVISIBLE_STEELWALL ||
6106 element == EL_INVISIBLE_WALL ||
6107 element == EL_INVISIBLE_SAND)
6109 if (game.lenses_time_left > 0)
6110 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6112 TEST_DrawLevelField(x, y);
6114 /* uncrumble neighbour fields, if needed */
6115 if (element == EL_INVISIBLE_SAND)
6116 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6118 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6119 element == EL_INVISIBLE_WALL_ACTIVE ||
6120 element == EL_INVISIBLE_SAND_ACTIVE)
6122 if (game.lenses_time_left == 0)
6123 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6125 TEST_DrawLevelField(x, y);
6127 /* re-crumble neighbour fields, if needed */
6128 if (element == EL_INVISIBLE_SAND)
6129 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6134 static void RedrawAllInvisibleElementsForMagnifier()
6138 SCAN_PLAYFIELD(x, y)
6140 int element = Feld[x][y];
6142 if (element == EL_EMC_FAKE_GRASS &&
6143 game.magnify_time_left > 0)
6145 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6146 TEST_DrawLevelField(x, y);
6148 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6149 game.magnify_time_left == 0)
6151 Feld[x][y] = EL_EMC_FAKE_GRASS;
6152 TEST_DrawLevelField(x, y);
6154 else if (IS_GATE_GRAY(element) &&
6155 game.magnify_time_left > 0)
6157 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6158 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6159 IS_EM_GATE_GRAY(element) ?
6160 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6161 IS_EMC_GATE_GRAY(element) ?
6162 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6163 IS_DC_GATE_GRAY(element) ?
6164 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6166 TEST_DrawLevelField(x, y);
6168 else if (IS_GATE_GRAY_ACTIVE(element) &&
6169 game.magnify_time_left == 0)
6171 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6172 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6173 IS_EM_GATE_GRAY_ACTIVE(element) ?
6174 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6175 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6176 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6177 IS_DC_GATE_GRAY_ACTIVE(element) ?
6178 EL_DC_GATE_WHITE_GRAY :
6180 TEST_DrawLevelField(x, y);
6185 static void ToggleLightSwitch(int x, int y)
6187 int element = Feld[x][y];
6189 game.light_time_left =
6190 (element == EL_LIGHT_SWITCH ?
6191 level.time_light * FRAMES_PER_SECOND : 0);
6193 RedrawAllLightSwitchesAndInvisibleElements();
6196 static void ActivateTimegateSwitch(int x, int y)
6200 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6202 SCAN_PLAYFIELD(xx, yy)
6204 int element = Feld[xx][yy];
6206 if (element == EL_TIMEGATE_CLOSED ||
6207 element == EL_TIMEGATE_CLOSING)
6209 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6210 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6214 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6216 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6217 TEST_DrawLevelField(xx, yy);
6223 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6224 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6227 void Impact(int x, int y)
6229 boolean last_line = (y == lev_fieldy - 1);
6230 boolean object_hit = FALSE;
6231 boolean impact = (last_line || object_hit);
6232 int element = Feld[x][y];
6233 int smashed = EL_STEELWALL;
6235 if (!last_line) /* check if element below was hit */
6237 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6240 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6241 MovDir[x][y + 1] != MV_DOWN ||
6242 MovPos[x][y + 1] <= TILEY / 2));
6244 /* do not smash moving elements that left the smashed field in time */
6245 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6246 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6249 #if USE_QUICKSAND_IMPACT_BUGFIX
6250 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6252 RemoveMovingField(x, y + 1);
6253 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6254 Feld[x][y + 2] = EL_ROCK;
6255 TEST_DrawLevelField(x, y + 2);
6260 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6262 RemoveMovingField(x, y + 1);
6263 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6264 Feld[x][y + 2] = EL_ROCK;
6265 TEST_DrawLevelField(x, y + 2);
6272 smashed = MovingOrBlocked2Element(x, y + 1);
6274 impact = (last_line || object_hit);
6277 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6279 SplashAcid(x, y + 1);
6283 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6284 /* only reset graphic animation if graphic really changes after impact */
6286 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6288 ResetGfxAnimation(x, y);
6289 TEST_DrawLevelField(x, y);
6292 if (impact && CAN_EXPLODE_IMPACT(element))
6297 else if (impact && element == EL_PEARL &&
6298 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6300 ResetGfxAnimation(x, y);
6302 Feld[x][y] = EL_PEARL_BREAKING;
6303 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6306 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6308 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6313 if (impact && element == EL_AMOEBA_DROP)
6315 if (object_hit && IS_PLAYER(x, y + 1))
6316 KillPlayerUnlessEnemyProtected(x, y + 1);
6317 else if (object_hit && smashed == EL_PENGUIN)
6321 Feld[x][y] = EL_AMOEBA_GROWING;
6322 Store[x][y] = EL_AMOEBA_WET;
6324 ResetRandomAnimationValue(x, y);
6329 if (object_hit) /* check which object was hit */
6331 if ((CAN_PASS_MAGIC_WALL(element) &&
6332 (smashed == EL_MAGIC_WALL ||
6333 smashed == EL_BD_MAGIC_WALL)) ||
6334 (CAN_PASS_DC_MAGIC_WALL(element) &&
6335 smashed == EL_DC_MAGIC_WALL))
6338 int activated_magic_wall =
6339 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6340 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6341 EL_DC_MAGIC_WALL_ACTIVE);
6343 /* activate magic wall / mill */
6344 SCAN_PLAYFIELD(xx, yy)
6346 if (Feld[xx][yy] == smashed)
6347 Feld[xx][yy] = activated_magic_wall;
6350 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6351 game.magic_wall_active = TRUE;
6353 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6354 SND_MAGIC_WALL_ACTIVATING :
6355 smashed == EL_BD_MAGIC_WALL ?
6356 SND_BD_MAGIC_WALL_ACTIVATING :
6357 SND_DC_MAGIC_WALL_ACTIVATING));
6360 if (IS_PLAYER(x, y + 1))
6362 if (CAN_SMASH_PLAYER(element))
6364 KillPlayerUnlessEnemyProtected(x, y + 1);
6368 else if (smashed == EL_PENGUIN)
6370 if (CAN_SMASH_PLAYER(element))
6376 else if (element == EL_BD_DIAMOND)
6378 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6384 else if (((element == EL_SP_INFOTRON ||
6385 element == EL_SP_ZONK) &&
6386 (smashed == EL_SP_SNIKSNAK ||
6387 smashed == EL_SP_ELECTRON ||
6388 smashed == EL_SP_DISK_ORANGE)) ||
6389 (element == EL_SP_INFOTRON &&
6390 smashed == EL_SP_DISK_YELLOW))
6395 else if (CAN_SMASH_EVERYTHING(element))
6397 if (IS_CLASSIC_ENEMY(smashed) ||
6398 CAN_EXPLODE_SMASHED(smashed))
6403 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6405 if (smashed == EL_LAMP ||
6406 smashed == EL_LAMP_ACTIVE)
6411 else if (smashed == EL_NUT)
6413 Feld[x][y + 1] = EL_NUT_BREAKING;
6414 PlayLevelSound(x, y, SND_NUT_BREAKING);
6415 RaiseScoreElement(EL_NUT);
6418 else if (smashed == EL_PEARL)
6420 ResetGfxAnimation(x, y);
6422 Feld[x][y + 1] = EL_PEARL_BREAKING;
6423 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6426 else if (smashed == EL_DIAMOND)
6428 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6429 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6432 else if (IS_BELT_SWITCH(smashed))
6434 ToggleBeltSwitch(x, y + 1);
6436 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6437 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6438 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6439 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6441 ToggleSwitchgateSwitch(x, y + 1);
6443 else if (smashed == EL_LIGHT_SWITCH ||
6444 smashed == EL_LIGHT_SWITCH_ACTIVE)
6446 ToggleLightSwitch(x, y + 1);
6450 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6452 CheckElementChangeBySide(x, y + 1, smashed, element,
6453 CE_SWITCHED, CH_SIDE_TOP);
6454 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6460 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6465 /* play sound of magic wall / mill */
6467 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6468 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6469 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6471 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6472 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6473 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6474 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6475 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6476 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6481 /* play sound of object that hits the ground */
6482 if (last_line || object_hit)
6483 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6486 inline static void TurnRoundExt(int x, int y)
6498 { 0, 0 }, { 0, 0 }, { 0, 0 },
6503 int left, right, back;
6507 { MV_DOWN, MV_UP, MV_RIGHT },
6508 { MV_UP, MV_DOWN, MV_LEFT },
6510 { MV_LEFT, MV_RIGHT, MV_DOWN },
6514 { MV_RIGHT, MV_LEFT, MV_UP }
6517 int element = Feld[x][y];
6518 int move_pattern = element_info[element].move_pattern;
6520 int old_move_dir = MovDir[x][y];
6521 int left_dir = turn[old_move_dir].left;
6522 int right_dir = turn[old_move_dir].right;
6523 int back_dir = turn[old_move_dir].back;
6525 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6526 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6527 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6528 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6530 int left_x = x + left_dx, left_y = y + left_dy;
6531 int right_x = x + right_dx, right_y = y + right_dy;
6532 int move_x = x + move_dx, move_y = y + move_dy;
6536 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6538 TestIfBadThingTouchesOtherBadThing(x, y);
6540 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6541 MovDir[x][y] = right_dir;
6542 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6543 MovDir[x][y] = left_dir;
6545 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6547 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6550 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6552 TestIfBadThingTouchesOtherBadThing(x, y);
6554 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6555 MovDir[x][y] = left_dir;
6556 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6557 MovDir[x][y] = right_dir;
6559 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6561 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6564 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6566 TestIfBadThingTouchesOtherBadThing(x, y);
6568 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6569 MovDir[x][y] = left_dir;
6570 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6571 MovDir[x][y] = right_dir;
6573 if (MovDir[x][y] != old_move_dir)
6576 else if (element == EL_YAMYAM)
6578 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6579 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6581 if (can_turn_left && can_turn_right)
6582 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6583 else if (can_turn_left)
6584 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6585 else if (can_turn_right)
6586 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6588 MovDir[x][y] = back_dir;
6590 MovDelay[x][y] = 16 + 16 * RND(3);
6592 else if (element == EL_DARK_YAMYAM)
6594 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6596 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6599 if (can_turn_left && can_turn_right)
6600 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6601 else if (can_turn_left)
6602 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6603 else if (can_turn_right)
6604 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6606 MovDir[x][y] = back_dir;
6608 MovDelay[x][y] = 16 + 16 * RND(3);
6610 else if (element == EL_PACMAN)
6612 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6613 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6615 if (can_turn_left && can_turn_right)
6616 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6617 else if (can_turn_left)
6618 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6619 else if (can_turn_right)
6620 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6622 MovDir[x][y] = back_dir;
6624 MovDelay[x][y] = 6 + RND(40);
6626 else if (element == EL_PIG)
6628 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6629 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6630 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6631 boolean should_turn_left, should_turn_right, should_move_on;
6633 int rnd = RND(rnd_value);
6635 should_turn_left = (can_turn_left &&
6637 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6638 y + back_dy + left_dy)));
6639 should_turn_right = (can_turn_right &&
6641 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6642 y + back_dy + right_dy)));
6643 should_move_on = (can_move_on &&
6646 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6647 y + move_dy + left_dy) ||
6648 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6649 y + move_dy + right_dy)));
6651 if (should_turn_left || should_turn_right || should_move_on)
6653 if (should_turn_left && should_turn_right && should_move_on)
6654 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6655 rnd < 2 * rnd_value / 3 ? right_dir :
6657 else if (should_turn_left && should_turn_right)
6658 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6659 else if (should_turn_left && should_move_on)
6660 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6661 else if (should_turn_right && should_move_on)
6662 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6663 else if (should_turn_left)
6664 MovDir[x][y] = left_dir;
6665 else if (should_turn_right)
6666 MovDir[x][y] = right_dir;
6667 else if (should_move_on)
6668 MovDir[x][y] = old_move_dir;
6670 else if (can_move_on && rnd > rnd_value / 8)
6671 MovDir[x][y] = old_move_dir;
6672 else if (can_turn_left && can_turn_right)
6673 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6674 else if (can_turn_left && rnd > rnd_value / 8)
6675 MovDir[x][y] = left_dir;
6676 else if (can_turn_right && rnd > rnd_value/8)
6677 MovDir[x][y] = right_dir;
6679 MovDir[x][y] = back_dir;
6681 xx = x + move_xy[MovDir[x][y]].dx;
6682 yy = y + move_xy[MovDir[x][y]].dy;
6684 if (!IN_LEV_FIELD(xx, yy) ||
6685 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6686 MovDir[x][y] = old_move_dir;
6690 else if (element == EL_DRAGON)
6692 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6693 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6694 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6696 int rnd = RND(rnd_value);
6698 if (can_move_on && rnd > rnd_value / 8)
6699 MovDir[x][y] = old_move_dir;
6700 else if (can_turn_left && can_turn_right)
6701 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6702 else if (can_turn_left && rnd > rnd_value / 8)
6703 MovDir[x][y] = left_dir;
6704 else if (can_turn_right && rnd > rnd_value / 8)
6705 MovDir[x][y] = right_dir;
6707 MovDir[x][y] = back_dir;
6709 xx = x + move_xy[MovDir[x][y]].dx;
6710 yy = y + move_xy[MovDir[x][y]].dy;
6712 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6713 MovDir[x][y] = old_move_dir;
6717 else if (element == EL_MOLE)
6719 boolean can_move_on =
6720 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6721 IS_AMOEBOID(Feld[move_x][move_y]) ||
6722 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6725 boolean can_turn_left =
6726 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6727 IS_AMOEBOID(Feld[left_x][left_y])));
6729 boolean can_turn_right =
6730 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6731 IS_AMOEBOID(Feld[right_x][right_y])));
6733 if (can_turn_left && can_turn_right)
6734 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6735 else if (can_turn_left)
6736 MovDir[x][y] = left_dir;
6738 MovDir[x][y] = right_dir;
6741 if (MovDir[x][y] != old_move_dir)
6744 else if (element == EL_BALLOON)
6746 MovDir[x][y] = game.wind_direction;
6749 else if (element == EL_SPRING)
6751 if (MovDir[x][y] & MV_HORIZONTAL)
6753 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6754 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6756 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6757 ResetGfxAnimation(move_x, move_y);
6758 TEST_DrawLevelField(move_x, move_y);
6760 MovDir[x][y] = back_dir;
6762 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6763 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6764 MovDir[x][y] = MV_NONE;
6769 else if (element == EL_ROBOT ||
6770 element == EL_SATELLITE ||
6771 element == EL_PENGUIN ||
6772 element == EL_EMC_ANDROID)
6774 int attr_x = -1, attr_y = -1;
6785 for (i = 0; i < MAX_PLAYERS; i++)
6787 struct PlayerInfo *player = &stored_player[i];
6788 int jx = player->jx, jy = player->jy;
6790 if (!player->active)
6794 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6802 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6803 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6804 game.engine_version < VERSION_IDENT(3,1,0,0)))
6810 if (element == EL_PENGUIN)
6813 static int xy[4][2] =
6821 for (i = 0; i < NUM_DIRECTIONS; i++)
6823 int ex = x + xy[i][0];
6824 int ey = y + xy[i][1];
6826 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6827 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6828 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6829 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6838 MovDir[x][y] = MV_NONE;
6840 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6841 else if (attr_x > x)
6842 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6844 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6845 else if (attr_y > y)
6846 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6848 if (element == EL_ROBOT)
6852 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6853 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6854 Moving2Blocked(x, y, &newx, &newy);
6856 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6857 MovDelay[x][y] = 8 + 8 * !RND(3);
6859 MovDelay[x][y] = 16;
6861 else if (element == EL_PENGUIN)
6867 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6869 boolean first_horiz = RND(2);
6870 int new_move_dir = MovDir[x][y];
6873 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6874 Moving2Blocked(x, y, &newx, &newy);
6876 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6880 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6881 Moving2Blocked(x, y, &newx, &newy);
6883 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6886 MovDir[x][y] = old_move_dir;
6890 else if (element == EL_SATELLITE)
6896 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6898 boolean first_horiz = RND(2);
6899 int new_move_dir = MovDir[x][y];
6902 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6903 Moving2Blocked(x, y, &newx, &newy);
6905 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6909 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6910 Moving2Blocked(x, y, &newx, &newy);
6912 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6915 MovDir[x][y] = old_move_dir;
6919 else if (element == EL_EMC_ANDROID)
6921 static int check_pos[16] =
6923 -1, /* 0 => (invalid) */
6924 7, /* 1 => MV_LEFT */
6925 3, /* 2 => MV_RIGHT */
6926 -1, /* 3 => (invalid) */
6928 0, /* 5 => MV_LEFT | MV_UP */
6929 2, /* 6 => MV_RIGHT | MV_UP */
6930 -1, /* 7 => (invalid) */
6931 5, /* 8 => MV_DOWN */
6932 6, /* 9 => MV_LEFT | MV_DOWN */
6933 4, /* 10 => MV_RIGHT | MV_DOWN */
6934 -1, /* 11 => (invalid) */
6935 -1, /* 12 => (invalid) */
6936 -1, /* 13 => (invalid) */
6937 -1, /* 14 => (invalid) */
6938 -1, /* 15 => (invalid) */
6946 { -1, -1, MV_LEFT | MV_UP },
6948 { +1, -1, MV_RIGHT | MV_UP },
6949 { +1, 0, MV_RIGHT },
6950 { +1, +1, MV_RIGHT | MV_DOWN },
6952 { -1, +1, MV_LEFT | MV_DOWN },
6955 int start_pos, check_order;
6956 boolean can_clone = FALSE;
6959 /* check if there is any free field around current position */
6960 for (i = 0; i < 8; i++)
6962 int newx = x + check_xy[i].dx;
6963 int newy = y + check_xy[i].dy;
6965 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6973 if (can_clone) /* randomly find an element to clone */
6977 start_pos = check_pos[RND(8)];
6978 check_order = (RND(2) ? -1 : +1);
6980 for (i = 0; i < 8; i++)
6982 int pos_raw = start_pos + i * check_order;
6983 int pos = (pos_raw + 8) % 8;
6984 int newx = x + check_xy[pos].dx;
6985 int newy = y + check_xy[pos].dy;
6987 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6989 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6990 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6992 Store[x][y] = Feld[newx][newy];
7001 if (can_clone) /* randomly find a direction to move */
7005 start_pos = check_pos[RND(8)];
7006 check_order = (RND(2) ? -1 : +1);
7008 for (i = 0; i < 8; i++)
7010 int pos_raw = start_pos + i * check_order;
7011 int pos = (pos_raw + 8) % 8;
7012 int newx = x + check_xy[pos].dx;
7013 int newy = y + check_xy[pos].dy;
7014 int new_move_dir = check_xy[pos].dir;
7016 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7018 MovDir[x][y] = new_move_dir;
7019 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7028 if (can_clone) /* cloning and moving successful */
7031 /* cannot clone -- try to move towards player */
7033 start_pos = check_pos[MovDir[x][y] & 0x0f];
7034 check_order = (RND(2) ? -1 : +1);
7036 for (i = 0; i < 3; i++)
7038 /* first check start_pos, then previous/next or (next/previous) pos */
7039 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7040 int pos = (pos_raw + 8) % 8;
7041 int newx = x + check_xy[pos].dx;
7042 int newy = y + check_xy[pos].dy;
7043 int new_move_dir = check_xy[pos].dir;
7045 if (IS_PLAYER(newx, newy))
7048 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7050 MovDir[x][y] = new_move_dir;
7051 MovDelay[x][y] = level.android_move_time * 8 + 1;
7058 else if (move_pattern == MV_TURNING_LEFT ||
7059 move_pattern == MV_TURNING_RIGHT ||
7060 move_pattern == MV_TURNING_LEFT_RIGHT ||
7061 move_pattern == MV_TURNING_RIGHT_LEFT ||
7062 move_pattern == MV_TURNING_RANDOM ||
7063 move_pattern == MV_ALL_DIRECTIONS)
7065 boolean can_turn_left =
7066 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7067 boolean can_turn_right =
7068 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7070 if (element_info[element].move_stepsize == 0) /* "not moving" */
7073 if (move_pattern == MV_TURNING_LEFT)
7074 MovDir[x][y] = left_dir;
7075 else if (move_pattern == MV_TURNING_RIGHT)
7076 MovDir[x][y] = right_dir;
7077 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7078 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7079 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7080 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7081 else if (move_pattern == MV_TURNING_RANDOM)
7082 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7083 can_turn_right && !can_turn_left ? right_dir :
7084 RND(2) ? left_dir : right_dir);
7085 else if (can_turn_left && can_turn_right)
7086 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7087 else if (can_turn_left)
7088 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7089 else if (can_turn_right)
7090 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7092 MovDir[x][y] = back_dir;
7094 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7096 else if (move_pattern == MV_HORIZONTAL ||
7097 move_pattern == MV_VERTICAL)
7099 if (move_pattern & old_move_dir)
7100 MovDir[x][y] = back_dir;
7101 else if (move_pattern == MV_HORIZONTAL)
7102 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7103 else if (move_pattern == MV_VERTICAL)
7104 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7106 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7108 else if (move_pattern & MV_ANY_DIRECTION)
7110 MovDir[x][y] = move_pattern;
7111 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7113 else if (move_pattern & MV_WIND_DIRECTION)
7115 MovDir[x][y] = game.wind_direction;
7116 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7118 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7120 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7121 MovDir[x][y] = left_dir;
7122 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7123 MovDir[x][y] = right_dir;
7125 if (MovDir[x][y] != old_move_dir)
7126 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7130 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7131 MovDir[x][y] = right_dir;
7132 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7133 MovDir[x][y] = left_dir;
7135 if (MovDir[x][y] != old_move_dir)
7136 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7138 else if (move_pattern == MV_TOWARDS_PLAYER ||
7139 move_pattern == MV_AWAY_FROM_PLAYER)
7141 int attr_x = -1, attr_y = -1;
7143 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7154 for (i = 0; i < MAX_PLAYERS; i++)
7156 struct PlayerInfo *player = &stored_player[i];
7157 int jx = player->jx, jy = player->jy;
7159 if (!player->active)
7163 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7171 MovDir[x][y] = MV_NONE;
7173 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7174 else if (attr_x > x)
7175 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7177 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7178 else if (attr_y > y)
7179 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7181 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7183 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7185 boolean first_horiz = RND(2);
7186 int new_move_dir = MovDir[x][y];
7188 if (element_info[element].move_stepsize == 0) /* "not moving" */
7190 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7191 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7197 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7198 Moving2Blocked(x, y, &newx, &newy);
7200 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7204 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7205 Moving2Blocked(x, y, &newx, &newy);
7207 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7210 MovDir[x][y] = old_move_dir;
7213 else if (move_pattern == MV_WHEN_PUSHED ||
7214 move_pattern == MV_WHEN_DROPPED)
7216 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7217 MovDir[x][y] = MV_NONE;
7221 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7223 static int test_xy[7][2] =
7233 static int test_dir[7] =
7243 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7244 int move_preference = -1000000; /* start with very low preference */
7245 int new_move_dir = MV_NONE;
7246 int start_test = RND(4);
7249 for (i = 0; i < NUM_DIRECTIONS; i++)
7251 int move_dir = test_dir[start_test + i];
7252 int move_dir_preference;
7254 xx = x + test_xy[start_test + i][0];
7255 yy = y + test_xy[start_test + i][1];
7257 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7258 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7260 new_move_dir = move_dir;
7265 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7268 move_dir_preference = -1 * RunnerVisit[xx][yy];
7269 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7270 move_dir_preference = PlayerVisit[xx][yy];
7272 if (move_dir_preference > move_preference)
7274 /* prefer field that has not been visited for the longest time */
7275 move_preference = move_dir_preference;
7276 new_move_dir = move_dir;
7278 else if (move_dir_preference == move_preference &&
7279 move_dir == old_move_dir)
7281 /* prefer last direction when all directions are preferred equally */
7282 move_preference = move_dir_preference;
7283 new_move_dir = move_dir;
7287 MovDir[x][y] = new_move_dir;
7288 if (old_move_dir != new_move_dir)
7289 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7293 static void TurnRound(int x, int y)
7295 int direction = MovDir[x][y];
7299 GfxDir[x][y] = MovDir[x][y];
7301 if (direction != MovDir[x][y])
7305 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7307 ResetGfxFrame(x, y);
7310 static boolean JustBeingPushed(int x, int y)
7314 for (i = 0; i < MAX_PLAYERS; i++)
7316 struct PlayerInfo *player = &stored_player[i];
7318 if (player->active && player->is_pushing && player->MovPos)
7320 int next_jx = player->jx + (player->jx - player->last_jx);
7321 int next_jy = player->jy + (player->jy - player->last_jy);
7323 if (x == next_jx && y == next_jy)
7331 void StartMoving(int x, int y)
7333 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7334 int element = Feld[x][y];
7339 if (MovDelay[x][y] == 0)
7340 GfxAction[x][y] = ACTION_DEFAULT;
7342 if (CAN_FALL(element) && y < lev_fieldy - 1)
7344 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7345 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7346 if (JustBeingPushed(x, y))
7349 if (element == EL_QUICKSAND_FULL)
7351 if (IS_FREE(x, y + 1))
7353 InitMovingField(x, y, MV_DOWN);
7354 started_moving = TRUE;
7356 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7357 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7358 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7359 Store[x][y] = EL_ROCK;
7361 Store[x][y] = EL_ROCK;
7364 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7366 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7368 if (!MovDelay[x][y])
7370 MovDelay[x][y] = TILEY + 1;
7372 ResetGfxAnimation(x, y);
7373 ResetGfxAnimation(x, y + 1);
7378 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7379 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7386 Feld[x][y] = EL_QUICKSAND_EMPTY;
7387 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7388 Store[x][y + 1] = Store[x][y];
7391 PlayLevelSoundAction(x, y, ACTION_FILLING);
7393 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7395 if (!MovDelay[x][y])
7397 MovDelay[x][y] = TILEY + 1;
7399 ResetGfxAnimation(x, y);
7400 ResetGfxAnimation(x, y + 1);
7405 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7406 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7413 Feld[x][y] = EL_QUICKSAND_EMPTY;
7414 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7415 Store[x][y + 1] = Store[x][y];
7418 PlayLevelSoundAction(x, y, ACTION_FILLING);
7421 else if (element == EL_QUICKSAND_FAST_FULL)
7423 if (IS_FREE(x, y + 1))
7425 InitMovingField(x, y, MV_DOWN);
7426 started_moving = TRUE;
7428 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7429 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7430 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7431 Store[x][y] = EL_ROCK;
7433 Store[x][y] = EL_ROCK;
7436 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7438 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7440 if (!MovDelay[x][y])
7442 MovDelay[x][y] = TILEY + 1;
7444 ResetGfxAnimation(x, y);
7445 ResetGfxAnimation(x, y + 1);
7450 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7451 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7458 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7459 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7460 Store[x][y + 1] = Store[x][y];
7463 PlayLevelSoundAction(x, y, ACTION_FILLING);
7465 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7467 if (!MovDelay[x][y])
7469 MovDelay[x][y] = TILEY + 1;
7471 ResetGfxAnimation(x, y);
7472 ResetGfxAnimation(x, y + 1);
7477 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7478 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7485 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7486 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7487 Store[x][y + 1] = Store[x][y];
7490 PlayLevelSoundAction(x, y, ACTION_FILLING);
7493 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7494 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7496 InitMovingField(x, y, MV_DOWN);
7497 started_moving = TRUE;
7499 Feld[x][y] = EL_QUICKSAND_FILLING;
7500 Store[x][y] = element;
7502 PlayLevelSoundAction(x, y, ACTION_FILLING);
7504 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7505 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7507 InitMovingField(x, y, MV_DOWN);
7508 started_moving = TRUE;
7510 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7511 Store[x][y] = element;
7513 PlayLevelSoundAction(x, y, ACTION_FILLING);
7515 else if (element == EL_MAGIC_WALL_FULL)
7517 if (IS_FREE(x, y + 1))
7519 InitMovingField(x, y, MV_DOWN);
7520 started_moving = TRUE;
7522 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7523 Store[x][y] = EL_CHANGED(Store[x][y]);
7525 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7527 if (!MovDelay[x][y])
7528 MovDelay[x][y] = TILEY / 4 + 1;
7537 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7538 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7539 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7543 else if (element == EL_BD_MAGIC_WALL_FULL)
7545 if (IS_FREE(x, y + 1))
7547 InitMovingField(x, y, MV_DOWN);
7548 started_moving = TRUE;
7550 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7551 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7553 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7555 if (!MovDelay[x][y])
7556 MovDelay[x][y] = TILEY / 4 + 1;
7565 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7566 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7567 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7571 else if (element == EL_DC_MAGIC_WALL_FULL)
7573 if (IS_FREE(x, y + 1))
7575 InitMovingField(x, y, MV_DOWN);
7576 started_moving = TRUE;
7578 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7579 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7581 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7583 if (!MovDelay[x][y])
7584 MovDelay[x][y] = TILEY / 4 + 1;
7593 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7594 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7595 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7599 else if ((CAN_PASS_MAGIC_WALL(element) &&
7600 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7601 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7602 (CAN_PASS_DC_MAGIC_WALL(element) &&
7603 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7606 InitMovingField(x, y, MV_DOWN);
7607 started_moving = TRUE;
7610 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7611 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7612 EL_DC_MAGIC_WALL_FILLING);
7613 Store[x][y] = element;
7615 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7617 SplashAcid(x, y + 1);
7619 InitMovingField(x, y, MV_DOWN);
7620 started_moving = TRUE;
7622 Store[x][y] = EL_ACID;
7625 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7626 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7627 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7628 CAN_FALL(element) && WasJustFalling[x][y] &&
7629 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7631 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7632 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7633 (Feld[x][y + 1] == EL_BLOCKED)))
7635 /* this is needed for a special case not covered by calling "Impact()"
7636 from "ContinueMoving()": if an element moves to a tile directly below
7637 another element which was just falling on that tile (which was empty
7638 in the previous frame), the falling element above would just stop
7639 instead of smashing the element below (in previous version, the above
7640 element was just checked for "moving" instead of "falling", resulting
7641 in incorrect smashes caused by horizontal movement of the above
7642 element; also, the case of the player being the element to smash was
7643 simply not covered here... :-/ ) */
7645 CheckCollision[x][y] = 0;
7646 CheckImpact[x][y] = 0;
7650 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7652 if (MovDir[x][y] == MV_NONE)
7654 InitMovingField(x, y, MV_DOWN);
7655 started_moving = TRUE;
7658 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7660 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7661 MovDir[x][y] = MV_DOWN;
7663 InitMovingField(x, y, MV_DOWN);
7664 started_moving = TRUE;
7666 else if (element == EL_AMOEBA_DROP)
7668 Feld[x][y] = EL_AMOEBA_GROWING;
7669 Store[x][y] = EL_AMOEBA_WET;
7671 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7672 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7673 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7674 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7676 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7677 (IS_FREE(x - 1, y + 1) ||
7678 Feld[x - 1][y + 1] == EL_ACID));
7679 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7680 (IS_FREE(x + 1, y + 1) ||
7681 Feld[x + 1][y + 1] == EL_ACID));
7682 boolean can_fall_any = (can_fall_left || can_fall_right);
7683 boolean can_fall_both = (can_fall_left && can_fall_right);
7684 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7686 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7688 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7689 can_fall_right = FALSE;
7690 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7691 can_fall_left = FALSE;
7692 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7693 can_fall_right = FALSE;
7694 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7695 can_fall_left = FALSE;
7697 can_fall_any = (can_fall_left || can_fall_right);
7698 can_fall_both = FALSE;
7703 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7704 can_fall_right = FALSE; /* slip down on left side */
7706 can_fall_left = !(can_fall_right = RND(2));
7708 can_fall_both = FALSE;
7713 /* if not determined otherwise, prefer left side for slipping down */
7714 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7715 started_moving = TRUE;
7718 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7720 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7721 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7722 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7723 int belt_dir = game.belt_dir[belt_nr];
7725 if ((belt_dir == MV_LEFT && left_is_free) ||
7726 (belt_dir == MV_RIGHT && right_is_free))
7728 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7730 InitMovingField(x, y, belt_dir);
7731 started_moving = TRUE;
7733 Pushed[x][y] = TRUE;
7734 Pushed[nextx][y] = TRUE;
7736 GfxAction[x][y] = ACTION_DEFAULT;
7740 MovDir[x][y] = 0; /* if element was moving, stop it */
7745 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7746 if (CAN_MOVE(element) && !started_moving)
7748 int move_pattern = element_info[element].move_pattern;
7751 Moving2Blocked(x, y, &newx, &newy);
7753 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7756 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7757 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7759 WasJustMoving[x][y] = 0;
7760 CheckCollision[x][y] = 0;
7762 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7764 if (Feld[x][y] != element) /* element has changed */
7768 if (!MovDelay[x][y]) /* start new movement phase */
7770 /* all objects that can change their move direction after each step
7771 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7773 if (element != EL_YAMYAM &&
7774 element != EL_DARK_YAMYAM &&
7775 element != EL_PACMAN &&
7776 !(move_pattern & MV_ANY_DIRECTION) &&
7777 move_pattern != MV_TURNING_LEFT &&
7778 move_pattern != MV_TURNING_RIGHT &&
7779 move_pattern != MV_TURNING_LEFT_RIGHT &&
7780 move_pattern != MV_TURNING_RIGHT_LEFT &&
7781 move_pattern != MV_TURNING_RANDOM)
7785 if (MovDelay[x][y] && (element == EL_BUG ||
7786 element == EL_SPACESHIP ||
7787 element == EL_SP_SNIKSNAK ||
7788 element == EL_SP_ELECTRON ||
7789 element == EL_MOLE))
7790 TEST_DrawLevelField(x, y);
7794 if (MovDelay[x][y]) /* wait some time before next movement */
7798 if (element == EL_ROBOT ||
7799 element == EL_YAMYAM ||
7800 element == EL_DARK_YAMYAM)
7802 DrawLevelElementAnimationIfNeeded(x, y, element);
7803 PlayLevelSoundAction(x, y, ACTION_WAITING);
7805 else if (element == EL_SP_ELECTRON)
7806 DrawLevelElementAnimationIfNeeded(x, y, element);
7807 else if (element == EL_DRAGON)
7810 int dir = MovDir[x][y];
7811 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7812 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7813 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7814 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7815 dir == MV_UP ? IMG_FLAMES_1_UP :
7816 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7817 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7819 GfxAction[x][y] = ACTION_ATTACKING;
7821 if (IS_PLAYER(x, y))
7822 DrawPlayerField(x, y);
7824 TEST_DrawLevelField(x, y);
7826 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7828 for (i = 1; i <= 3; i++)
7830 int xx = x + i * dx;
7831 int yy = y + i * dy;
7832 int sx = SCREENX(xx);
7833 int sy = SCREENY(yy);
7834 int flame_graphic = graphic + (i - 1);
7836 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7841 int flamed = MovingOrBlocked2Element(xx, yy);
7843 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7846 RemoveMovingField(xx, yy);
7848 ChangeDelay[xx][yy] = 0;
7850 Feld[xx][yy] = EL_FLAMES;
7852 if (IN_SCR_FIELD(sx, sy))
7854 TEST_DrawLevelFieldCrumbled(xx, yy);
7855 DrawGraphic(sx, sy, flame_graphic, frame);
7860 if (Feld[xx][yy] == EL_FLAMES)
7861 Feld[xx][yy] = EL_EMPTY;
7862 TEST_DrawLevelField(xx, yy);
7867 if (MovDelay[x][y]) /* element still has to wait some time */
7869 PlayLevelSoundAction(x, y, ACTION_WAITING);
7875 /* now make next step */
7877 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7879 if (DONT_COLLIDE_WITH(element) &&
7880 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7881 !PLAYER_ENEMY_PROTECTED(newx, newy))
7883 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7888 else if (CAN_MOVE_INTO_ACID(element) &&
7889 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7890 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7891 (MovDir[x][y] == MV_DOWN ||
7892 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7894 SplashAcid(newx, newy);
7895 Store[x][y] = EL_ACID;
7897 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7899 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7900 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7901 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7902 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7905 TEST_DrawLevelField(x, y);
7907 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7908 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7909 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7911 local_player->friends_still_needed--;
7912 if (!local_player->friends_still_needed &&
7913 !local_player->GameOver && AllPlayersGone)
7914 PlayerWins(local_player);
7918 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7920 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7921 TEST_DrawLevelField(newx, newy);
7923 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7925 else if (!IS_FREE(newx, newy))
7927 GfxAction[x][y] = ACTION_WAITING;
7929 if (IS_PLAYER(x, y))
7930 DrawPlayerField(x, y);
7932 TEST_DrawLevelField(x, y);
7937 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7939 if (IS_FOOD_PIG(Feld[newx][newy]))
7941 if (IS_MOVING(newx, newy))
7942 RemoveMovingField(newx, newy);
7945 Feld[newx][newy] = EL_EMPTY;
7946 TEST_DrawLevelField(newx, newy);
7949 PlayLevelSound(x, y, SND_PIG_DIGGING);
7951 else if (!IS_FREE(newx, newy))
7953 if (IS_PLAYER(x, y))
7954 DrawPlayerField(x, y);
7956 TEST_DrawLevelField(x, y);
7961 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7963 if (Store[x][y] != EL_EMPTY)
7965 boolean can_clone = FALSE;
7968 /* check if element to clone is still there */
7969 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7971 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7979 /* cannot clone or target field not free anymore -- do not clone */
7980 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7981 Store[x][y] = EL_EMPTY;
7984 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7986 if (IS_MV_DIAGONAL(MovDir[x][y]))
7988 int diagonal_move_dir = MovDir[x][y];
7989 int stored = Store[x][y];
7990 int change_delay = 8;
7993 /* android is moving diagonally */
7995 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7997 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7998 GfxElement[x][y] = EL_EMC_ANDROID;
7999 GfxAction[x][y] = ACTION_SHRINKING;
8000 GfxDir[x][y] = diagonal_move_dir;
8001 ChangeDelay[x][y] = change_delay;
8003 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8006 DrawLevelGraphicAnimation(x, y, graphic);
8007 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8009 if (Feld[newx][newy] == EL_ACID)
8011 SplashAcid(newx, newy);
8016 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8018 Store[newx][newy] = EL_EMC_ANDROID;
8019 GfxElement[newx][newy] = EL_EMC_ANDROID;
8020 GfxAction[newx][newy] = ACTION_GROWING;
8021 GfxDir[newx][newy] = diagonal_move_dir;
8022 ChangeDelay[newx][newy] = change_delay;
8024 graphic = el_act_dir2img(GfxElement[newx][newy],
8025 GfxAction[newx][newy], GfxDir[newx][newy]);
8027 DrawLevelGraphicAnimation(newx, newy, graphic);
8028 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8034 Feld[newx][newy] = EL_EMPTY;
8035 TEST_DrawLevelField(newx, newy);
8037 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8040 else if (!IS_FREE(newx, newy))
8045 else if (IS_CUSTOM_ELEMENT(element) &&
8046 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8048 if (!DigFieldByCE(newx, newy, element))
8051 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8053 RunnerVisit[x][y] = FrameCounter;
8054 PlayerVisit[x][y] /= 8; /* expire player visit path */
8057 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8059 if (!IS_FREE(newx, newy))
8061 if (IS_PLAYER(x, y))
8062 DrawPlayerField(x, y);
8064 TEST_DrawLevelField(x, y);
8070 boolean wanna_flame = !RND(10);
8071 int dx = newx - x, dy = newy - y;
8072 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8073 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8074 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8075 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8076 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8077 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8080 IS_CLASSIC_ENEMY(element1) ||
8081 IS_CLASSIC_ENEMY(element2)) &&
8082 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8083 element1 != EL_FLAMES && element2 != EL_FLAMES)
8085 ResetGfxAnimation(x, y);
8086 GfxAction[x][y] = ACTION_ATTACKING;
8088 if (IS_PLAYER(x, y))
8089 DrawPlayerField(x, y);
8091 TEST_DrawLevelField(x, y);
8093 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8095 MovDelay[x][y] = 50;
8097 Feld[newx][newy] = EL_FLAMES;
8098 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8099 Feld[newx1][newy1] = EL_FLAMES;
8100 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8101 Feld[newx2][newy2] = EL_FLAMES;
8107 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8108 Feld[newx][newy] == EL_DIAMOND)
8110 if (IS_MOVING(newx, newy))
8111 RemoveMovingField(newx, newy);
8114 Feld[newx][newy] = EL_EMPTY;
8115 TEST_DrawLevelField(newx, newy);
8118 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8120 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8121 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8123 if (AmoebaNr[newx][newy])
8125 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8126 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8127 Feld[newx][newy] == EL_BD_AMOEBA)
8128 AmoebaCnt[AmoebaNr[newx][newy]]--;
8131 if (IS_MOVING(newx, newy))
8133 RemoveMovingField(newx, newy);
8137 Feld[newx][newy] = EL_EMPTY;
8138 TEST_DrawLevelField(newx, newy);
8141 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8143 else if ((element == EL_PACMAN || element == EL_MOLE)
8144 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8146 if (AmoebaNr[newx][newy])
8148 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8149 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8150 Feld[newx][newy] == EL_BD_AMOEBA)
8151 AmoebaCnt[AmoebaNr[newx][newy]]--;
8154 if (element == EL_MOLE)
8156 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8157 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8159 ResetGfxAnimation(x, y);
8160 GfxAction[x][y] = ACTION_DIGGING;
8161 TEST_DrawLevelField(x, y);
8163 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8165 return; /* wait for shrinking amoeba */
8167 else /* element == EL_PACMAN */
8169 Feld[newx][newy] = EL_EMPTY;
8170 TEST_DrawLevelField(newx, newy);
8171 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8174 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8175 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8176 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8178 /* wait for shrinking amoeba to completely disappear */
8181 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8183 /* object was running against a wall */
8187 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8188 DrawLevelElementAnimation(x, y, element);
8190 if (DONT_TOUCH(element))
8191 TestIfBadThingTouchesPlayer(x, y);
8196 InitMovingField(x, y, MovDir[x][y]);
8198 PlayLevelSoundAction(x, y, ACTION_MOVING);
8202 ContinueMoving(x, y);
8205 void ContinueMoving(int x, int y)
8207 int element = Feld[x][y];
8208 struct ElementInfo *ei = &element_info[element];
8209 int direction = MovDir[x][y];
8210 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8211 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8212 int newx = x + dx, newy = y + dy;
8213 int stored = Store[x][y];
8214 int stored_new = Store[newx][newy];
8215 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8216 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8217 boolean last_line = (newy == lev_fieldy - 1);
8219 MovPos[x][y] += getElementMoveStepsize(x, y);
8221 if (pushed_by_player) /* special case: moving object pushed by player */
8222 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8224 if (ABS(MovPos[x][y]) < TILEX)
8226 TEST_DrawLevelField(x, y);
8228 return; /* element is still moving */
8231 /* element reached destination field */
8233 Feld[x][y] = EL_EMPTY;
8234 Feld[newx][newy] = element;
8235 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8237 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8239 element = Feld[newx][newy] = EL_ACID;
8241 else if (element == EL_MOLE)
8243 Feld[x][y] = EL_SAND;
8245 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8247 else if (element == EL_QUICKSAND_FILLING)
8249 element = Feld[newx][newy] = get_next_element(element);
8250 Store[newx][newy] = Store[x][y];
8252 else if (element == EL_QUICKSAND_EMPTYING)
8254 Feld[x][y] = get_next_element(element);
8255 element = Feld[newx][newy] = Store[x][y];
8257 else if (element == EL_QUICKSAND_FAST_FILLING)
8259 element = Feld[newx][newy] = get_next_element(element);
8260 Store[newx][newy] = Store[x][y];
8262 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8264 Feld[x][y] = get_next_element(element);
8265 element = Feld[newx][newy] = Store[x][y];
8267 else if (element == EL_MAGIC_WALL_FILLING)
8269 element = Feld[newx][newy] = get_next_element(element);
8270 if (!game.magic_wall_active)
8271 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8272 Store[newx][newy] = Store[x][y];
8274 else if (element == EL_MAGIC_WALL_EMPTYING)
8276 Feld[x][y] = get_next_element(element);
8277 if (!game.magic_wall_active)
8278 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8279 element = Feld[newx][newy] = Store[x][y];
8281 InitField(newx, newy, FALSE);
8283 else if (element == EL_BD_MAGIC_WALL_FILLING)
8285 element = Feld[newx][newy] = get_next_element(element);
8286 if (!game.magic_wall_active)
8287 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8288 Store[newx][newy] = Store[x][y];
8290 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8292 Feld[x][y] = get_next_element(element);
8293 if (!game.magic_wall_active)
8294 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8295 element = Feld[newx][newy] = Store[x][y];
8297 InitField(newx, newy, FALSE);
8299 else if (element == EL_DC_MAGIC_WALL_FILLING)
8301 element = Feld[newx][newy] = get_next_element(element);
8302 if (!game.magic_wall_active)
8303 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8304 Store[newx][newy] = Store[x][y];
8306 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8308 Feld[x][y] = get_next_element(element);
8309 if (!game.magic_wall_active)
8310 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8311 element = Feld[newx][newy] = Store[x][y];
8313 InitField(newx, newy, FALSE);
8315 else if (element == EL_AMOEBA_DROPPING)
8317 Feld[x][y] = get_next_element(element);
8318 element = Feld[newx][newy] = Store[x][y];
8320 else if (element == EL_SOKOBAN_OBJECT)
8323 Feld[x][y] = Back[x][y];
8325 if (Back[newx][newy])
8326 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8328 Back[x][y] = Back[newx][newy] = 0;
8331 Store[x][y] = EL_EMPTY;
8336 MovDelay[newx][newy] = 0;
8338 if (CAN_CHANGE_OR_HAS_ACTION(element))
8340 /* copy element change control values to new field */
8341 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8342 ChangePage[newx][newy] = ChangePage[x][y];
8343 ChangeCount[newx][newy] = ChangeCount[x][y];
8344 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8347 CustomValue[newx][newy] = CustomValue[x][y];
8349 ChangeDelay[x][y] = 0;
8350 ChangePage[x][y] = -1;
8351 ChangeCount[x][y] = 0;
8352 ChangeEvent[x][y] = -1;
8354 CustomValue[x][y] = 0;
8356 /* copy animation control values to new field */
8357 GfxFrame[newx][newy] = GfxFrame[x][y];
8358 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8359 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8360 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8362 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8364 /* some elements can leave other elements behind after moving */
8365 if (ei->move_leave_element != EL_EMPTY &&
8366 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8367 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8369 int move_leave_element = ei->move_leave_element;
8371 /* this makes it possible to leave the removed element again */
8372 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8373 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8375 Feld[x][y] = move_leave_element;
8377 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8378 MovDir[x][y] = direction;
8380 InitField(x, y, FALSE);
8382 if (GFX_CRUMBLED(Feld[x][y]))
8383 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8385 if (ELEM_IS_PLAYER(move_leave_element))
8386 RelocatePlayer(x, y, move_leave_element);
8389 /* do this after checking for left-behind element */
8390 ResetGfxAnimation(x, y); /* reset animation values for old field */
8392 if (!CAN_MOVE(element) ||
8393 (CAN_FALL(element) && direction == MV_DOWN &&
8394 (element == EL_SPRING ||
8395 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8396 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8397 GfxDir[x][y] = MovDir[newx][newy] = 0;
8399 TEST_DrawLevelField(x, y);
8400 TEST_DrawLevelField(newx, newy);
8402 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8404 /* prevent pushed element from moving on in pushed direction */
8405 if (pushed_by_player && CAN_MOVE(element) &&
8406 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8407 !(element_info[element].move_pattern & direction))
8408 TurnRound(newx, newy);
8410 /* prevent elements on conveyor belt from moving on in last direction */
8411 if (pushed_by_conveyor && CAN_FALL(element) &&
8412 direction & MV_HORIZONTAL)
8413 MovDir[newx][newy] = 0;
8415 if (!pushed_by_player)
8417 int nextx = newx + dx, nexty = newy + dy;
8418 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8420 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8422 if (CAN_FALL(element) && direction == MV_DOWN)
8423 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8425 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8426 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8428 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8429 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8432 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8434 TestIfBadThingTouchesPlayer(newx, newy);
8435 TestIfBadThingTouchesFriend(newx, newy);
8437 if (!IS_CUSTOM_ELEMENT(element))
8438 TestIfBadThingTouchesOtherBadThing(newx, newy);
8440 else if (element == EL_PENGUIN)
8441 TestIfFriendTouchesBadThing(newx, newy);
8443 if (DONT_GET_HIT_BY(element))
8445 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8448 /* give the player one last chance (one more frame) to move away */
8449 if (CAN_FALL(element) && direction == MV_DOWN &&
8450 (last_line || (!IS_FREE(x, newy + 1) &&
8451 (!IS_PLAYER(x, newy + 1) ||
8452 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8455 if (pushed_by_player && !game.use_change_when_pushing_bug)
8457 int push_side = MV_DIR_OPPOSITE(direction);
8458 struct PlayerInfo *player = PLAYERINFO(x, y);
8460 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8461 player->index_bit, push_side);
8462 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8463 player->index_bit, push_side);
8466 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8467 MovDelay[newx][newy] = 1;
8469 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8471 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8472 TestIfElementHitsCustomElement(newx, newy, direction);
8473 TestIfPlayerTouchesCustomElement(newx, newy);
8474 TestIfElementTouchesCustomElement(newx, newy);
8476 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8477 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8478 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8479 MV_DIR_OPPOSITE(direction));
8482 int AmoebeNachbarNr(int ax, int ay)
8485 int element = Feld[ax][ay];
8487 static int xy[4][2] =
8495 for (i = 0; i < NUM_DIRECTIONS; i++)
8497 int x = ax + xy[i][0];
8498 int y = ay + xy[i][1];
8500 if (!IN_LEV_FIELD(x, y))
8503 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8504 group_nr = AmoebaNr[x][y];
8510 void AmoebenVereinigen(int ax, int ay)
8512 int i, x, y, xx, yy;
8513 int new_group_nr = AmoebaNr[ax][ay];
8514 static int xy[4][2] =
8522 if (new_group_nr == 0)
8525 for (i = 0; i < NUM_DIRECTIONS; i++)
8530 if (!IN_LEV_FIELD(x, y))
8533 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8534 Feld[x][y] == EL_BD_AMOEBA ||
8535 Feld[x][y] == EL_AMOEBA_DEAD) &&
8536 AmoebaNr[x][y] != new_group_nr)
8538 int old_group_nr = AmoebaNr[x][y];
8540 if (old_group_nr == 0)
8543 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8544 AmoebaCnt[old_group_nr] = 0;
8545 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8546 AmoebaCnt2[old_group_nr] = 0;
8548 SCAN_PLAYFIELD(xx, yy)
8550 if (AmoebaNr[xx][yy] == old_group_nr)
8551 AmoebaNr[xx][yy] = new_group_nr;
8557 void AmoebeUmwandeln(int ax, int ay)
8561 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8563 int group_nr = AmoebaNr[ax][ay];
8568 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8569 printf("AmoebeUmwandeln(): This should never happen!\n");
8574 SCAN_PLAYFIELD(x, y)
8576 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8579 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8583 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8584 SND_AMOEBA_TURNING_TO_GEM :
8585 SND_AMOEBA_TURNING_TO_ROCK));
8590 static int xy[4][2] =
8598 for (i = 0; i < NUM_DIRECTIONS; i++)
8603 if (!IN_LEV_FIELD(x, y))
8606 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8608 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8609 SND_AMOEBA_TURNING_TO_GEM :
8610 SND_AMOEBA_TURNING_TO_ROCK));
8617 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8620 int group_nr = AmoebaNr[ax][ay];
8621 boolean done = FALSE;
8626 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8627 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8632 SCAN_PLAYFIELD(x, y)
8634 if (AmoebaNr[x][y] == group_nr &&
8635 (Feld[x][y] == EL_AMOEBA_DEAD ||
8636 Feld[x][y] == EL_BD_AMOEBA ||
8637 Feld[x][y] == EL_AMOEBA_GROWING))
8640 Feld[x][y] = new_element;
8641 InitField(x, y, FALSE);
8642 TEST_DrawLevelField(x, y);
8648 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8649 SND_BD_AMOEBA_TURNING_TO_ROCK :
8650 SND_BD_AMOEBA_TURNING_TO_GEM));
8653 void AmoebeWaechst(int x, int y)
8655 static unsigned int sound_delay = 0;
8656 static unsigned int sound_delay_value = 0;
8658 if (!MovDelay[x][y]) /* start new growing cycle */
8662 if (DelayReached(&sound_delay, sound_delay_value))
8664 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8665 sound_delay_value = 30;
8669 if (MovDelay[x][y]) /* wait some time before growing bigger */
8672 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8674 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8675 6 - MovDelay[x][y]);
8677 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8680 if (!MovDelay[x][y])
8682 Feld[x][y] = Store[x][y];
8684 TEST_DrawLevelField(x, y);
8689 void AmoebaDisappearing(int x, int y)
8691 static unsigned int sound_delay = 0;
8692 static unsigned int sound_delay_value = 0;
8694 if (!MovDelay[x][y]) /* start new shrinking cycle */
8698 if (DelayReached(&sound_delay, sound_delay_value))
8699 sound_delay_value = 30;
8702 if (MovDelay[x][y]) /* wait some time before shrinking */
8705 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8707 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8708 6 - MovDelay[x][y]);
8710 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8713 if (!MovDelay[x][y])
8715 Feld[x][y] = EL_EMPTY;
8716 TEST_DrawLevelField(x, y);
8718 /* don't let mole enter this field in this cycle;
8719 (give priority to objects falling to this field from above) */
8725 void AmoebeAbleger(int ax, int ay)
8728 int element = Feld[ax][ay];
8729 int graphic = el2img(element);
8730 int newax = ax, neway = ay;
8731 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8732 static int xy[4][2] =
8740 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8742 Feld[ax][ay] = EL_AMOEBA_DEAD;
8743 TEST_DrawLevelField(ax, ay);
8747 if (IS_ANIMATED(graphic))
8748 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8750 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8751 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8753 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8756 if (MovDelay[ax][ay])
8760 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8763 int x = ax + xy[start][0];
8764 int y = ay + xy[start][1];
8766 if (!IN_LEV_FIELD(x, y))
8769 if (IS_FREE(x, y) ||
8770 CAN_GROW_INTO(Feld[x][y]) ||
8771 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8772 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8778 if (newax == ax && neway == ay)
8781 else /* normal or "filled" (BD style) amoeba */
8784 boolean waiting_for_player = FALSE;
8786 for (i = 0; i < NUM_DIRECTIONS; i++)
8788 int j = (start + i) % 4;
8789 int x = ax + xy[j][0];
8790 int y = ay + xy[j][1];
8792 if (!IN_LEV_FIELD(x, y))
8795 if (IS_FREE(x, y) ||
8796 CAN_GROW_INTO(Feld[x][y]) ||
8797 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8798 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8804 else if (IS_PLAYER(x, y))
8805 waiting_for_player = TRUE;
8808 if (newax == ax && neway == ay) /* amoeba cannot grow */
8810 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8812 Feld[ax][ay] = EL_AMOEBA_DEAD;
8813 TEST_DrawLevelField(ax, ay);
8814 AmoebaCnt[AmoebaNr[ax][ay]]--;
8816 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8818 if (element == EL_AMOEBA_FULL)
8819 AmoebeUmwandeln(ax, ay);
8820 else if (element == EL_BD_AMOEBA)
8821 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8826 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8828 /* amoeba gets larger by growing in some direction */
8830 int new_group_nr = AmoebaNr[ax][ay];
8833 if (new_group_nr == 0)
8835 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8836 printf("AmoebeAbleger(): This should never happen!\n");
8841 AmoebaNr[newax][neway] = new_group_nr;
8842 AmoebaCnt[new_group_nr]++;
8843 AmoebaCnt2[new_group_nr]++;
8845 /* if amoeba touches other amoeba(s) after growing, unify them */
8846 AmoebenVereinigen(newax, neway);
8848 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8850 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8856 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8857 (neway == lev_fieldy - 1 && newax != ax))
8859 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8860 Store[newax][neway] = element;
8862 else if (neway == ay || element == EL_EMC_DRIPPER)
8864 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8866 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8870 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8871 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8872 Store[ax][ay] = EL_AMOEBA_DROP;
8873 ContinueMoving(ax, ay);
8877 TEST_DrawLevelField(newax, neway);
8880 void Life(int ax, int ay)
8884 int element = Feld[ax][ay];
8885 int graphic = el2img(element);
8886 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8888 boolean changed = FALSE;
8890 if (IS_ANIMATED(graphic))
8891 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8896 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8897 MovDelay[ax][ay] = life_time;
8899 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8902 if (MovDelay[ax][ay])
8906 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8908 int xx = ax+x1, yy = ay+y1;
8911 if (!IN_LEV_FIELD(xx, yy))
8914 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8916 int x = xx+x2, y = yy+y2;
8918 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8921 if (((Feld[x][y] == element ||
8922 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8924 (IS_FREE(x, y) && Stop[x][y]))
8928 if (xx == ax && yy == ay) /* field in the middle */
8930 if (nachbarn < life_parameter[0] ||
8931 nachbarn > life_parameter[1])
8933 Feld[xx][yy] = EL_EMPTY;
8935 TEST_DrawLevelField(xx, yy);
8936 Stop[xx][yy] = TRUE;
8940 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8941 { /* free border field */
8942 if (nachbarn >= life_parameter[2] &&
8943 nachbarn <= life_parameter[3])
8945 Feld[xx][yy] = element;
8946 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8948 TEST_DrawLevelField(xx, yy);
8949 Stop[xx][yy] = TRUE;
8956 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8957 SND_GAME_OF_LIFE_GROWING);
8960 static void InitRobotWheel(int x, int y)
8962 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8965 static void RunRobotWheel(int x, int y)
8967 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8970 static void StopRobotWheel(int x, int y)
8972 if (ZX == x && ZY == y)
8976 game.robot_wheel_active = FALSE;
8980 static void InitTimegateWheel(int x, int y)
8982 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8985 static void RunTimegateWheel(int x, int y)
8987 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8990 static void InitMagicBallDelay(int x, int y)
8992 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8995 static void ActivateMagicBall(int bx, int by)
8999 if (level.ball_random)
9001 int pos_border = RND(8); /* select one of the eight border elements */
9002 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9003 int xx = pos_content % 3;
9004 int yy = pos_content / 3;
9009 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9010 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9014 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9016 int xx = x - bx + 1;
9017 int yy = y - by + 1;
9019 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9020 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9024 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9027 void CheckExit(int x, int y)
9029 if (local_player->gems_still_needed > 0 ||
9030 local_player->sokobanfields_still_needed > 0 ||
9031 local_player->lights_still_needed > 0)
9033 int element = Feld[x][y];
9034 int graphic = el2img(element);
9036 if (IS_ANIMATED(graphic))
9037 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9042 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9045 Feld[x][y] = EL_EXIT_OPENING;
9047 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9050 void CheckExitEM(int x, int y)
9052 if (local_player->gems_still_needed > 0 ||
9053 local_player->sokobanfields_still_needed > 0 ||
9054 local_player->lights_still_needed > 0)
9056 int element = Feld[x][y];
9057 int graphic = el2img(element);
9059 if (IS_ANIMATED(graphic))
9060 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9065 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9068 Feld[x][y] = EL_EM_EXIT_OPENING;
9070 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9073 void CheckExitSteel(int x, int y)
9075 if (local_player->gems_still_needed > 0 ||
9076 local_player->sokobanfields_still_needed > 0 ||
9077 local_player->lights_still_needed > 0)
9079 int element = Feld[x][y];
9080 int graphic = el2img(element);
9082 if (IS_ANIMATED(graphic))
9083 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9088 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9091 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9093 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9096 void CheckExitSteelEM(int x, int y)
9098 if (local_player->gems_still_needed > 0 ||
9099 local_player->sokobanfields_still_needed > 0 ||
9100 local_player->lights_still_needed > 0)
9102 int element = Feld[x][y];
9103 int graphic = el2img(element);
9105 if (IS_ANIMATED(graphic))
9106 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9111 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9114 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9116 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9119 void CheckExitSP(int x, int y)
9121 if (local_player->gems_still_needed > 0)
9123 int element = Feld[x][y];
9124 int graphic = el2img(element);
9126 if (IS_ANIMATED(graphic))
9127 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9132 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9135 Feld[x][y] = EL_SP_EXIT_OPENING;
9137 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9140 static void CloseAllOpenTimegates()
9144 SCAN_PLAYFIELD(x, y)
9146 int element = Feld[x][y];
9148 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9150 Feld[x][y] = EL_TIMEGATE_CLOSING;
9152 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9157 void DrawTwinkleOnField(int x, int y)
9159 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9162 if (Feld[x][y] == EL_BD_DIAMOND)
9165 if (MovDelay[x][y] == 0) /* next animation frame */
9166 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9168 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9172 DrawLevelElementAnimation(x, y, Feld[x][y]);
9174 if (MovDelay[x][y] != 0)
9176 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9177 10 - MovDelay[x][y]);
9179 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9184 void MauerWaechst(int x, int y)
9188 if (!MovDelay[x][y]) /* next animation frame */
9189 MovDelay[x][y] = 3 * delay;
9191 if (MovDelay[x][y]) /* wait some time before next frame */
9195 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9197 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9198 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9200 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9203 if (!MovDelay[x][y])
9205 if (MovDir[x][y] == MV_LEFT)
9207 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9208 TEST_DrawLevelField(x - 1, y);
9210 else if (MovDir[x][y] == MV_RIGHT)
9212 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9213 TEST_DrawLevelField(x + 1, y);
9215 else if (MovDir[x][y] == MV_UP)
9217 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9218 TEST_DrawLevelField(x, y - 1);
9222 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9223 TEST_DrawLevelField(x, y + 1);
9226 Feld[x][y] = Store[x][y];
9228 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9229 TEST_DrawLevelField(x, y);
9234 void MauerAbleger(int ax, int ay)
9236 int element = Feld[ax][ay];
9237 int graphic = el2img(element);
9238 boolean oben_frei = FALSE, unten_frei = FALSE;
9239 boolean links_frei = FALSE, rechts_frei = FALSE;
9240 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9241 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9242 boolean new_wall = FALSE;
9244 if (IS_ANIMATED(graphic))
9245 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9247 if (!MovDelay[ax][ay]) /* start building new wall */
9248 MovDelay[ax][ay] = 6;
9250 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9253 if (MovDelay[ax][ay])
9257 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9259 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9261 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9263 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9266 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9267 element == EL_EXPANDABLE_WALL_ANY)
9271 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9272 Store[ax][ay-1] = element;
9273 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9274 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9275 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9276 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9281 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9282 Store[ax][ay+1] = element;
9283 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9284 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9285 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9286 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9291 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9292 element == EL_EXPANDABLE_WALL_ANY ||
9293 element == EL_EXPANDABLE_WALL ||
9294 element == EL_BD_EXPANDABLE_WALL)
9298 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9299 Store[ax-1][ay] = element;
9300 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9301 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9302 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9303 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9309 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9310 Store[ax+1][ay] = element;
9311 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9312 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9313 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9314 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9319 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9320 TEST_DrawLevelField(ax, ay);
9322 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9324 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9325 unten_massiv = TRUE;
9326 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9327 links_massiv = TRUE;
9328 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9329 rechts_massiv = TRUE;
9331 if (((oben_massiv && unten_massiv) ||
9332 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9333 element == EL_EXPANDABLE_WALL) &&
9334 ((links_massiv && rechts_massiv) ||
9335 element == EL_EXPANDABLE_WALL_VERTICAL))
9336 Feld[ax][ay] = EL_WALL;
9339 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9342 void MauerAblegerStahl(int ax, int ay)
9344 int element = Feld[ax][ay];
9345 int graphic = el2img(element);
9346 boolean oben_frei = FALSE, unten_frei = FALSE;
9347 boolean links_frei = FALSE, rechts_frei = FALSE;
9348 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9349 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9350 boolean new_wall = FALSE;
9352 if (IS_ANIMATED(graphic))
9353 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9355 if (!MovDelay[ax][ay]) /* start building new wall */
9356 MovDelay[ax][ay] = 6;
9358 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9361 if (MovDelay[ax][ay])
9365 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9367 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9369 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9371 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9374 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9375 element == EL_EXPANDABLE_STEELWALL_ANY)
9379 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9380 Store[ax][ay-1] = element;
9381 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9382 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9383 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9384 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9389 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9390 Store[ax][ay+1] = element;
9391 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9392 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9393 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9394 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9399 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9400 element == EL_EXPANDABLE_STEELWALL_ANY)
9404 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9405 Store[ax-1][ay] = element;
9406 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9407 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9408 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9409 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9415 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9416 Store[ax+1][ay] = element;
9417 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9418 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9419 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9420 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9425 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9427 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9428 unten_massiv = TRUE;
9429 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9430 links_massiv = TRUE;
9431 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9432 rechts_massiv = TRUE;
9434 if (((oben_massiv && unten_massiv) ||
9435 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9436 ((links_massiv && rechts_massiv) ||
9437 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9438 Feld[ax][ay] = EL_STEELWALL;
9441 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9444 void CheckForDragon(int x, int y)
9447 boolean dragon_found = FALSE;
9448 static int xy[4][2] =
9456 for (i = 0; i < NUM_DIRECTIONS; i++)
9458 for (j = 0; j < 4; j++)
9460 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9462 if (IN_LEV_FIELD(xx, yy) &&
9463 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9465 if (Feld[xx][yy] == EL_DRAGON)
9466 dragon_found = TRUE;
9475 for (i = 0; i < NUM_DIRECTIONS; i++)
9477 for (j = 0; j < 3; j++)
9479 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9481 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9483 Feld[xx][yy] = EL_EMPTY;
9484 TEST_DrawLevelField(xx, yy);
9493 static void InitBuggyBase(int x, int y)
9495 int element = Feld[x][y];
9496 int activating_delay = FRAMES_PER_SECOND / 4;
9499 (element == EL_SP_BUGGY_BASE ?
9500 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9501 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9503 element == EL_SP_BUGGY_BASE_ACTIVE ?
9504 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9507 static void WarnBuggyBase(int x, int y)
9510 static int xy[4][2] =
9518 for (i = 0; i < NUM_DIRECTIONS; i++)
9520 int xx = x + xy[i][0];
9521 int yy = y + xy[i][1];
9523 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9525 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9532 static void InitTrap(int x, int y)
9534 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9537 static void ActivateTrap(int x, int y)
9539 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9542 static void ChangeActiveTrap(int x, int y)
9544 int graphic = IMG_TRAP_ACTIVE;
9546 /* if new animation frame was drawn, correct crumbled sand border */
9547 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9548 TEST_DrawLevelFieldCrumbled(x, y);
9551 static int getSpecialActionElement(int element, int number, int base_element)
9553 return (element != EL_EMPTY ? element :
9554 number != -1 ? base_element + number - 1 :
9558 static int getModifiedActionNumber(int value_old, int operator, int operand,
9559 int value_min, int value_max)
9561 int value_new = (operator == CA_MODE_SET ? operand :
9562 operator == CA_MODE_ADD ? value_old + operand :
9563 operator == CA_MODE_SUBTRACT ? value_old - operand :
9564 operator == CA_MODE_MULTIPLY ? value_old * operand :
9565 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9566 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9569 return (value_new < value_min ? value_min :
9570 value_new > value_max ? value_max :
9574 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9576 struct ElementInfo *ei = &element_info[element];
9577 struct ElementChangeInfo *change = &ei->change_page[page];
9578 int target_element = change->target_element;
9579 int action_type = change->action_type;
9580 int action_mode = change->action_mode;
9581 int action_arg = change->action_arg;
9582 int action_element = change->action_element;
9585 if (!change->has_action)
9588 /* ---------- determine action paramater values -------------------------- */
9590 int level_time_value =
9591 (level.time > 0 ? TimeLeft :
9594 int action_arg_element_raw =
9595 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9596 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9597 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9598 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9599 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9600 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9601 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9603 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9605 int action_arg_direction =
9606 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9607 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9608 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9609 change->actual_trigger_side :
9610 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9611 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9614 int action_arg_number_min =
9615 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9618 int action_arg_number_max =
9619 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9620 action_type == CA_SET_LEVEL_GEMS ? 999 :
9621 action_type == CA_SET_LEVEL_TIME ? 9999 :
9622 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9623 action_type == CA_SET_CE_VALUE ? 9999 :
9624 action_type == CA_SET_CE_SCORE ? 9999 :
9627 int action_arg_number_reset =
9628 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9629 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9630 action_type == CA_SET_LEVEL_TIME ? level.time :
9631 action_type == CA_SET_LEVEL_SCORE ? 0 :
9632 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9633 action_type == CA_SET_CE_SCORE ? 0 :
9636 int action_arg_number =
9637 (action_arg <= CA_ARG_MAX ? action_arg :
9638 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9639 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9640 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9641 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9642 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9643 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9644 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9645 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9646 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9647 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9648 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9649 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9650 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9651 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9652 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9653 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9654 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9655 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9656 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9657 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9658 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9661 int action_arg_number_old =
9662 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9663 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9664 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9665 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9666 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9669 int action_arg_number_new =
9670 getModifiedActionNumber(action_arg_number_old,
9671 action_mode, action_arg_number,
9672 action_arg_number_min, action_arg_number_max);
9674 int trigger_player_bits =
9675 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9676 change->actual_trigger_player_bits : change->trigger_player);
9678 int action_arg_player_bits =
9679 (action_arg >= CA_ARG_PLAYER_1 &&
9680 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9681 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9682 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9685 /* ---------- execute action -------------------------------------------- */
9687 switch (action_type)
9694 /* ---------- level actions ------------------------------------------- */
9696 case CA_RESTART_LEVEL:
9698 game.restart_level = TRUE;
9703 case CA_SHOW_ENVELOPE:
9705 int element = getSpecialActionElement(action_arg_element,
9706 action_arg_number, EL_ENVELOPE_1);
9708 if (IS_ENVELOPE(element))
9709 local_player->show_envelope = element;
9714 case CA_SET_LEVEL_TIME:
9716 if (level.time > 0) /* only modify limited time value */
9718 TimeLeft = action_arg_number_new;
9720 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9722 DisplayGameControlValues();
9724 if (!TimeLeft && setup.time_limit)
9725 for (i = 0; i < MAX_PLAYERS; i++)
9726 KillPlayer(&stored_player[i]);
9732 case CA_SET_LEVEL_SCORE:
9734 local_player->score = action_arg_number_new;
9736 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9738 DisplayGameControlValues();
9743 case CA_SET_LEVEL_GEMS:
9745 local_player->gems_still_needed = action_arg_number_new;
9747 game.snapshot.collected_item = TRUE;
9749 game_panel_controls[GAME_PANEL_GEMS].value =
9750 local_player->gems_still_needed;
9752 DisplayGameControlValues();
9757 case CA_SET_LEVEL_WIND:
9759 game.wind_direction = action_arg_direction;
9764 case CA_SET_LEVEL_RANDOM_SEED:
9766 /* ensure that setting a new random seed while playing is predictable */
9767 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9772 /* ---------- player actions ------------------------------------------ */
9774 case CA_MOVE_PLAYER:
9776 /* automatically move to the next field in specified direction */
9777 for (i = 0; i < MAX_PLAYERS; i++)
9778 if (trigger_player_bits & (1 << i))
9779 stored_player[i].programmed_action = action_arg_direction;
9784 case CA_EXIT_PLAYER:
9786 for (i = 0; i < MAX_PLAYERS; i++)
9787 if (action_arg_player_bits & (1 << i))
9788 ExitPlayer(&stored_player[i]);
9791 PlayerWins(local_player);
9796 case CA_KILL_PLAYER:
9798 for (i = 0; i < MAX_PLAYERS; i++)
9799 if (action_arg_player_bits & (1 << i))
9800 KillPlayer(&stored_player[i]);
9805 case CA_SET_PLAYER_KEYS:
9807 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9808 int element = getSpecialActionElement(action_arg_element,
9809 action_arg_number, EL_KEY_1);
9811 if (IS_KEY(element))
9813 for (i = 0; i < MAX_PLAYERS; i++)
9815 if (trigger_player_bits & (1 << i))
9817 stored_player[i].key[KEY_NR(element)] = key_state;
9819 DrawGameDoorValues();
9827 case CA_SET_PLAYER_SPEED:
9829 for (i = 0; i < MAX_PLAYERS; i++)
9831 if (trigger_player_bits & (1 << i))
9833 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9835 if (action_arg == CA_ARG_SPEED_FASTER &&
9836 stored_player[i].cannot_move)
9838 action_arg_number = STEPSIZE_VERY_SLOW;
9840 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9841 action_arg == CA_ARG_SPEED_FASTER)
9843 action_arg_number = 2;
9844 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9847 else if (action_arg == CA_ARG_NUMBER_RESET)
9849 action_arg_number = level.initial_player_stepsize[i];
9853 getModifiedActionNumber(move_stepsize,
9856 action_arg_number_min,
9857 action_arg_number_max);
9859 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9866 case CA_SET_PLAYER_SHIELD:
9868 for (i = 0; i < MAX_PLAYERS; i++)
9870 if (trigger_player_bits & (1 << i))
9872 if (action_arg == CA_ARG_SHIELD_OFF)
9874 stored_player[i].shield_normal_time_left = 0;
9875 stored_player[i].shield_deadly_time_left = 0;
9877 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9879 stored_player[i].shield_normal_time_left = 999999;
9881 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9883 stored_player[i].shield_normal_time_left = 999999;
9884 stored_player[i].shield_deadly_time_left = 999999;
9892 case CA_SET_PLAYER_GRAVITY:
9894 for (i = 0; i < MAX_PLAYERS; i++)
9896 if (trigger_player_bits & (1 << i))
9898 stored_player[i].gravity =
9899 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9900 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9901 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9902 stored_player[i].gravity);
9909 case CA_SET_PLAYER_ARTWORK:
9911 for (i = 0; i < MAX_PLAYERS; i++)
9913 if (trigger_player_bits & (1 << i))
9915 int artwork_element = action_arg_element;
9917 if (action_arg == CA_ARG_ELEMENT_RESET)
9919 (level.use_artwork_element[i] ? level.artwork_element[i] :
9920 stored_player[i].element_nr);
9922 if (stored_player[i].artwork_element != artwork_element)
9923 stored_player[i].Frame = 0;
9925 stored_player[i].artwork_element = artwork_element;
9927 SetPlayerWaiting(&stored_player[i], FALSE);
9929 /* set number of special actions for bored and sleeping animation */
9930 stored_player[i].num_special_action_bored =
9931 get_num_special_action(artwork_element,
9932 ACTION_BORING_1, ACTION_BORING_LAST);
9933 stored_player[i].num_special_action_sleeping =
9934 get_num_special_action(artwork_element,
9935 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9942 case CA_SET_PLAYER_INVENTORY:
9944 for (i = 0; i < MAX_PLAYERS; i++)
9946 struct PlayerInfo *player = &stored_player[i];
9949 if (trigger_player_bits & (1 << i))
9951 int inventory_element = action_arg_element;
9953 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9954 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9955 action_arg == CA_ARG_ELEMENT_ACTION)
9957 int element = inventory_element;
9958 int collect_count = element_info[element].collect_count_initial;
9960 if (!IS_CUSTOM_ELEMENT(element))
9963 if (collect_count == 0)
9964 player->inventory_infinite_element = element;
9966 for (k = 0; k < collect_count; k++)
9967 if (player->inventory_size < MAX_INVENTORY_SIZE)
9968 player->inventory_element[player->inventory_size++] =
9971 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9972 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9973 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9975 if (player->inventory_infinite_element != EL_UNDEFINED &&
9976 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9977 action_arg_element_raw))
9978 player->inventory_infinite_element = EL_UNDEFINED;
9980 for (k = 0, j = 0; j < player->inventory_size; j++)
9982 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9983 action_arg_element_raw))
9984 player->inventory_element[k++] = player->inventory_element[j];
9987 player->inventory_size = k;
9989 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9991 if (player->inventory_size > 0)
9993 for (j = 0; j < player->inventory_size - 1; j++)
9994 player->inventory_element[j] = player->inventory_element[j + 1];
9996 player->inventory_size--;
9999 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10001 if (player->inventory_size > 0)
10002 player->inventory_size--;
10004 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10006 player->inventory_infinite_element = EL_UNDEFINED;
10007 player->inventory_size = 0;
10009 else if (action_arg == CA_ARG_INVENTORY_RESET)
10011 player->inventory_infinite_element = EL_UNDEFINED;
10012 player->inventory_size = 0;
10014 if (level.use_initial_inventory[i])
10016 for (j = 0; j < level.initial_inventory_size[i]; j++)
10018 int element = level.initial_inventory_content[i][j];
10019 int collect_count = element_info[element].collect_count_initial;
10021 if (!IS_CUSTOM_ELEMENT(element))
10024 if (collect_count == 0)
10025 player->inventory_infinite_element = element;
10027 for (k = 0; k < collect_count; k++)
10028 if (player->inventory_size < MAX_INVENTORY_SIZE)
10029 player->inventory_element[player->inventory_size++] =
10040 /* ---------- CE actions ---------------------------------------------- */
10042 case CA_SET_CE_VALUE:
10044 int last_ce_value = CustomValue[x][y];
10046 CustomValue[x][y] = action_arg_number_new;
10048 if (CustomValue[x][y] != last_ce_value)
10050 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10051 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10053 if (CustomValue[x][y] == 0)
10055 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10056 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10063 case CA_SET_CE_SCORE:
10065 int last_ce_score = ei->collect_score;
10067 ei->collect_score = action_arg_number_new;
10069 if (ei->collect_score != last_ce_score)
10071 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10072 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10074 if (ei->collect_score == 0)
10078 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10079 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10082 This is a very special case that seems to be a mixture between
10083 CheckElementChange() and CheckTriggeredElementChange(): while
10084 the first one only affects single elements that are triggered
10085 directly, the second one affects multiple elements in the playfield
10086 that are triggered indirectly by another element. This is a third
10087 case: Changing the CE score always affects multiple identical CEs,
10088 so every affected CE must be checked, not only the single CE for
10089 which the CE score was changed in the first place (as every instance
10090 of that CE shares the same CE score, and therefore also can change)!
10092 SCAN_PLAYFIELD(xx, yy)
10094 if (Feld[xx][yy] == element)
10095 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10096 CE_SCORE_GETS_ZERO);
10104 case CA_SET_CE_ARTWORK:
10106 int artwork_element = action_arg_element;
10107 boolean reset_frame = FALSE;
10110 if (action_arg == CA_ARG_ELEMENT_RESET)
10111 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10114 if (ei->gfx_element != artwork_element)
10115 reset_frame = TRUE;
10117 ei->gfx_element = artwork_element;
10119 SCAN_PLAYFIELD(xx, yy)
10121 if (Feld[xx][yy] == element)
10125 ResetGfxAnimation(xx, yy);
10126 ResetRandomAnimationValue(xx, yy);
10129 TEST_DrawLevelField(xx, yy);
10136 /* ---------- engine actions ------------------------------------------ */
10138 case CA_SET_ENGINE_SCAN_MODE:
10140 InitPlayfieldScanMode(action_arg);
10150 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10152 int old_element = Feld[x][y];
10153 int new_element = GetElementFromGroupElement(element);
10154 int previous_move_direction = MovDir[x][y];
10155 int last_ce_value = CustomValue[x][y];
10156 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10157 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10158 boolean add_player_onto_element = (new_element_is_player &&
10159 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10160 IS_WALKABLE(old_element));
10162 if (!add_player_onto_element)
10164 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10165 RemoveMovingField(x, y);
10169 Feld[x][y] = new_element;
10171 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10172 MovDir[x][y] = previous_move_direction;
10174 if (element_info[new_element].use_last_ce_value)
10175 CustomValue[x][y] = last_ce_value;
10177 InitField_WithBug1(x, y, FALSE);
10179 new_element = Feld[x][y]; /* element may have changed */
10181 ResetGfxAnimation(x, y);
10182 ResetRandomAnimationValue(x, y);
10184 TEST_DrawLevelField(x, y);
10186 if (GFX_CRUMBLED(new_element))
10187 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10190 /* check if element under the player changes from accessible to unaccessible
10191 (needed for special case of dropping element which then changes) */
10192 /* (must be checked after creating new element for walkable group elements) */
10193 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10194 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10201 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10202 if (new_element_is_player)
10203 RelocatePlayer(x, y, new_element);
10206 ChangeCount[x][y]++; /* count number of changes in the same frame */
10208 TestIfBadThingTouchesPlayer(x, y);
10209 TestIfPlayerTouchesCustomElement(x, y);
10210 TestIfElementTouchesCustomElement(x, y);
10213 static void CreateField(int x, int y, int element)
10215 CreateFieldExt(x, y, element, FALSE);
10218 static void CreateElementFromChange(int x, int y, int element)
10220 element = GET_VALID_RUNTIME_ELEMENT(element);
10222 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10224 int old_element = Feld[x][y];
10226 /* prevent changed element from moving in same engine frame
10227 unless both old and new element can either fall or move */
10228 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10229 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10233 CreateFieldExt(x, y, element, TRUE);
10236 static boolean ChangeElement(int x, int y, int element, int page)
10238 struct ElementInfo *ei = &element_info[element];
10239 struct ElementChangeInfo *change = &ei->change_page[page];
10240 int ce_value = CustomValue[x][y];
10241 int ce_score = ei->collect_score;
10242 int target_element;
10243 int old_element = Feld[x][y];
10245 /* always use default change event to prevent running into a loop */
10246 if (ChangeEvent[x][y] == -1)
10247 ChangeEvent[x][y] = CE_DELAY;
10249 if (ChangeEvent[x][y] == CE_DELAY)
10251 /* reset actual trigger element, trigger player and action element */
10252 change->actual_trigger_element = EL_EMPTY;
10253 change->actual_trigger_player = EL_EMPTY;
10254 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10255 change->actual_trigger_side = CH_SIDE_NONE;
10256 change->actual_trigger_ce_value = 0;
10257 change->actual_trigger_ce_score = 0;
10260 /* do not change elements more than a specified maximum number of changes */
10261 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10264 ChangeCount[x][y]++; /* count number of changes in the same frame */
10266 if (change->explode)
10273 if (change->use_target_content)
10275 boolean complete_replace = TRUE;
10276 boolean can_replace[3][3];
10279 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10282 boolean is_walkable;
10283 boolean is_diggable;
10284 boolean is_collectible;
10285 boolean is_removable;
10286 boolean is_destructible;
10287 int ex = x + xx - 1;
10288 int ey = y + yy - 1;
10289 int content_element = change->target_content.e[xx][yy];
10292 can_replace[xx][yy] = TRUE;
10294 if (ex == x && ey == y) /* do not check changing element itself */
10297 if (content_element == EL_EMPTY_SPACE)
10299 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10304 if (!IN_LEV_FIELD(ex, ey))
10306 can_replace[xx][yy] = FALSE;
10307 complete_replace = FALSE;
10314 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10315 e = MovingOrBlocked2Element(ex, ey);
10317 is_empty = (IS_FREE(ex, ey) ||
10318 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10320 is_walkable = (is_empty || IS_WALKABLE(e));
10321 is_diggable = (is_empty || IS_DIGGABLE(e));
10322 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10323 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10324 is_removable = (is_diggable || is_collectible);
10326 can_replace[xx][yy] =
10327 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10328 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10329 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10330 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10331 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10332 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10333 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10335 if (!can_replace[xx][yy])
10336 complete_replace = FALSE;
10339 if (!change->only_if_complete || complete_replace)
10341 boolean something_has_changed = FALSE;
10343 if (change->only_if_complete && change->use_random_replace &&
10344 RND(100) < change->random_percentage)
10347 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10349 int ex = x + xx - 1;
10350 int ey = y + yy - 1;
10351 int content_element;
10353 if (can_replace[xx][yy] && (!change->use_random_replace ||
10354 RND(100) < change->random_percentage))
10356 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10357 RemoveMovingField(ex, ey);
10359 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10361 content_element = change->target_content.e[xx][yy];
10362 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10363 ce_value, ce_score);
10365 CreateElementFromChange(ex, ey, target_element);
10367 something_has_changed = TRUE;
10369 /* for symmetry reasons, freeze newly created border elements */
10370 if (ex != x || ey != y)
10371 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10375 if (something_has_changed)
10377 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10378 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10384 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10385 ce_value, ce_score);
10387 if (element == EL_DIAGONAL_GROWING ||
10388 element == EL_DIAGONAL_SHRINKING)
10390 target_element = Store[x][y];
10392 Store[x][y] = EL_EMPTY;
10395 CreateElementFromChange(x, y, target_element);
10397 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10398 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10401 /* this uses direct change before indirect change */
10402 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10407 static void HandleElementChange(int x, int y, int page)
10409 int element = MovingOrBlocked2Element(x, y);
10410 struct ElementInfo *ei = &element_info[element];
10411 struct ElementChangeInfo *change = &ei->change_page[page];
10412 boolean handle_action_before_change = FALSE;
10415 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10416 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10419 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10420 x, y, element, element_info[element].token_name);
10421 printf("HandleElementChange(): This should never happen!\n");
10426 /* this can happen with classic bombs on walkable, changing elements */
10427 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10432 if (ChangeDelay[x][y] == 0) /* initialize element change */
10434 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10436 if (change->can_change)
10438 /* !!! not clear why graphic animation should be reset at all here !!! */
10439 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10440 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10443 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10445 When using an animation frame delay of 1 (this only happens with
10446 "sp_zonk.moving.left/right" in the classic graphics), the default
10447 (non-moving) animation shows wrong animation frames (while the
10448 moving animation, like "sp_zonk.moving.left/right", is correct,
10449 so this graphical bug never shows up with the classic graphics).
10450 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10451 be drawn instead of the correct frames 0,1,2,3. This is caused by
10452 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10453 an element change: First when the change delay ("ChangeDelay[][]")
10454 counter has reached zero after decrementing, then a second time in
10455 the next frame (after "GfxFrame[][]" was already incremented) when
10456 "ChangeDelay[][]" is reset to the initial delay value again.
10458 This causes frame 0 to be drawn twice, while the last frame won't
10459 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10461 As some animations may already be cleverly designed around this bug
10462 (at least the "Snake Bite" snake tail animation does this), it cannot
10463 simply be fixed here without breaking such existing animations.
10464 Unfortunately, it cannot easily be detected if a graphics set was
10465 designed "before" or "after" the bug was fixed. As a workaround,
10466 a new graphics set option "game.graphics_engine_version" was added
10467 to be able to specify the game's major release version for which the
10468 graphics set was designed, which can then be used to decide if the
10469 bugfix should be used (version 4 and above) or not (version 3 or
10470 below, or if no version was specified at all, as with old sets).
10472 (The wrong/fixed animation frames can be tested with the test level set
10473 "test_gfxframe" and level "000", which contains a specially prepared
10474 custom element at level position (x/y) == (11/9) which uses the zonk
10475 animation mentioned above. Using "game.graphics_engine_version: 4"
10476 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10477 This can also be seen from the debug output for this test element.)
10480 /* when a custom element is about to change (for example by change delay),
10481 do not reset graphic animation when the custom element is moving */
10482 if (game.graphics_engine_version < 4 &&
10485 ResetGfxAnimation(x, y);
10486 ResetRandomAnimationValue(x, y);
10489 if (change->pre_change_function)
10490 change->pre_change_function(x, y);
10494 ChangeDelay[x][y]--;
10496 if (ChangeDelay[x][y] != 0) /* continue element change */
10498 if (change->can_change)
10500 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10502 if (IS_ANIMATED(graphic))
10503 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10505 if (change->change_function)
10506 change->change_function(x, y);
10509 else /* finish element change */
10511 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10513 page = ChangePage[x][y];
10514 ChangePage[x][y] = -1;
10516 change = &ei->change_page[page];
10519 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10521 ChangeDelay[x][y] = 1; /* try change after next move step */
10522 ChangePage[x][y] = page; /* remember page to use for change */
10527 /* special case: set new level random seed before changing element */
10528 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10529 handle_action_before_change = TRUE;
10531 if (change->has_action && handle_action_before_change)
10532 ExecuteCustomElementAction(x, y, element, page);
10534 if (change->can_change)
10536 if (ChangeElement(x, y, element, page))
10538 if (change->post_change_function)
10539 change->post_change_function(x, y);
10543 if (change->has_action && !handle_action_before_change)
10544 ExecuteCustomElementAction(x, y, element, page);
10548 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10549 int trigger_element,
10551 int trigger_player,
10555 boolean change_done_any = FALSE;
10556 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10559 if (!(trigger_events[trigger_element][trigger_event]))
10562 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10564 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10566 int element = EL_CUSTOM_START + i;
10567 boolean change_done = FALSE;
10570 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10571 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10574 for (p = 0; p < element_info[element].num_change_pages; p++)
10576 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10578 if (change->can_change_or_has_action &&
10579 change->has_event[trigger_event] &&
10580 change->trigger_side & trigger_side &&
10581 change->trigger_player & trigger_player &&
10582 change->trigger_page & trigger_page_bits &&
10583 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10585 change->actual_trigger_element = trigger_element;
10586 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10587 change->actual_trigger_player_bits = trigger_player;
10588 change->actual_trigger_side = trigger_side;
10589 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10590 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10592 if ((change->can_change && !change_done) || change->has_action)
10596 SCAN_PLAYFIELD(x, y)
10598 if (Feld[x][y] == element)
10600 if (change->can_change && !change_done)
10602 /* if element already changed in this frame, not only prevent
10603 another element change (checked in ChangeElement()), but
10604 also prevent additional element actions for this element */
10606 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10607 !level.use_action_after_change_bug)
10610 ChangeDelay[x][y] = 1;
10611 ChangeEvent[x][y] = trigger_event;
10613 HandleElementChange(x, y, p);
10615 else if (change->has_action)
10617 /* if element already changed in this frame, not only prevent
10618 another element change (checked in ChangeElement()), but
10619 also prevent additional element actions for this element */
10621 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10622 !level.use_action_after_change_bug)
10625 ExecuteCustomElementAction(x, y, element, p);
10626 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10631 if (change->can_change)
10633 change_done = TRUE;
10634 change_done_any = TRUE;
10641 RECURSION_LOOP_DETECTION_END();
10643 return change_done_any;
10646 static boolean CheckElementChangeExt(int x, int y,
10648 int trigger_element,
10650 int trigger_player,
10653 boolean change_done = FALSE;
10656 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10657 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10660 if (Feld[x][y] == EL_BLOCKED)
10662 Blocked2Moving(x, y, &x, &y);
10663 element = Feld[x][y];
10666 /* check if element has already changed or is about to change after moving */
10667 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10668 Feld[x][y] != element) ||
10670 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10671 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10672 ChangePage[x][y] != -1)))
10675 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10677 for (p = 0; p < element_info[element].num_change_pages; p++)
10679 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10681 /* check trigger element for all events where the element that is checked
10682 for changing interacts with a directly adjacent element -- this is
10683 different to element changes that affect other elements to change on the
10684 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10685 boolean check_trigger_element =
10686 (trigger_event == CE_TOUCHING_X ||
10687 trigger_event == CE_HITTING_X ||
10688 trigger_event == CE_HIT_BY_X ||
10689 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10691 if (change->can_change_or_has_action &&
10692 change->has_event[trigger_event] &&
10693 change->trigger_side & trigger_side &&
10694 change->trigger_player & trigger_player &&
10695 (!check_trigger_element ||
10696 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10698 change->actual_trigger_element = trigger_element;
10699 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10700 change->actual_trigger_player_bits = trigger_player;
10701 change->actual_trigger_side = trigger_side;
10702 change->actual_trigger_ce_value = CustomValue[x][y];
10703 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10705 /* special case: trigger element not at (x,y) position for some events */
10706 if (check_trigger_element)
10718 { 0, 0 }, { 0, 0 }, { 0, 0 },
10722 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10723 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10725 change->actual_trigger_ce_value = CustomValue[xx][yy];
10726 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10729 if (change->can_change && !change_done)
10731 ChangeDelay[x][y] = 1;
10732 ChangeEvent[x][y] = trigger_event;
10734 HandleElementChange(x, y, p);
10736 change_done = TRUE;
10738 else if (change->has_action)
10740 ExecuteCustomElementAction(x, y, element, p);
10741 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10746 RECURSION_LOOP_DETECTION_END();
10748 return change_done;
10751 static void PlayPlayerSound(struct PlayerInfo *player)
10753 int jx = player->jx, jy = player->jy;
10754 int sound_element = player->artwork_element;
10755 int last_action = player->last_action_waiting;
10756 int action = player->action_waiting;
10758 if (player->is_waiting)
10760 if (action != last_action)
10761 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10763 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10767 if (action != last_action)
10768 StopSound(element_info[sound_element].sound[last_action]);
10770 if (last_action == ACTION_SLEEPING)
10771 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10775 static void PlayAllPlayersSound()
10779 for (i = 0; i < MAX_PLAYERS; i++)
10780 if (stored_player[i].active)
10781 PlayPlayerSound(&stored_player[i]);
10784 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10786 boolean last_waiting = player->is_waiting;
10787 int move_dir = player->MovDir;
10789 player->dir_waiting = move_dir;
10790 player->last_action_waiting = player->action_waiting;
10794 if (!last_waiting) /* not waiting -> waiting */
10796 player->is_waiting = TRUE;
10798 player->frame_counter_bored =
10800 game.player_boring_delay_fixed +
10801 GetSimpleRandom(game.player_boring_delay_random);
10802 player->frame_counter_sleeping =
10804 game.player_sleeping_delay_fixed +
10805 GetSimpleRandom(game.player_sleeping_delay_random);
10807 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10810 if (game.player_sleeping_delay_fixed +
10811 game.player_sleeping_delay_random > 0 &&
10812 player->anim_delay_counter == 0 &&
10813 player->post_delay_counter == 0 &&
10814 FrameCounter >= player->frame_counter_sleeping)
10815 player->is_sleeping = TRUE;
10816 else if (game.player_boring_delay_fixed +
10817 game.player_boring_delay_random > 0 &&
10818 FrameCounter >= player->frame_counter_bored)
10819 player->is_bored = TRUE;
10821 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10822 player->is_bored ? ACTION_BORING :
10825 if (player->is_sleeping && player->use_murphy)
10827 /* special case for sleeping Murphy when leaning against non-free tile */
10829 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10830 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10831 !IS_MOVING(player->jx - 1, player->jy)))
10832 move_dir = MV_LEFT;
10833 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10834 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10835 !IS_MOVING(player->jx + 1, player->jy)))
10836 move_dir = MV_RIGHT;
10838 player->is_sleeping = FALSE;
10840 player->dir_waiting = move_dir;
10843 if (player->is_sleeping)
10845 if (player->num_special_action_sleeping > 0)
10847 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10849 int last_special_action = player->special_action_sleeping;
10850 int num_special_action = player->num_special_action_sleeping;
10851 int special_action =
10852 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10853 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10854 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10855 last_special_action + 1 : ACTION_SLEEPING);
10856 int special_graphic =
10857 el_act_dir2img(player->artwork_element, special_action, move_dir);
10859 player->anim_delay_counter =
10860 graphic_info[special_graphic].anim_delay_fixed +
10861 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10862 player->post_delay_counter =
10863 graphic_info[special_graphic].post_delay_fixed +
10864 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10866 player->special_action_sleeping = special_action;
10869 if (player->anim_delay_counter > 0)
10871 player->action_waiting = player->special_action_sleeping;
10872 player->anim_delay_counter--;
10874 else if (player->post_delay_counter > 0)
10876 player->post_delay_counter--;
10880 else if (player->is_bored)
10882 if (player->num_special_action_bored > 0)
10884 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10886 int special_action =
10887 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10888 int special_graphic =
10889 el_act_dir2img(player->artwork_element, special_action, move_dir);
10891 player->anim_delay_counter =
10892 graphic_info[special_graphic].anim_delay_fixed +
10893 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10894 player->post_delay_counter =
10895 graphic_info[special_graphic].post_delay_fixed +
10896 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10898 player->special_action_bored = special_action;
10901 if (player->anim_delay_counter > 0)
10903 player->action_waiting = player->special_action_bored;
10904 player->anim_delay_counter--;
10906 else if (player->post_delay_counter > 0)
10908 player->post_delay_counter--;
10913 else if (last_waiting) /* waiting -> not waiting */
10915 player->is_waiting = FALSE;
10916 player->is_bored = FALSE;
10917 player->is_sleeping = FALSE;
10919 player->frame_counter_bored = -1;
10920 player->frame_counter_sleeping = -1;
10922 player->anim_delay_counter = 0;
10923 player->post_delay_counter = 0;
10925 player->dir_waiting = player->MovDir;
10926 player->action_waiting = ACTION_DEFAULT;
10928 player->special_action_bored = ACTION_DEFAULT;
10929 player->special_action_sleeping = ACTION_DEFAULT;
10933 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10935 if ((!player->is_moving && player->was_moving) ||
10936 (player->MovPos == 0 && player->was_moving) ||
10937 (player->is_snapping && !player->was_snapping) ||
10938 (player->is_dropping && !player->was_dropping))
10940 if (!CheckSaveEngineSnapshotToList())
10943 player->was_moving = FALSE;
10944 player->was_snapping = TRUE;
10945 player->was_dropping = TRUE;
10949 if (player->is_moving)
10950 player->was_moving = TRUE;
10952 if (!player->is_snapping)
10953 player->was_snapping = FALSE;
10955 if (!player->is_dropping)
10956 player->was_dropping = FALSE;
10960 static void CheckSingleStepMode(struct PlayerInfo *player)
10962 if (tape.single_step && tape.recording && !tape.pausing)
10964 /* as it is called "single step mode", just return to pause mode when the
10965 player stopped moving after one tile (or never starts moving at all) */
10966 if (!player->is_moving &&
10967 !player->is_pushing &&
10968 !player->is_dropping_pressed)
10970 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10971 SnapField(player, 0, 0); /* stop snapping */
10975 CheckSaveEngineSnapshot(player);
10978 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10980 int left = player_action & JOY_LEFT;
10981 int right = player_action & JOY_RIGHT;
10982 int up = player_action & JOY_UP;
10983 int down = player_action & JOY_DOWN;
10984 int button1 = player_action & JOY_BUTTON_1;
10985 int button2 = player_action & JOY_BUTTON_2;
10986 int dx = (left ? -1 : right ? 1 : 0);
10987 int dy = (up ? -1 : down ? 1 : 0);
10989 if (!player->active || tape.pausing)
10995 SnapField(player, dx, dy);
10999 DropElement(player);
11001 MovePlayer(player, dx, dy);
11004 CheckSingleStepMode(player);
11006 SetPlayerWaiting(player, FALSE);
11008 return player_action;
11012 /* no actions for this player (no input at player's configured device) */
11014 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11015 SnapField(player, 0, 0);
11016 CheckGravityMovementWhenNotMoving(player);
11018 if (player->MovPos == 0)
11019 SetPlayerWaiting(player, TRUE);
11021 if (player->MovPos == 0) /* needed for tape.playing */
11022 player->is_moving = FALSE;
11024 player->is_dropping = FALSE;
11025 player->is_dropping_pressed = FALSE;
11026 player->drop_pressed_delay = 0;
11028 CheckSingleStepMode(player);
11034 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11037 if (!tape.use_mouse)
11040 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11041 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11042 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11045 static void SetTapeActionFromMouseAction(byte *tape_action,
11046 struct MouseActionInfo *mouse_action)
11048 if (!tape.use_mouse)
11051 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11052 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11053 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11056 static void CheckLevelTime()
11060 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11061 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11063 if (level.native_em_level->lev->home == 0) /* all players at home */
11065 PlayerWins(local_player);
11067 AllPlayersGone = TRUE;
11069 level.native_em_level->lev->home = -1;
11072 if (level.native_em_level->ply[0]->alive == 0 &&
11073 level.native_em_level->ply[1]->alive == 0 &&
11074 level.native_em_level->ply[2]->alive == 0 &&
11075 level.native_em_level->ply[3]->alive == 0) /* all dead */
11076 AllPlayersGone = TRUE;
11078 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11080 if (game_sp.LevelSolved &&
11081 !game_sp.GameOver) /* game won */
11083 PlayerWins(local_player);
11085 game_sp.GameOver = TRUE;
11087 AllPlayersGone = TRUE;
11090 if (game_sp.GameOver) /* game lost */
11091 AllPlayersGone = TRUE;
11093 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11095 if (game_mm.level_solved &&
11096 !game_mm.game_over) /* game won */
11098 PlayerWins(local_player);
11100 game_mm.game_over = TRUE;
11102 AllPlayersGone = TRUE;
11105 if (game_mm.game_over) /* game lost */
11106 AllPlayersGone = TRUE;
11109 if (TimeFrames >= FRAMES_PER_SECOND)
11114 for (i = 0; i < MAX_PLAYERS; i++)
11116 struct PlayerInfo *player = &stored_player[i];
11118 if (SHIELD_ON(player))
11120 player->shield_normal_time_left--;
11122 if (player->shield_deadly_time_left > 0)
11123 player->shield_deadly_time_left--;
11127 if (!local_player->LevelSolved && !level.use_step_counter)
11135 if (TimeLeft <= 10 && setup.time_limit)
11136 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11138 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11139 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11141 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11143 if (!TimeLeft && setup.time_limit)
11145 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11146 level.native_em_level->lev->killed_out_of_time = TRUE;
11148 for (i = 0; i < MAX_PLAYERS; i++)
11149 KillPlayer(&stored_player[i]);
11152 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11154 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11157 level.native_em_level->lev->time =
11158 (game.no_time_limit ? TimePlayed : TimeLeft);
11161 if (tape.recording || tape.playing)
11162 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11165 if (tape.recording || tape.playing)
11166 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11168 UpdateAndDisplayGameControlValues();
11171 void AdvanceFrameAndPlayerCounters(int player_nr)
11175 /* advance frame counters (global frame counter and time frame counter) */
11179 /* advance player counters (counters for move delay, move animation etc.) */
11180 for (i = 0; i < MAX_PLAYERS; i++)
11182 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11183 int move_delay_value = stored_player[i].move_delay_value;
11184 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11186 if (!advance_player_counters) /* not all players may be affected */
11189 if (move_frames == 0) /* less than one move per game frame */
11191 int stepsize = TILEX / move_delay_value;
11192 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11193 int count = (stored_player[i].is_moving ?
11194 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11196 if (count % delay == 0)
11200 stored_player[i].Frame += move_frames;
11202 if (stored_player[i].MovPos != 0)
11203 stored_player[i].StepFrame += move_frames;
11205 if (stored_player[i].move_delay > 0)
11206 stored_player[i].move_delay--;
11208 /* due to bugs in previous versions, counter must count up, not down */
11209 if (stored_player[i].push_delay != -1)
11210 stored_player[i].push_delay++;
11212 if (stored_player[i].drop_delay > 0)
11213 stored_player[i].drop_delay--;
11215 if (stored_player[i].is_dropping_pressed)
11216 stored_player[i].drop_pressed_delay++;
11220 void StartGameActions(boolean init_network_game, boolean record_tape,
11223 unsigned int new_random_seed = InitRND(random_seed);
11226 TapeStartRecording(new_random_seed);
11228 if (init_network_game)
11230 SendToServer_StartPlaying();
11238 void GameActionsExt()
11241 static unsigned int game_frame_delay = 0;
11243 unsigned int game_frame_delay_value;
11244 byte *recorded_player_action;
11245 byte summarized_player_action = 0;
11246 byte tape_action[MAX_PLAYERS];
11249 /* detect endless loops, caused by custom element programming */
11250 if (recursion_loop_detected && recursion_loop_depth == 0)
11252 char *message = getStringCat3("Internal Error! Element ",
11253 EL_NAME(recursion_loop_element),
11254 " caused endless loop! Quit the game?");
11256 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11257 EL_NAME(recursion_loop_element));
11259 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11261 recursion_loop_detected = FALSE; /* if game should be continued */
11268 if (game.restart_level)
11269 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11271 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11272 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11274 if (level.native_em_level->lev->home == 0) /* all players at home */
11276 PlayerWins(local_player);
11278 AllPlayersGone = TRUE;
11280 level.native_em_level->lev->home = -1;
11283 if (level.native_em_level->ply[0]->alive == 0 &&
11284 level.native_em_level->ply[1]->alive == 0 &&
11285 level.native_em_level->ply[2]->alive == 0 &&
11286 level.native_em_level->ply[3]->alive == 0) /* all dead */
11287 AllPlayersGone = TRUE;
11289 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11291 if (game_sp.LevelSolved &&
11292 !game_sp.GameOver) /* game won */
11294 PlayerWins(local_player);
11296 game_sp.GameOver = TRUE;
11298 AllPlayersGone = TRUE;
11301 if (game_sp.GameOver) /* game lost */
11302 AllPlayersGone = TRUE;
11304 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11306 if (game_mm.level_solved &&
11307 !game_mm.game_over) /* game won */
11309 PlayerWins(local_player);
11311 game_mm.game_over = TRUE;
11313 AllPlayersGone = TRUE;
11316 if (game_mm.game_over) /* game lost */
11317 AllPlayersGone = TRUE;
11320 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11323 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11326 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11329 game_frame_delay_value =
11330 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11332 if (tape.playing && tape.warp_forward && !tape.pausing)
11333 game_frame_delay_value = 0;
11335 SetVideoFrameDelay(game_frame_delay_value);
11339 /* ---------- main game synchronization point ---------- */
11341 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11343 printf("::: skip == %d\n", skip);
11346 /* ---------- main game synchronization point ---------- */
11348 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11352 if (network_playing && !network_player_action_received)
11354 /* try to get network player actions in time */
11356 /* last chance to get network player actions without main loop delay */
11357 HandleNetworking();
11359 /* game was quit by network peer */
11360 if (game_status != GAME_MODE_PLAYING)
11363 if (!network_player_action_received)
11364 return; /* failed to get network player actions in time */
11366 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11372 /* at this point we know that we really continue executing the game */
11374 network_player_action_received = FALSE;
11376 /* when playing tape, read previously recorded player input from tape data */
11377 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11379 local_player->effective_mouse_action = local_player->mouse_action;
11381 if (recorded_player_action != NULL)
11382 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11383 recorded_player_action);
11385 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11389 if (tape.set_centered_player)
11391 game.centered_player_nr_next = tape.centered_player_nr_next;
11392 game.set_centered_player = TRUE;
11395 for (i = 0; i < MAX_PLAYERS; i++)
11397 summarized_player_action |= stored_player[i].action;
11399 if (!network_playing && (game.team_mode || tape.playing))
11400 stored_player[i].effective_action = stored_player[i].action;
11403 if (network_playing)
11404 SendToServer_MovePlayer(summarized_player_action);
11406 // summarize all actions at local players mapped input device position
11407 // (this allows using different input devices in single player mode)
11408 if (!network.enabled && !game.team_mode)
11409 stored_player[map_player_action[local_player->index_nr]].effective_action =
11410 summarized_player_action;
11412 if (tape.recording &&
11414 setup.input_on_focus &&
11415 game.centered_player_nr != -1)
11417 for (i = 0; i < MAX_PLAYERS; i++)
11418 stored_player[i].effective_action =
11419 (i == game.centered_player_nr ? summarized_player_action : 0);
11422 if (recorded_player_action != NULL)
11423 for (i = 0; i < MAX_PLAYERS; i++)
11424 stored_player[i].effective_action = recorded_player_action[i];
11426 for (i = 0; i < MAX_PLAYERS; i++)
11428 tape_action[i] = stored_player[i].effective_action;
11430 /* (this may happen in the RND game engine if a player was not present on
11431 the playfield on level start, but appeared later from a custom element */
11432 if (setup.team_mode &&
11435 !tape.player_participates[i])
11436 tape.player_participates[i] = TRUE;
11439 SetTapeActionFromMouseAction(tape_action,
11440 &local_player->effective_mouse_action);
11442 /* only record actions from input devices, but not programmed actions */
11443 if (tape.recording)
11444 TapeRecordAction(tape_action);
11446 #if USE_NEW_PLAYER_ASSIGNMENTS
11447 // !!! also map player actions in single player mode !!!
11448 // if (game.team_mode)
11451 byte mapped_action[MAX_PLAYERS];
11453 #if DEBUG_PLAYER_ACTIONS
11455 for (i = 0; i < MAX_PLAYERS; i++)
11456 printf(" %d, ", stored_player[i].effective_action);
11459 for (i = 0; i < MAX_PLAYERS; i++)
11460 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11462 for (i = 0; i < MAX_PLAYERS; i++)
11463 stored_player[i].effective_action = mapped_action[i];
11465 #if DEBUG_PLAYER_ACTIONS
11467 for (i = 0; i < MAX_PLAYERS; i++)
11468 printf(" %d, ", stored_player[i].effective_action);
11472 #if DEBUG_PLAYER_ACTIONS
11476 for (i = 0; i < MAX_PLAYERS; i++)
11477 printf(" %d, ", stored_player[i].effective_action);
11483 for (i = 0; i < MAX_PLAYERS; i++)
11485 // allow engine snapshot in case of changed movement attempt
11486 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11487 (stored_player[i].effective_action & KEY_MOTION))
11488 game.snapshot.changed_action = TRUE;
11490 // allow engine snapshot in case of snapping/dropping attempt
11491 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11492 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11493 game.snapshot.changed_action = TRUE;
11495 game.snapshot.last_action[i] = stored_player[i].effective_action;
11498 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11500 GameActions_EM_Main();
11502 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11504 GameActions_SP_Main();
11506 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11508 GameActions_MM_Main();
11512 GameActions_RND_Main();
11515 BlitScreenToBitmap(backbuffer);
11519 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11521 if (global.show_frames_per_second)
11523 static unsigned int fps_counter = 0;
11524 static int fps_frames = 0;
11525 unsigned int fps_delay_ms = Counter() - fps_counter;
11529 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11531 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11534 fps_counter = Counter();
11536 /* always draw FPS to screen after FPS value was updated */
11537 redraw_mask |= REDRAW_FPS;
11540 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11541 if (GetDrawDeactivationMask() == REDRAW_NONE)
11542 redraw_mask |= REDRAW_FPS;
11546 static void GameActions_CheckSaveEngineSnapshot()
11548 if (!game.snapshot.save_snapshot)
11551 // clear flag for saving snapshot _before_ saving snapshot
11552 game.snapshot.save_snapshot = FALSE;
11554 SaveEngineSnapshotToList();
11561 GameActions_CheckSaveEngineSnapshot();
11564 void GameActions_EM_Main()
11566 byte effective_action[MAX_PLAYERS];
11567 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11570 for (i = 0; i < MAX_PLAYERS; i++)
11571 effective_action[i] = stored_player[i].effective_action;
11573 GameActions_EM(effective_action, warp_mode);
11576 void GameActions_SP_Main()
11578 byte effective_action[MAX_PLAYERS];
11579 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11582 for (i = 0; i < MAX_PLAYERS; i++)
11583 effective_action[i] = stored_player[i].effective_action;
11585 GameActions_SP(effective_action, warp_mode);
11587 for (i = 0; i < MAX_PLAYERS; i++)
11589 if (stored_player[i].force_dropping)
11590 stored_player[i].action |= KEY_BUTTON_DROP;
11592 stored_player[i].force_dropping = FALSE;
11596 void GameActions_MM_Main()
11598 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11600 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11603 void GameActions_RND_Main()
11608 void GameActions_RND()
11610 int magic_wall_x = 0, magic_wall_y = 0;
11611 int i, x, y, element, graphic, last_gfx_frame;
11613 InitPlayfieldScanModeVars();
11615 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11617 SCAN_PLAYFIELD(x, y)
11619 ChangeCount[x][y] = 0;
11620 ChangeEvent[x][y] = -1;
11624 if (game.set_centered_player)
11626 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11628 /* switching to "all players" only possible if all players fit to screen */
11629 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11631 game.centered_player_nr_next = game.centered_player_nr;
11632 game.set_centered_player = FALSE;
11635 /* do not switch focus to non-existing (or non-active) player */
11636 if (game.centered_player_nr_next >= 0 &&
11637 !stored_player[game.centered_player_nr_next].active)
11639 game.centered_player_nr_next = game.centered_player_nr;
11640 game.set_centered_player = FALSE;
11644 if (game.set_centered_player &&
11645 ScreenMovPos == 0) /* screen currently aligned at tile position */
11649 if (game.centered_player_nr_next == -1)
11651 setScreenCenteredToAllPlayers(&sx, &sy);
11655 sx = stored_player[game.centered_player_nr_next].jx;
11656 sy = stored_player[game.centered_player_nr_next].jy;
11659 game.centered_player_nr = game.centered_player_nr_next;
11660 game.set_centered_player = FALSE;
11662 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11663 DrawGameDoorValues();
11666 for (i = 0; i < MAX_PLAYERS; i++)
11668 int actual_player_action = stored_player[i].effective_action;
11671 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11672 - rnd_equinox_tetrachloride 048
11673 - rnd_equinox_tetrachloride_ii 096
11674 - rnd_emanuel_schmieg 002
11675 - doctor_sloan_ww 001, 020
11677 if (stored_player[i].MovPos == 0)
11678 CheckGravityMovement(&stored_player[i]);
11681 /* overwrite programmed action with tape action */
11682 if (stored_player[i].programmed_action)
11683 actual_player_action = stored_player[i].programmed_action;
11685 PlayerActions(&stored_player[i], actual_player_action);
11687 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11690 ScrollScreen(NULL, SCROLL_GO_ON);
11692 /* for backwards compatibility, the following code emulates a fixed bug that
11693 occured when pushing elements (causing elements that just made their last
11694 pushing step to already (if possible) make their first falling step in the
11695 same game frame, which is bad); this code is also needed to use the famous
11696 "spring push bug" which is used in older levels and might be wanted to be
11697 used also in newer levels, but in this case the buggy pushing code is only
11698 affecting the "spring" element and no other elements */
11700 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11702 for (i = 0; i < MAX_PLAYERS; i++)
11704 struct PlayerInfo *player = &stored_player[i];
11705 int x = player->jx;
11706 int y = player->jy;
11708 if (player->active && player->is_pushing && player->is_moving &&
11710 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11711 Feld[x][y] == EL_SPRING))
11713 ContinueMoving(x, y);
11715 /* continue moving after pushing (this is actually a bug) */
11716 if (!IS_MOVING(x, y))
11717 Stop[x][y] = FALSE;
11722 SCAN_PLAYFIELD(x, y)
11724 ChangeCount[x][y] = 0;
11725 ChangeEvent[x][y] = -1;
11727 /* this must be handled before main playfield loop */
11728 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11731 if (MovDelay[x][y] <= 0)
11735 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11738 if (MovDelay[x][y] <= 0)
11741 TEST_DrawLevelField(x, y);
11743 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11748 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11750 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11751 printf("GameActions(): This should never happen!\n");
11753 ChangePage[x][y] = -1;
11757 Stop[x][y] = FALSE;
11758 if (WasJustMoving[x][y] > 0)
11759 WasJustMoving[x][y]--;
11760 if (WasJustFalling[x][y] > 0)
11761 WasJustFalling[x][y]--;
11762 if (CheckCollision[x][y] > 0)
11763 CheckCollision[x][y]--;
11764 if (CheckImpact[x][y] > 0)
11765 CheckImpact[x][y]--;
11769 /* reset finished pushing action (not done in ContinueMoving() to allow
11770 continuous pushing animation for elements with zero push delay) */
11771 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11773 ResetGfxAnimation(x, y);
11774 TEST_DrawLevelField(x, y);
11778 if (IS_BLOCKED(x, y))
11782 Blocked2Moving(x, y, &oldx, &oldy);
11783 if (!IS_MOVING(oldx, oldy))
11785 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11786 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11787 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11788 printf("GameActions(): This should never happen!\n");
11794 SCAN_PLAYFIELD(x, y)
11796 element = Feld[x][y];
11797 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11798 last_gfx_frame = GfxFrame[x][y];
11800 ResetGfxFrame(x, y);
11802 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11803 DrawLevelGraphicAnimation(x, y, graphic);
11805 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11806 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11807 ResetRandomAnimationValue(x, y);
11809 SetRandomAnimationValue(x, y);
11811 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11813 if (IS_INACTIVE(element))
11815 if (IS_ANIMATED(graphic))
11816 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11821 /* this may take place after moving, so 'element' may have changed */
11822 if (IS_CHANGING(x, y) &&
11823 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11825 int page = element_info[element].event_page_nr[CE_DELAY];
11827 HandleElementChange(x, y, page);
11829 element = Feld[x][y];
11830 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11833 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11837 element = Feld[x][y];
11838 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11840 if (IS_ANIMATED(graphic) &&
11841 !IS_MOVING(x, y) &&
11843 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11845 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11846 TEST_DrawTwinkleOnField(x, y);
11848 else if (element == EL_ACID)
11851 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11853 else if ((element == EL_EXIT_OPEN ||
11854 element == EL_EM_EXIT_OPEN ||
11855 element == EL_SP_EXIT_OPEN ||
11856 element == EL_STEEL_EXIT_OPEN ||
11857 element == EL_EM_STEEL_EXIT_OPEN ||
11858 element == EL_SP_TERMINAL ||
11859 element == EL_SP_TERMINAL_ACTIVE ||
11860 element == EL_EXTRA_TIME ||
11861 element == EL_SHIELD_NORMAL ||
11862 element == EL_SHIELD_DEADLY) &&
11863 IS_ANIMATED(graphic))
11864 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11865 else if (IS_MOVING(x, y))
11866 ContinueMoving(x, y);
11867 else if (IS_ACTIVE_BOMB(element))
11868 CheckDynamite(x, y);
11869 else if (element == EL_AMOEBA_GROWING)
11870 AmoebeWaechst(x, y);
11871 else if (element == EL_AMOEBA_SHRINKING)
11872 AmoebaDisappearing(x, y);
11874 #if !USE_NEW_AMOEBA_CODE
11875 else if (IS_AMOEBALIVE(element))
11876 AmoebeAbleger(x, y);
11879 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11881 else if (element == EL_EXIT_CLOSED)
11883 else if (element == EL_EM_EXIT_CLOSED)
11885 else if (element == EL_STEEL_EXIT_CLOSED)
11886 CheckExitSteel(x, y);
11887 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11888 CheckExitSteelEM(x, y);
11889 else if (element == EL_SP_EXIT_CLOSED)
11891 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11892 element == EL_EXPANDABLE_STEELWALL_GROWING)
11893 MauerWaechst(x, y);
11894 else if (element == EL_EXPANDABLE_WALL ||
11895 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11896 element == EL_EXPANDABLE_WALL_VERTICAL ||
11897 element == EL_EXPANDABLE_WALL_ANY ||
11898 element == EL_BD_EXPANDABLE_WALL)
11899 MauerAbleger(x, y);
11900 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11901 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11902 element == EL_EXPANDABLE_STEELWALL_ANY)
11903 MauerAblegerStahl(x, y);
11904 else if (element == EL_FLAMES)
11905 CheckForDragon(x, y);
11906 else if (element == EL_EXPLOSION)
11907 ; /* drawing of correct explosion animation is handled separately */
11908 else if (element == EL_ELEMENT_SNAPPING ||
11909 element == EL_DIAGONAL_SHRINKING ||
11910 element == EL_DIAGONAL_GROWING)
11912 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11914 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11916 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11917 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11919 if (IS_BELT_ACTIVE(element))
11920 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11922 if (game.magic_wall_active)
11924 int jx = local_player->jx, jy = local_player->jy;
11926 /* play the element sound at the position nearest to the player */
11927 if ((element == EL_MAGIC_WALL_FULL ||
11928 element == EL_MAGIC_WALL_ACTIVE ||
11929 element == EL_MAGIC_WALL_EMPTYING ||
11930 element == EL_BD_MAGIC_WALL_FULL ||
11931 element == EL_BD_MAGIC_WALL_ACTIVE ||
11932 element == EL_BD_MAGIC_WALL_EMPTYING ||
11933 element == EL_DC_MAGIC_WALL_FULL ||
11934 element == EL_DC_MAGIC_WALL_ACTIVE ||
11935 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11936 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11944 #if USE_NEW_AMOEBA_CODE
11945 /* new experimental amoeba growth stuff */
11946 if (!(FrameCounter % 8))
11948 static unsigned int random = 1684108901;
11950 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11952 x = RND(lev_fieldx);
11953 y = RND(lev_fieldy);
11954 element = Feld[x][y];
11956 if (!IS_PLAYER(x,y) &&
11957 (element == EL_EMPTY ||
11958 CAN_GROW_INTO(element) ||
11959 element == EL_QUICKSAND_EMPTY ||
11960 element == EL_QUICKSAND_FAST_EMPTY ||
11961 element == EL_ACID_SPLASH_LEFT ||
11962 element == EL_ACID_SPLASH_RIGHT))
11964 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11965 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11966 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11967 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11968 Feld[x][y] = EL_AMOEBA_DROP;
11971 random = random * 129 + 1;
11976 game.explosions_delayed = FALSE;
11978 SCAN_PLAYFIELD(x, y)
11980 element = Feld[x][y];
11982 if (ExplodeField[x][y])
11983 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11984 else if (element == EL_EXPLOSION)
11985 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11987 ExplodeField[x][y] = EX_TYPE_NONE;
11990 game.explosions_delayed = TRUE;
11992 if (game.magic_wall_active)
11994 if (!(game.magic_wall_time_left % 4))
11996 int element = Feld[magic_wall_x][magic_wall_y];
11998 if (element == EL_BD_MAGIC_WALL_FULL ||
11999 element == EL_BD_MAGIC_WALL_ACTIVE ||
12000 element == EL_BD_MAGIC_WALL_EMPTYING)
12001 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12002 else if (element == EL_DC_MAGIC_WALL_FULL ||
12003 element == EL_DC_MAGIC_WALL_ACTIVE ||
12004 element == EL_DC_MAGIC_WALL_EMPTYING)
12005 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12007 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12010 if (game.magic_wall_time_left > 0)
12012 game.magic_wall_time_left--;
12014 if (!game.magic_wall_time_left)
12016 SCAN_PLAYFIELD(x, y)
12018 element = Feld[x][y];
12020 if (element == EL_MAGIC_WALL_ACTIVE ||
12021 element == EL_MAGIC_WALL_FULL)
12023 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12024 TEST_DrawLevelField(x, y);
12026 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12027 element == EL_BD_MAGIC_WALL_FULL)
12029 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12030 TEST_DrawLevelField(x, y);
12032 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12033 element == EL_DC_MAGIC_WALL_FULL)
12035 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12036 TEST_DrawLevelField(x, y);
12040 game.magic_wall_active = FALSE;
12045 if (game.light_time_left > 0)
12047 game.light_time_left--;
12049 if (game.light_time_left == 0)
12050 RedrawAllLightSwitchesAndInvisibleElements();
12053 if (game.timegate_time_left > 0)
12055 game.timegate_time_left--;
12057 if (game.timegate_time_left == 0)
12058 CloseAllOpenTimegates();
12061 if (game.lenses_time_left > 0)
12063 game.lenses_time_left--;
12065 if (game.lenses_time_left == 0)
12066 RedrawAllInvisibleElementsForLenses();
12069 if (game.magnify_time_left > 0)
12071 game.magnify_time_left--;
12073 if (game.magnify_time_left == 0)
12074 RedrawAllInvisibleElementsForMagnifier();
12077 for (i = 0; i < MAX_PLAYERS; i++)
12079 struct PlayerInfo *player = &stored_player[i];
12081 if (SHIELD_ON(player))
12083 if (player->shield_deadly_time_left)
12084 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12085 else if (player->shield_normal_time_left)
12086 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12090 #if USE_DELAYED_GFX_REDRAW
12091 SCAN_PLAYFIELD(x, y)
12093 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12095 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12096 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12098 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12099 DrawLevelField(x, y);
12101 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12102 DrawLevelFieldCrumbled(x, y);
12104 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12105 DrawLevelFieldCrumbledNeighbours(x, y);
12107 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12108 DrawTwinkleOnField(x, y);
12111 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12116 PlayAllPlayersSound();
12118 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12120 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12122 local_player->show_envelope = 0;
12125 /* use random number generator in every frame to make it less predictable */
12126 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12130 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12132 int min_x = x, min_y = y, max_x = x, max_y = y;
12135 for (i = 0; i < MAX_PLAYERS; i++)
12137 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12139 if (!stored_player[i].active || &stored_player[i] == player)
12142 min_x = MIN(min_x, jx);
12143 min_y = MIN(min_y, jy);
12144 max_x = MAX(max_x, jx);
12145 max_y = MAX(max_y, jy);
12148 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12151 static boolean AllPlayersInVisibleScreen()
12155 for (i = 0; i < MAX_PLAYERS; i++)
12157 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12159 if (!stored_player[i].active)
12162 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12169 void ScrollLevel(int dx, int dy)
12171 int scroll_offset = 2 * TILEX_VAR;
12174 BlitBitmap(drawto_field, drawto_field,
12175 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12176 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12177 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12178 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12179 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12180 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12184 x = (dx == 1 ? BX1 : BX2);
12185 for (y = BY1; y <= BY2; y++)
12186 DrawScreenField(x, y);
12191 y = (dy == 1 ? BY1 : BY2);
12192 for (x = BX1; x <= BX2; x++)
12193 DrawScreenField(x, y);
12196 redraw_mask |= REDRAW_FIELD;
12199 static boolean canFallDown(struct PlayerInfo *player)
12201 int jx = player->jx, jy = player->jy;
12203 return (IN_LEV_FIELD(jx, jy + 1) &&
12204 (IS_FREE(jx, jy + 1) ||
12205 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12206 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12207 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12210 static boolean canPassField(int x, int y, int move_dir)
12212 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12213 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12214 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12215 int nextx = x + dx;
12216 int nexty = y + dy;
12217 int element = Feld[x][y];
12219 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12220 !CAN_MOVE(element) &&
12221 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12222 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12223 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12226 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12228 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12229 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12230 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12234 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12235 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12236 (IS_DIGGABLE(Feld[newx][newy]) ||
12237 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12238 canPassField(newx, newy, move_dir)));
12241 static void CheckGravityMovement(struct PlayerInfo *player)
12243 if (player->gravity && !player->programmed_action)
12245 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12246 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12247 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12248 int jx = player->jx, jy = player->jy;
12249 boolean player_is_moving_to_valid_field =
12250 (!player_is_snapping &&
12251 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12252 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12253 boolean player_can_fall_down = canFallDown(player);
12255 if (player_can_fall_down &&
12256 !player_is_moving_to_valid_field)
12257 player->programmed_action = MV_DOWN;
12261 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12263 return CheckGravityMovement(player);
12265 if (player->gravity && !player->programmed_action)
12267 int jx = player->jx, jy = player->jy;
12268 boolean field_under_player_is_free =
12269 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12270 boolean player_is_standing_on_valid_field =
12271 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12272 (IS_WALKABLE(Feld[jx][jy]) &&
12273 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12275 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12276 player->programmed_action = MV_DOWN;
12281 MovePlayerOneStep()
12282 -----------------------------------------------------------------------------
12283 dx, dy: direction (non-diagonal) to try to move the player to
12284 real_dx, real_dy: direction as read from input device (can be diagonal)
12287 boolean MovePlayerOneStep(struct PlayerInfo *player,
12288 int dx, int dy, int real_dx, int real_dy)
12290 int jx = player->jx, jy = player->jy;
12291 int new_jx = jx + dx, new_jy = jy + dy;
12293 boolean player_can_move = !player->cannot_move;
12295 if (!player->active || (!dx && !dy))
12296 return MP_NO_ACTION;
12298 player->MovDir = (dx < 0 ? MV_LEFT :
12299 dx > 0 ? MV_RIGHT :
12301 dy > 0 ? MV_DOWN : MV_NONE);
12303 if (!IN_LEV_FIELD(new_jx, new_jy))
12304 return MP_NO_ACTION;
12306 if (!player_can_move)
12308 if (player->MovPos == 0)
12310 player->is_moving = FALSE;
12311 player->is_digging = FALSE;
12312 player->is_collecting = FALSE;
12313 player->is_snapping = FALSE;
12314 player->is_pushing = FALSE;
12318 if (!network.enabled && game.centered_player_nr == -1 &&
12319 !AllPlayersInSight(player, new_jx, new_jy))
12320 return MP_NO_ACTION;
12322 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12323 if (can_move != MP_MOVING)
12326 /* check if DigField() has caused relocation of the player */
12327 if (player->jx != jx || player->jy != jy)
12328 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12330 StorePlayer[jx][jy] = 0;
12331 player->last_jx = jx;
12332 player->last_jy = jy;
12333 player->jx = new_jx;
12334 player->jy = new_jy;
12335 StorePlayer[new_jx][new_jy] = player->element_nr;
12337 if (player->move_delay_value_next != -1)
12339 player->move_delay_value = player->move_delay_value_next;
12340 player->move_delay_value_next = -1;
12344 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12346 player->step_counter++;
12348 PlayerVisit[jx][jy] = FrameCounter;
12350 player->is_moving = TRUE;
12353 /* should better be called in MovePlayer(), but this breaks some tapes */
12354 ScrollPlayer(player, SCROLL_INIT);
12360 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12362 int jx = player->jx, jy = player->jy;
12363 int old_jx = jx, old_jy = jy;
12364 int moved = MP_NO_ACTION;
12366 if (!player->active)
12371 if (player->MovPos == 0)
12373 player->is_moving = FALSE;
12374 player->is_digging = FALSE;
12375 player->is_collecting = FALSE;
12376 player->is_snapping = FALSE;
12377 player->is_pushing = FALSE;
12383 if (player->move_delay > 0)
12386 player->move_delay = -1; /* set to "uninitialized" value */
12388 /* store if player is automatically moved to next field */
12389 player->is_auto_moving = (player->programmed_action != MV_NONE);
12391 /* remove the last programmed player action */
12392 player->programmed_action = 0;
12394 if (player->MovPos)
12396 /* should only happen if pre-1.2 tape recordings are played */
12397 /* this is only for backward compatibility */
12399 int original_move_delay_value = player->move_delay_value;
12402 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12406 /* scroll remaining steps with finest movement resolution */
12407 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12409 while (player->MovPos)
12411 ScrollPlayer(player, SCROLL_GO_ON);
12412 ScrollScreen(NULL, SCROLL_GO_ON);
12414 AdvanceFrameAndPlayerCounters(player->index_nr);
12417 BackToFront_WithFrameDelay(0);
12420 player->move_delay_value = original_move_delay_value;
12423 player->is_active = FALSE;
12425 if (player->last_move_dir & MV_HORIZONTAL)
12427 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12428 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12432 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12433 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12436 if (!moved && !player->is_active)
12438 player->is_moving = FALSE;
12439 player->is_digging = FALSE;
12440 player->is_collecting = FALSE;
12441 player->is_snapping = FALSE;
12442 player->is_pushing = FALSE;
12448 if (moved & MP_MOVING && !ScreenMovPos &&
12449 (player->index_nr == game.centered_player_nr ||
12450 game.centered_player_nr == -1))
12452 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12453 int offset = game.scroll_delay_value;
12455 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12457 /* actual player has left the screen -- scroll in that direction */
12458 if (jx != old_jx) /* player has moved horizontally */
12459 scroll_x += (jx - old_jx);
12460 else /* player has moved vertically */
12461 scroll_y += (jy - old_jy);
12465 if (jx != old_jx) /* player has moved horizontally */
12467 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12468 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12469 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12471 /* don't scroll over playfield boundaries */
12472 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12473 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12475 /* don't scroll more than one field at a time */
12476 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12478 /* don't scroll against the player's moving direction */
12479 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12480 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12481 scroll_x = old_scroll_x;
12483 else /* player has moved vertically */
12485 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12486 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12487 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12489 /* don't scroll over playfield boundaries */
12490 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12491 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12493 /* don't scroll more than one field at a time */
12494 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12496 /* don't scroll against the player's moving direction */
12497 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12498 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12499 scroll_y = old_scroll_y;
12503 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12505 if (!network.enabled && game.centered_player_nr == -1 &&
12506 !AllPlayersInVisibleScreen())
12508 scroll_x = old_scroll_x;
12509 scroll_y = old_scroll_y;
12513 ScrollScreen(player, SCROLL_INIT);
12514 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12519 player->StepFrame = 0;
12521 if (moved & MP_MOVING)
12523 if (old_jx != jx && old_jy == jy)
12524 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12525 else if (old_jx == jx && old_jy != jy)
12526 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12528 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12530 player->last_move_dir = player->MovDir;
12531 player->is_moving = TRUE;
12532 player->is_snapping = FALSE;
12533 player->is_switching = FALSE;
12534 player->is_dropping = FALSE;
12535 player->is_dropping_pressed = FALSE;
12536 player->drop_pressed_delay = 0;
12539 /* should better be called here than above, but this breaks some tapes */
12540 ScrollPlayer(player, SCROLL_INIT);
12545 CheckGravityMovementWhenNotMoving(player);
12547 player->is_moving = FALSE;
12549 /* at this point, the player is allowed to move, but cannot move right now
12550 (e.g. because of something blocking the way) -- ensure that the player
12551 is also allowed to move in the next frame (in old versions before 3.1.1,
12552 the player was forced to wait again for eight frames before next try) */
12554 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12555 player->move_delay = 0; /* allow direct movement in the next frame */
12558 if (player->move_delay == -1) /* not yet initialized by DigField() */
12559 player->move_delay = player->move_delay_value;
12561 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12563 TestIfPlayerTouchesBadThing(jx, jy);
12564 TestIfPlayerTouchesCustomElement(jx, jy);
12567 if (!player->active)
12568 RemovePlayer(player);
12573 void ScrollPlayer(struct PlayerInfo *player, int mode)
12575 int jx = player->jx, jy = player->jy;
12576 int last_jx = player->last_jx, last_jy = player->last_jy;
12577 int move_stepsize = TILEX / player->move_delay_value;
12579 if (!player->active)
12582 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12585 if (mode == SCROLL_INIT)
12587 player->actual_frame_counter = FrameCounter;
12588 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12590 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12591 Feld[last_jx][last_jy] == EL_EMPTY)
12593 int last_field_block_delay = 0; /* start with no blocking at all */
12594 int block_delay_adjustment = player->block_delay_adjustment;
12596 /* if player blocks last field, add delay for exactly one move */
12597 if (player->block_last_field)
12599 last_field_block_delay += player->move_delay_value;
12601 /* when blocking enabled, prevent moving up despite gravity */
12602 if (player->gravity && player->MovDir == MV_UP)
12603 block_delay_adjustment = -1;
12606 /* add block delay adjustment (also possible when not blocking) */
12607 last_field_block_delay += block_delay_adjustment;
12609 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12610 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12613 if (player->MovPos != 0) /* player has not yet reached destination */
12616 else if (!FrameReached(&player->actual_frame_counter, 1))
12619 if (player->MovPos != 0)
12621 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12622 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12624 /* before DrawPlayer() to draw correct player graphic for this case */
12625 if (player->MovPos == 0)
12626 CheckGravityMovement(player);
12629 if (player->MovPos == 0) /* player reached destination field */
12631 if (player->move_delay_reset_counter > 0)
12633 player->move_delay_reset_counter--;
12635 if (player->move_delay_reset_counter == 0)
12637 /* continue with normal speed after quickly moving through gate */
12638 HALVE_PLAYER_SPEED(player);
12640 /* be able to make the next move without delay */
12641 player->move_delay = 0;
12645 player->last_jx = jx;
12646 player->last_jy = jy;
12648 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12649 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12650 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12651 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12652 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12653 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12654 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12655 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12657 ExitPlayer(player);
12659 if ((local_player->friends_still_needed == 0 ||
12660 IS_SP_ELEMENT(Feld[jx][jy])) &&
12662 PlayerWins(local_player);
12665 /* this breaks one level: "machine", level 000 */
12667 int move_direction = player->MovDir;
12668 int enter_side = MV_DIR_OPPOSITE(move_direction);
12669 int leave_side = move_direction;
12670 int old_jx = last_jx;
12671 int old_jy = last_jy;
12672 int old_element = Feld[old_jx][old_jy];
12673 int new_element = Feld[jx][jy];
12675 if (IS_CUSTOM_ELEMENT(old_element))
12676 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12678 player->index_bit, leave_side);
12680 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12681 CE_PLAYER_LEAVES_X,
12682 player->index_bit, leave_side);
12684 if (IS_CUSTOM_ELEMENT(new_element))
12685 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12686 player->index_bit, enter_side);
12688 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12689 CE_PLAYER_ENTERS_X,
12690 player->index_bit, enter_side);
12692 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12693 CE_MOVE_OF_X, move_direction);
12696 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12698 TestIfPlayerTouchesBadThing(jx, jy);
12699 TestIfPlayerTouchesCustomElement(jx, jy);
12701 /* needed because pushed element has not yet reached its destination,
12702 so it would trigger a change event at its previous field location */
12703 if (!player->is_pushing)
12704 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12706 if (!player->active)
12707 RemovePlayer(player);
12710 if (!local_player->LevelSolved && level.use_step_counter)
12720 if (TimeLeft <= 10 && setup.time_limit)
12721 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12723 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12725 DisplayGameControlValues();
12727 if (!TimeLeft && setup.time_limit)
12728 for (i = 0; i < MAX_PLAYERS; i++)
12729 KillPlayer(&stored_player[i]);
12731 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12733 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12735 DisplayGameControlValues();
12739 if (tape.single_step && tape.recording && !tape.pausing &&
12740 !player->programmed_action)
12741 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12743 if (!player->programmed_action)
12744 CheckSaveEngineSnapshot(player);
12748 void ScrollScreen(struct PlayerInfo *player, int mode)
12750 static unsigned int screen_frame_counter = 0;
12752 if (mode == SCROLL_INIT)
12754 /* set scrolling step size according to actual player's moving speed */
12755 ScrollStepSize = TILEX / player->move_delay_value;
12757 screen_frame_counter = FrameCounter;
12758 ScreenMovDir = player->MovDir;
12759 ScreenMovPos = player->MovPos;
12760 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12763 else if (!FrameReached(&screen_frame_counter, 1))
12768 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12769 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12770 redraw_mask |= REDRAW_FIELD;
12773 ScreenMovDir = MV_NONE;
12776 void TestIfPlayerTouchesCustomElement(int x, int y)
12778 static int xy[4][2] =
12785 static int trigger_sides[4][2] =
12787 /* center side border side */
12788 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12789 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12790 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12791 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12793 static int touch_dir[4] =
12795 MV_LEFT | MV_RIGHT,
12800 int center_element = Feld[x][y]; /* should always be non-moving! */
12803 for (i = 0; i < NUM_DIRECTIONS; i++)
12805 int xx = x + xy[i][0];
12806 int yy = y + xy[i][1];
12807 int center_side = trigger_sides[i][0];
12808 int border_side = trigger_sides[i][1];
12809 int border_element;
12811 if (!IN_LEV_FIELD(xx, yy))
12814 if (IS_PLAYER(x, y)) /* player found at center element */
12816 struct PlayerInfo *player = PLAYERINFO(x, y);
12818 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12819 border_element = Feld[xx][yy]; /* may be moving! */
12820 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12821 border_element = Feld[xx][yy];
12822 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12823 border_element = MovingOrBlocked2Element(xx, yy);
12825 continue; /* center and border element do not touch */
12827 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12828 player->index_bit, border_side);
12829 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12830 CE_PLAYER_TOUCHES_X,
12831 player->index_bit, border_side);
12834 /* use player element that is initially defined in the level playfield,
12835 not the player element that corresponds to the runtime player number
12836 (example: a level that contains EL_PLAYER_3 as the only player would
12837 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12838 int player_element = PLAYERINFO(x, y)->initial_element;
12840 CheckElementChangeBySide(xx, yy, border_element, player_element,
12841 CE_TOUCHING_X, border_side);
12844 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12846 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12848 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12850 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12851 continue; /* center and border element do not touch */
12854 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12855 player->index_bit, center_side);
12856 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12857 CE_PLAYER_TOUCHES_X,
12858 player->index_bit, center_side);
12861 /* use player element that is initially defined in the level playfield,
12862 not the player element that corresponds to the runtime player number
12863 (example: a level that contains EL_PLAYER_3 as the only player would
12864 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12865 int player_element = PLAYERINFO(xx, yy)->initial_element;
12867 CheckElementChangeBySide(x, y, center_element, player_element,
12868 CE_TOUCHING_X, center_side);
12876 void TestIfElementTouchesCustomElement(int x, int y)
12878 static int xy[4][2] =
12885 static int trigger_sides[4][2] =
12887 /* center side border side */
12888 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12889 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12890 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12891 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12893 static int touch_dir[4] =
12895 MV_LEFT | MV_RIGHT,
12900 boolean change_center_element = FALSE;
12901 int center_element = Feld[x][y]; /* should always be non-moving! */
12902 int border_element_old[NUM_DIRECTIONS];
12905 for (i = 0; i < NUM_DIRECTIONS; i++)
12907 int xx = x + xy[i][0];
12908 int yy = y + xy[i][1];
12909 int border_element;
12911 border_element_old[i] = -1;
12913 if (!IN_LEV_FIELD(xx, yy))
12916 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12917 border_element = Feld[xx][yy]; /* may be moving! */
12918 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12919 border_element = Feld[xx][yy];
12920 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12921 border_element = MovingOrBlocked2Element(xx, yy);
12923 continue; /* center and border element do not touch */
12925 border_element_old[i] = border_element;
12928 for (i = 0; i < NUM_DIRECTIONS; i++)
12930 int xx = x + xy[i][0];
12931 int yy = y + xy[i][1];
12932 int center_side = trigger_sides[i][0];
12933 int border_element = border_element_old[i];
12935 if (border_element == -1)
12938 /* check for change of border element */
12939 CheckElementChangeBySide(xx, yy, border_element, center_element,
12940 CE_TOUCHING_X, center_side);
12942 /* (center element cannot be player, so we dont have to check this here) */
12945 for (i = 0; i < NUM_DIRECTIONS; i++)
12947 int xx = x + xy[i][0];
12948 int yy = y + xy[i][1];
12949 int border_side = trigger_sides[i][1];
12950 int border_element = border_element_old[i];
12952 if (border_element == -1)
12955 /* check for change of center element (but change it only once) */
12956 if (!change_center_element)
12957 change_center_element =
12958 CheckElementChangeBySide(x, y, center_element, border_element,
12959 CE_TOUCHING_X, border_side);
12961 if (IS_PLAYER(xx, yy))
12963 /* use player element that is initially defined in the level playfield,
12964 not the player element that corresponds to the runtime player number
12965 (example: a level that contains EL_PLAYER_3 as the only player would
12966 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12967 int player_element = PLAYERINFO(xx, yy)->initial_element;
12969 CheckElementChangeBySide(x, y, center_element, player_element,
12970 CE_TOUCHING_X, border_side);
12975 void TestIfElementHitsCustomElement(int x, int y, int direction)
12977 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12978 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12979 int hitx = x + dx, hity = y + dy;
12980 int hitting_element = Feld[x][y];
12981 int touched_element;
12983 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12986 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12987 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12989 if (IN_LEV_FIELD(hitx, hity))
12991 int opposite_direction = MV_DIR_OPPOSITE(direction);
12992 int hitting_side = direction;
12993 int touched_side = opposite_direction;
12994 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12995 MovDir[hitx][hity] != direction ||
12996 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13002 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13003 CE_HITTING_X, touched_side);
13005 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13006 CE_HIT_BY_X, hitting_side);
13008 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13009 CE_HIT_BY_SOMETHING, opposite_direction);
13011 if (IS_PLAYER(hitx, hity))
13013 /* use player element that is initially defined in the level playfield,
13014 not the player element that corresponds to the runtime player number
13015 (example: a level that contains EL_PLAYER_3 as the only player would
13016 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13017 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13019 CheckElementChangeBySide(x, y, hitting_element, player_element,
13020 CE_HITTING_X, touched_side);
13025 /* "hitting something" is also true when hitting the playfield border */
13026 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13027 CE_HITTING_SOMETHING, direction);
13030 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13032 int i, kill_x = -1, kill_y = -1;
13034 int bad_element = -1;
13035 static int test_xy[4][2] =
13042 static int test_dir[4] =
13050 for (i = 0; i < NUM_DIRECTIONS; i++)
13052 int test_x, test_y, test_move_dir, test_element;
13054 test_x = good_x + test_xy[i][0];
13055 test_y = good_y + test_xy[i][1];
13057 if (!IN_LEV_FIELD(test_x, test_y))
13061 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13063 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13065 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13066 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13068 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13069 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13073 bad_element = test_element;
13079 if (kill_x != -1 || kill_y != -1)
13081 if (IS_PLAYER(good_x, good_y))
13083 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13085 if (player->shield_deadly_time_left > 0 &&
13086 !IS_INDESTRUCTIBLE(bad_element))
13087 Bang(kill_x, kill_y);
13088 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13089 KillPlayer(player);
13092 Bang(good_x, good_y);
13096 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13098 int i, kill_x = -1, kill_y = -1;
13099 int bad_element = Feld[bad_x][bad_y];
13100 static int test_xy[4][2] =
13107 static int touch_dir[4] =
13109 MV_LEFT | MV_RIGHT,
13114 static int test_dir[4] =
13122 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13125 for (i = 0; i < NUM_DIRECTIONS; i++)
13127 int test_x, test_y, test_move_dir, test_element;
13129 test_x = bad_x + test_xy[i][0];
13130 test_y = bad_y + test_xy[i][1];
13132 if (!IN_LEV_FIELD(test_x, test_y))
13136 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13138 test_element = Feld[test_x][test_y];
13140 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13141 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13143 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13144 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13146 /* good thing is player or penguin that does not move away */
13147 if (IS_PLAYER(test_x, test_y))
13149 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13151 if (bad_element == EL_ROBOT && player->is_moving)
13152 continue; /* robot does not kill player if he is moving */
13154 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13156 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13157 continue; /* center and border element do not touch */
13165 else if (test_element == EL_PENGUIN)
13175 if (kill_x != -1 || kill_y != -1)
13177 if (IS_PLAYER(kill_x, kill_y))
13179 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13181 if (player->shield_deadly_time_left > 0 &&
13182 !IS_INDESTRUCTIBLE(bad_element))
13183 Bang(bad_x, bad_y);
13184 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13185 KillPlayer(player);
13188 Bang(kill_x, kill_y);
13192 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13194 int bad_element = Feld[bad_x][bad_y];
13195 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13196 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13197 int test_x = bad_x + dx, test_y = bad_y + dy;
13198 int test_move_dir, test_element;
13199 int kill_x = -1, kill_y = -1;
13201 if (!IN_LEV_FIELD(test_x, test_y))
13205 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13207 test_element = Feld[test_x][test_y];
13209 if (test_move_dir != bad_move_dir)
13211 /* good thing can be player or penguin that does not move away */
13212 if (IS_PLAYER(test_x, test_y))
13214 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13216 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13217 player as being hit when he is moving towards the bad thing, because
13218 the "get hit by" condition would be lost after the player stops) */
13219 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13220 return; /* player moves away from bad thing */
13225 else if (test_element == EL_PENGUIN)
13232 if (kill_x != -1 || kill_y != -1)
13234 if (IS_PLAYER(kill_x, kill_y))
13236 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13238 if (player->shield_deadly_time_left > 0 &&
13239 !IS_INDESTRUCTIBLE(bad_element))
13240 Bang(bad_x, bad_y);
13241 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13242 KillPlayer(player);
13245 Bang(kill_x, kill_y);
13249 void TestIfPlayerTouchesBadThing(int x, int y)
13251 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13254 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13256 TestIfGoodThingHitsBadThing(x, y, move_dir);
13259 void TestIfBadThingTouchesPlayer(int x, int y)
13261 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13264 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13266 TestIfBadThingHitsGoodThing(x, y, move_dir);
13269 void TestIfFriendTouchesBadThing(int x, int y)
13271 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13274 void TestIfBadThingTouchesFriend(int x, int y)
13276 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13279 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13281 int i, kill_x = bad_x, kill_y = bad_y;
13282 static int xy[4][2] =
13290 for (i = 0; i < NUM_DIRECTIONS; i++)
13294 x = bad_x + xy[i][0];
13295 y = bad_y + xy[i][1];
13296 if (!IN_LEV_FIELD(x, y))
13299 element = Feld[x][y];
13300 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13301 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13309 if (kill_x != bad_x || kill_y != bad_y)
13310 Bang(bad_x, bad_y);
13313 void KillPlayer(struct PlayerInfo *player)
13315 int jx = player->jx, jy = player->jy;
13317 if (!player->active)
13321 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13322 player->killed, player->active, player->reanimated);
13325 /* the following code was introduced to prevent an infinite loop when calling
13327 -> CheckTriggeredElementChangeExt()
13328 -> ExecuteCustomElementAction()
13330 -> (infinitely repeating the above sequence of function calls)
13331 which occurs when killing the player while having a CE with the setting
13332 "kill player X when explosion of <player X>"; the solution using a new
13333 field "player->killed" was chosen for backwards compatibility, although
13334 clever use of the fields "player->active" etc. would probably also work */
13336 if (player->killed)
13340 player->killed = TRUE;
13342 /* remove accessible field at the player's position */
13343 Feld[jx][jy] = EL_EMPTY;
13345 /* deactivate shield (else Bang()/Explode() would not work right) */
13346 player->shield_normal_time_left = 0;
13347 player->shield_deadly_time_left = 0;
13350 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13351 player->killed, player->active, player->reanimated);
13357 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13358 player->killed, player->active, player->reanimated);
13361 if (player->reanimated) /* killed player may have been reanimated */
13362 player->killed = player->reanimated = FALSE;
13364 BuryPlayer(player);
13367 static void KillPlayerUnlessEnemyProtected(int x, int y)
13369 if (!PLAYER_ENEMY_PROTECTED(x, y))
13370 KillPlayer(PLAYERINFO(x, y));
13373 static void KillPlayerUnlessExplosionProtected(int x, int y)
13375 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13376 KillPlayer(PLAYERINFO(x, y));
13379 void BuryPlayer(struct PlayerInfo *player)
13381 int jx = player->jx, jy = player->jy;
13383 if (!player->active)
13386 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13387 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13389 player->GameOver = TRUE;
13390 RemovePlayer(player);
13393 void RemovePlayer(struct PlayerInfo *player)
13395 int jx = player->jx, jy = player->jy;
13396 int i, found = FALSE;
13398 player->present = FALSE;
13399 player->active = FALSE;
13401 if (!ExplodeField[jx][jy])
13402 StorePlayer[jx][jy] = 0;
13404 if (player->is_moving)
13405 TEST_DrawLevelField(player->last_jx, player->last_jy);
13407 for (i = 0; i < MAX_PLAYERS; i++)
13408 if (stored_player[i].active)
13412 AllPlayersGone = TRUE;
13418 void ExitPlayer(struct PlayerInfo *player)
13420 DrawPlayer(player); /* needed here only to cleanup last field */
13421 RemovePlayer(player);
13423 local_player->players_still_needed--;
13426 static void setFieldForSnapping(int x, int y, int element, int direction)
13428 struct ElementInfo *ei = &element_info[element];
13429 int direction_bit = MV_DIR_TO_BIT(direction);
13430 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13431 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13432 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13434 Feld[x][y] = EL_ELEMENT_SNAPPING;
13435 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13437 ResetGfxAnimation(x, y);
13439 GfxElement[x][y] = element;
13440 GfxAction[x][y] = action;
13441 GfxDir[x][y] = direction;
13442 GfxFrame[x][y] = -1;
13446 =============================================================================
13447 checkDiagonalPushing()
13448 -----------------------------------------------------------------------------
13449 check if diagonal input device direction results in pushing of object
13450 (by checking if the alternative direction is walkable, diggable, ...)
13451 =============================================================================
13454 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13455 int x, int y, int real_dx, int real_dy)
13457 int jx, jy, dx, dy, xx, yy;
13459 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13462 /* diagonal direction: check alternative direction */
13467 xx = jx + (dx == 0 ? real_dx : 0);
13468 yy = jy + (dy == 0 ? real_dy : 0);
13470 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13474 =============================================================================
13476 -----------------------------------------------------------------------------
13477 x, y: field next to player (non-diagonal) to try to dig to
13478 real_dx, real_dy: direction as read from input device (can be diagonal)
13479 =============================================================================
13482 static int DigField(struct PlayerInfo *player,
13483 int oldx, int oldy, int x, int y,
13484 int real_dx, int real_dy, int mode)
13486 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13487 boolean player_was_pushing = player->is_pushing;
13488 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13489 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13490 int jx = oldx, jy = oldy;
13491 int dx = x - jx, dy = y - jy;
13492 int nextx = x + dx, nexty = y + dy;
13493 int move_direction = (dx == -1 ? MV_LEFT :
13494 dx == +1 ? MV_RIGHT :
13496 dy == +1 ? MV_DOWN : MV_NONE);
13497 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13498 int dig_side = MV_DIR_OPPOSITE(move_direction);
13499 int old_element = Feld[jx][jy];
13500 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13503 if (is_player) /* function can also be called by EL_PENGUIN */
13505 if (player->MovPos == 0)
13507 player->is_digging = FALSE;
13508 player->is_collecting = FALSE;
13511 if (player->MovPos == 0) /* last pushing move finished */
13512 player->is_pushing = FALSE;
13514 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13516 player->is_switching = FALSE;
13517 player->push_delay = -1;
13519 return MP_NO_ACTION;
13523 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13524 old_element = Back[jx][jy];
13526 /* in case of element dropped at player position, check background */
13527 else if (Back[jx][jy] != EL_EMPTY &&
13528 game.engine_version >= VERSION_IDENT(2,2,0,0))
13529 old_element = Back[jx][jy];
13531 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13532 return MP_NO_ACTION; /* field has no opening in this direction */
13534 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13535 return MP_NO_ACTION; /* field has no opening in this direction */
13537 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13541 Feld[jx][jy] = player->artwork_element;
13542 InitMovingField(jx, jy, MV_DOWN);
13543 Store[jx][jy] = EL_ACID;
13544 ContinueMoving(jx, jy);
13545 BuryPlayer(player);
13547 return MP_DONT_RUN_INTO;
13550 if (player_can_move && DONT_RUN_INTO(element))
13552 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13554 return MP_DONT_RUN_INTO;
13557 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13558 return MP_NO_ACTION;
13560 collect_count = element_info[element].collect_count_initial;
13562 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13563 return MP_NO_ACTION;
13565 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13566 player_can_move = player_can_move_or_snap;
13568 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13569 game.engine_version >= VERSION_IDENT(2,2,0,0))
13571 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13572 player->index_bit, dig_side);
13573 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13574 player->index_bit, dig_side);
13576 if (element == EL_DC_LANDMINE)
13579 if (Feld[x][y] != element) /* field changed by snapping */
13582 return MP_NO_ACTION;
13585 if (player->gravity && is_player && !player->is_auto_moving &&
13586 canFallDown(player) && move_direction != MV_DOWN &&
13587 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13588 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13590 if (player_can_move &&
13591 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13593 int sound_element = SND_ELEMENT(element);
13594 int sound_action = ACTION_WALKING;
13596 if (IS_RND_GATE(element))
13598 if (!player->key[RND_GATE_NR(element)])
13599 return MP_NO_ACTION;
13601 else if (IS_RND_GATE_GRAY(element))
13603 if (!player->key[RND_GATE_GRAY_NR(element)])
13604 return MP_NO_ACTION;
13606 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13608 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13609 return MP_NO_ACTION;
13611 else if (element == EL_EXIT_OPEN ||
13612 element == EL_EM_EXIT_OPEN ||
13613 element == EL_EM_EXIT_OPENING ||
13614 element == EL_STEEL_EXIT_OPEN ||
13615 element == EL_EM_STEEL_EXIT_OPEN ||
13616 element == EL_EM_STEEL_EXIT_OPENING ||
13617 element == EL_SP_EXIT_OPEN ||
13618 element == EL_SP_EXIT_OPENING)
13620 sound_action = ACTION_PASSING; /* player is passing exit */
13622 else if (element == EL_EMPTY)
13624 sound_action = ACTION_MOVING; /* nothing to walk on */
13627 /* play sound from background or player, whatever is available */
13628 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13629 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13631 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13633 else if (player_can_move &&
13634 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13636 if (!ACCESS_FROM(element, opposite_direction))
13637 return MP_NO_ACTION; /* field not accessible from this direction */
13639 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13640 return MP_NO_ACTION;
13642 if (IS_EM_GATE(element))
13644 if (!player->key[EM_GATE_NR(element)])
13645 return MP_NO_ACTION;
13647 else if (IS_EM_GATE_GRAY(element))
13649 if (!player->key[EM_GATE_GRAY_NR(element)])
13650 return MP_NO_ACTION;
13652 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13654 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13655 return MP_NO_ACTION;
13657 else if (IS_EMC_GATE(element))
13659 if (!player->key[EMC_GATE_NR(element)])
13660 return MP_NO_ACTION;
13662 else if (IS_EMC_GATE_GRAY(element))
13664 if (!player->key[EMC_GATE_GRAY_NR(element)])
13665 return MP_NO_ACTION;
13667 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13669 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13670 return MP_NO_ACTION;
13672 else if (element == EL_DC_GATE_WHITE ||
13673 element == EL_DC_GATE_WHITE_GRAY ||
13674 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13676 if (player->num_white_keys == 0)
13677 return MP_NO_ACTION;
13679 player->num_white_keys--;
13681 else if (IS_SP_PORT(element))
13683 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13684 element == EL_SP_GRAVITY_PORT_RIGHT ||
13685 element == EL_SP_GRAVITY_PORT_UP ||
13686 element == EL_SP_GRAVITY_PORT_DOWN)
13687 player->gravity = !player->gravity;
13688 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13689 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13690 element == EL_SP_GRAVITY_ON_PORT_UP ||
13691 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13692 player->gravity = TRUE;
13693 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13694 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13695 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13696 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13697 player->gravity = FALSE;
13700 /* automatically move to the next field with double speed */
13701 player->programmed_action = move_direction;
13703 if (player->move_delay_reset_counter == 0)
13705 player->move_delay_reset_counter = 2; /* two double speed steps */
13707 DOUBLE_PLAYER_SPEED(player);
13710 PlayLevelSoundAction(x, y, ACTION_PASSING);
13712 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13716 if (mode != DF_SNAP)
13718 GfxElement[x][y] = GFX_ELEMENT(element);
13719 player->is_digging = TRUE;
13722 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13724 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13725 player->index_bit, dig_side);
13727 if (mode == DF_SNAP)
13729 if (level.block_snap_field)
13730 setFieldForSnapping(x, y, element, move_direction);
13732 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13734 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13735 player->index_bit, dig_side);
13738 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13742 if (is_player && mode != DF_SNAP)
13744 GfxElement[x][y] = element;
13745 player->is_collecting = TRUE;
13748 if (element == EL_SPEED_PILL)
13750 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13752 else if (element == EL_EXTRA_TIME && level.time > 0)
13754 TimeLeft += level.extra_time;
13756 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13758 DisplayGameControlValues();
13760 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13762 player->shield_normal_time_left += level.shield_normal_time;
13763 if (element == EL_SHIELD_DEADLY)
13764 player->shield_deadly_time_left += level.shield_deadly_time;
13766 else if (element == EL_DYNAMITE ||
13767 element == EL_EM_DYNAMITE ||
13768 element == EL_SP_DISK_RED)
13770 if (player->inventory_size < MAX_INVENTORY_SIZE)
13771 player->inventory_element[player->inventory_size++] = element;
13773 DrawGameDoorValues();
13775 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13777 player->dynabomb_count++;
13778 player->dynabombs_left++;
13780 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13782 player->dynabomb_size++;
13784 else if (element == EL_DYNABOMB_INCREASE_POWER)
13786 player->dynabomb_xl = TRUE;
13788 else if (IS_KEY(element))
13790 player->key[KEY_NR(element)] = TRUE;
13792 DrawGameDoorValues();
13794 else if (element == EL_DC_KEY_WHITE)
13796 player->num_white_keys++;
13798 /* display white keys? */
13799 /* DrawGameDoorValues(); */
13801 else if (IS_ENVELOPE(element))
13803 player->show_envelope = element;
13805 else if (element == EL_EMC_LENSES)
13807 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13809 RedrawAllInvisibleElementsForLenses();
13811 else if (element == EL_EMC_MAGNIFIER)
13813 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13815 RedrawAllInvisibleElementsForMagnifier();
13817 else if (IS_DROPPABLE(element) ||
13818 IS_THROWABLE(element)) /* can be collected and dropped */
13822 if (collect_count == 0)
13823 player->inventory_infinite_element = element;
13825 for (i = 0; i < collect_count; i++)
13826 if (player->inventory_size < MAX_INVENTORY_SIZE)
13827 player->inventory_element[player->inventory_size++] = element;
13829 DrawGameDoorValues();
13831 else if (collect_count > 0)
13833 local_player->gems_still_needed -= collect_count;
13834 if (local_player->gems_still_needed < 0)
13835 local_player->gems_still_needed = 0;
13837 game.snapshot.collected_item = TRUE;
13839 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13841 DisplayGameControlValues();
13844 RaiseScoreElement(element);
13845 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13848 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13849 player->index_bit, dig_side);
13851 if (mode == DF_SNAP)
13853 if (level.block_snap_field)
13854 setFieldForSnapping(x, y, element, move_direction);
13856 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13858 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13859 player->index_bit, dig_side);
13862 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13864 if (mode == DF_SNAP && element != EL_BD_ROCK)
13865 return MP_NO_ACTION;
13867 if (CAN_FALL(element) && dy)
13868 return MP_NO_ACTION;
13870 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13871 !(element == EL_SPRING && level.use_spring_bug))
13872 return MP_NO_ACTION;
13874 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13875 ((move_direction & MV_VERTICAL &&
13876 ((element_info[element].move_pattern & MV_LEFT &&
13877 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13878 (element_info[element].move_pattern & MV_RIGHT &&
13879 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13880 (move_direction & MV_HORIZONTAL &&
13881 ((element_info[element].move_pattern & MV_UP &&
13882 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13883 (element_info[element].move_pattern & MV_DOWN &&
13884 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13885 return MP_NO_ACTION;
13887 /* do not push elements already moving away faster than player */
13888 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13889 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13890 return MP_NO_ACTION;
13892 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13894 if (player->push_delay_value == -1 || !player_was_pushing)
13895 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13897 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13899 if (player->push_delay_value == -1)
13900 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13902 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13904 if (!player->is_pushing)
13905 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13908 player->is_pushing = TRUE;
13909 player->is_active = TRUE;
13911 if (!(IN_LEV_FIELD(nextx, nexty) &&
13912 (IS_FREE(nextx, nexty) ||
13913 (IS_SB_ELEMENT(element) &&
13914 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13915 (IS_CUSTOM_ELEMENT(element) &&
13916 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13917 return MP_NO_ACTION;
13919 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13920 return MP_NO_ACTION;
13922 if (player->push_delay == -1) /* new pushing; restart delay */
13923 player->push_delay = 0;
13925 if (player->push_delay < player->push_delay_value &&
13926 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13927 element != EL_SPRING && element != EL_BALLOON)
13929 /* make sure that there is no move delay before next try to push */
13930 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13931 player->move_delay = 0;
13933 return MP_NO_ACTION;
13936 if (IS_CUSTOM_ELEMENT(element) &&
13937 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13939 if (!DigFieldByCE(nextx, nexty, element))
13940 return MP_NO_ACTION;
13943 if (IS_SB_ELEMENT(element))
13945 if (element == EL_SOKOBAN_FIELD_FULL)
13947 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13948 local_player->sokobanfields_still_needed++;
13951 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13953 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13954 local_player->sokobanfields_still_needed--;
13957 Feld[x][y] = EL_SOKOBAN_OBJECT;
13959 if (Back[x][y] == Back[nextx][nexty])
13960 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13961 else if (Back[x][y] != 0)
13962 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13965 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13968 if (local_player->sokobanfields_still_needed == 0 &&
13969 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13971 local_player->players_still_needed = 0;
13973 PlayerWins(player);
13975 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13979 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13981 InitMovingField(x, y, move_direction);
13982 GfxAction[x][y] = ACTION_PUSHING;
13984 if (mode == DF_SNAP)
13985 ContinueMoving(x, y);
13987 MovPos[x][y] = (dx != 0 ? dx : dy);
13989 Pushed[x][y] = TRUE;
13990 Pushed[nextx][nexty] = TRUE;
13992 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13993 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13995 player->push_delay_value = -1; /* get new value later */
13997 /* check for element change _after_ element has been pushed */
13998 if (game.use_change_when_pushing_bug)
14000 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14001 player->index_bit, dig_side);
14002 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14003 player->index_bit, dig_side);
14006 else if (IS_SWITCHABLE(element))
14008 if (PLAYER_SWITCHING(player, x, y))
14010 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14011 player->index_bit, dig_side);
14016 player->is_switching = TRUE;
14017 player->switch_x = x;
14018 player->switch_y = y;
14020 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14022 if (element == EL_ROBOT_WHEEL)
14024 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14028 game.robot_wheel_active = TRUE;
14030 TEST_DrawLevelField(x, y);
14032 else if (element == EL_SP_TERMINAL)
14036 SCAN_PLAYFIELD(xx, yy)
14038 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14042 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14044 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14046 ResetGfxAnimation(xx, yy);
14047 TEST_DrawLevelField(xx, yy);
14051 else if (IS_BELT_SWITCH(element))
14053 ToggleBeltSwitch(x, y);
14055 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14056 element == EL_SWITCHGATE_SWITCH_DOWN ||
14057 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14058 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14060 ToggleSwitchgateSwitch(x, y);
14062 else if (element == EL_LIGHT_SWITCH ||
14063 element == EL_LIGHT_SWITCH_ACTIVE)
14065 ToggleLightSwitch(x, y);
14067 else if (element == EL_TIMEGATE_SWITCH ||
14068 element == EL_DC_TIMEGATE_SWITCH)
14070 ActivateTimegateSwitch(x, y);
14072 else if (element == EL_BALLOON_SWITCH_LEFT ||
14073 element == EL_BALLOON_SWITCH_RIGHT ||
14074 element == EL_BALLOON_SWITCH_UP ||
14075 element == EL_BALLOON_SWITCH_DOWN ||
14076 element == EL_BALLOON_SWITCH_NONE ||
14077 element == EL_BALLOON_SWITCH_ANY)
14079 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14080 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14081 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14082 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14083 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14086 else if (element == EL_LAMP)
14088 Feld[x][y] = EL_LAMP_ACTIVE;
14089 local_player->lights_still_needed--;
14091 ResetGfxAnimation(x, y);
14092 TEST_DrawLevelField(x, y);
14094 else if (element == EL_TIME_ORB_FULL)
14096 Feld[x][y] = EL_TIME_ORB_EMPTY;
14098 if (level.time > 0 || level.use_time_orb_bug)
14100 TimeLeft += level.time_orb_time;
14101 game.no_time_limit = FALSE;
14103 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14105 DisplayGameControlValues();
14108 ResetGfxAnimation(x, y);
14109 TEST_DrawLevelField(x, y);
14111 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14112 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14116 game.ball_state = !game.ball_state;
14118 SCAN_PLAYFIELD(xx, yy)
14120 int e = Feld[xx][yy];
14122 if (game.ball_state)
14124 if (e == EL_EMC_MAGIC_BALL)
14125 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14126 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14127 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14131 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14132 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14133 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14134 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14139 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14140 player->index_bit, dig_side);
14142 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14143 player->index_bit, dig_side);
14145 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14146 player->index_bit, dig_side);
14152 if (!PLAYER_SWITCHING(player, x, y))
14154 player->is_switching = TRUE;
14155 player->switch_x = x;
14156 player->switch_y = y;
14158 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14159 player->index_bit, dig_side);
14160 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14161 player->index_bit, dig_side);
14163 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14164 player->index_bit, dig_side);
14165 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14166 player->index_bit, dig_side);
14169 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14170 player->index_bit, dig_side);
14171 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14172 player->index_bit, dig_side);
14174 return MP_NO_ACTION;
14177 player->push_delay = -1;
14179 if (is_player) /* function can also be called by EL_PENGUIN */
14181 if (Feld[x][y] != element) /* really digged/collected something */
14183 player->is_collecting = !player->is_digging;
14184 player->is_active = TRUE;
14191 static boolean DigFieldByCE(int x, int y, int digging_element)
14193 int element = Feld[x][y];
14195 if (!IS_FREE(x, y))
14197 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14198 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14201 /* no element can dig solid indestructible elements */
14202 if (IS_INDESTRUCTIBLE(element) &&
14203 !IS_DIGGABLE(element) &&
14204 !IS_COLLECTIBLE(element))
14207 if (AmoebaNr[x][y] &&
14208 (element == EL_AMOEBA_FULL ||
14209 element == EL_BD_AMOEBA ||
14210 element == EL_AMOEBA_GROWING))
14212 AmoebaCnt[AmoebaNr[x][y]]--;
14213 AmoebaCnt2[AmoebaNr[x][y]]--;
14216 if (IS_MOVING(x, y))
14217 RemoveMovingField(x, y);
14221 TEST_DrawLevelField(x, y);
14224 /* if digged element was about to explode, prevent the explosion */
14225 ExplodeField[x][y] = EX_TYPE_NONE;
14227 PlayLevelSoundAction(x, y, action);
14230 Store[x][y] = EL_EMPTY;
14232 /* this makes it possible to leave the removed element again */
14233 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14234 Store[x][y] = element;
14239 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14241 int jx = player->jx, jy = player->jy;
14242 int x = jx + dx, y = jy + dy;
14243 int snap_direction = (dx == -1 ? MV_LEFT :
14244 dx == +1 ? MV_RIGHT :
14246 dy == +1 ? MV_DOWN : MV_NONE);
14247 boolean can_continue_snapping = (level.continuous_snapping &&
14248 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14250 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14253 if (!player->active || !IN_LEV_FIELD(x, y))
14261 if (player->MovPos == 0)
14262 player->is_pushing = FALSE;
14264 player->is_snapping = FALSE;
14266 if (player->MovPos == 0)
14268 player->is_moving = FALSE;
14269 player->is_digging = FALSE;
14270 player->is_collecting = FALSE;
14276 /* prevent snapping with already pressed snap key when not allowed */
14277 if (player->is_snapping && !can_continue_snapping)
14280 player->MovDir = snap_direction;
14282 if (player->MovPos == 0)
14284 player->is_moving = FALSE;
14285 player->is_digging = FALSE;
14286 player->is_collecting = FALSE;
14289 player->is_dropping = FALSE;
14290 player->is_dropping_pressed = FALSE;
14291 player->drop_pressed_delay = 0;
14293 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14296 player->is_snapping = TRUE;
14297 player->is_active = TRUE;
14299 if (player->MovPos == 0)
14301 player->is_moving = FALSE;
14302 player->is_digging = FALSE;
14303 player->is_collecting = FALSE;
14306 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14307 TEST_DrawLevelField(player->last_jx, player->last_jy);
14309 TEST_DrawLevelField(x, y);
14314 static boolean DropElement(struct PlayerInfo *player)
14316 int old_element, new_element;
14317 int dropx = player->jx, dropy = player->jy;
14318 int drop_direction = player->MovDir;
14319 int drop_side = drop_direction;
14320 int drop_element = get_next_dropped_element(player);
14322 /* do not drop an element on top of another element; when holding drop key
14323 pressed without moving, dropped element must move away before the next
14324 element can be dropped (this is especially important if the next element
14325 is dynamite, which can be placed on background for historical reasons) */
14326 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14329 if (IS_THROWABLE(drop_element))
14331 dropx += GET_DX_FROM_DIR(drop_direction);
14332 dropy += GET_DY_FROM_DIR(drop_direction);
14334 if (!IN_LEV_FIELD(dropx, dropy))
14338 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14339 new_element = drop_element; /* default: no change when dropping */
14341 /* check if player is active, not moving and ready to drop */
14342 if (!player->active || player->MovPos || player->drop_delay > 0)
14345 /* check if player has anything that can be dropped */
14346 if (new_element == EL_UNDEFINED)
14349 /* only set if player has anything that can be dropped */
14350 player->is_dropping_pressed = TRUE;
14352 /* check if drop key was pressed long enough for EM style dynamite */
14353 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14356 /* check if anything can be dropped at the current position */
14357 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14360 /* collected custom elements can only be dropped on empty fields */
14361 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14364 if (old_element != EL_EMPTY)
14365 Back[dropx][dropy] = old_element; /* store old element on this field */
14367 ResetGfxAnimation(dropx, dropy);
14368 ResetRandomAnimationValue(dropx, dropy);
14370 if (player->inventory_size > 0 ||
14371 player->inventory_infinite_element != EL_UNDEFINED)
14373 if (player->inventory_size > 0)
14375 player->inventory_size--;
14377 DrawGameDoorValues();
14379 if (new_element == EL_DYNAMITE)
14380 new_element = EL_DYNAMITE_ACTIVE;
14381 else if (new_element == EL_EM_DYNAMITE)
14382 new_element = EL_EM_DYNAMITE_ACTIVE;
14383 else if (new_element == EL_SP_DISK_RED)
14384 new_element = EL_SP_DISK_RED_ACTIVE;
14387 Feld[dropx][dropy] = new_element;
14389 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14390 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14391 el2img(Feld[dropx][dropy]), 0);
14393 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14395 /* needed if previous element just changed to "empty" in the last frame */
14396 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14398 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14399 player->index_bit, drop_side);
14400 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14402 player->index_bit, drop_side);
14404 TestIfElementTouchesCustomElement(dropx, dropy);
14406 else /* player is dropping a dyna bomb */
14408 player->dynabombs_left--;
14410 Feld[dropx][dropy] = new_element;
14412 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14413 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14414 el2img(Feld[dropx][dropy]), 0);
14416 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14419 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14420 InitField_WithBug1(dropx, dropy, FALSE);
14422 new_element = Feld[dropx][dropy]; /* element might have changed */
14424 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14425 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14427 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14428 MovDir[dropx][dropy] = drop_direction;
14430 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14432 /* do not cause impact style collision by dropping elements that can fall */
14433 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14436 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14437 player->is_dropping = TRUE;
14439 player->drop_pressed_delay = 0;
14440 player->is_dropping_pressed = FALSE;
14442 player->drop_x = dropx;
14443 player->drop_y = dropy;
14448 /* ------------------------------------------------------------------------- */
14449 /* game sound playing functions */
14450 /* ------------------------------------------------------------------------- */
14452 static int *loop_sound_frame = NULL;
14453 static int *loop_sound_volume = NULL;
14455 void InitPlayLevelSound()
14457 int num_sounds = getSoundListSize();
14459 checked_free(loop_sound_frame);
14460 checked_free(loop_sound_volume);
14462 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14463 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14466 static void PlayLevelSound(int x, int y, int nr)
14468 int sx = SCREENX(x), sy = SCREENY(y);
14469 int volume, stereo_position;
14470 int max_distance = 8;
14471 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14473 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14474 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14477 if (!IN_LEV_FIELD(x, y) ||
14478 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14479 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14482 volume = SOUND_MAX_VOLUME;
14484 if (!IN_SCR_FIELD(sx, sy))
14486 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14487 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14489 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14492 stereo_position = (SOUND_MAX_LEFT +
14493 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14494 (SCR_FIELDX + 2 * max_distance));
14496 if (IS_LOOP_SOUND(nr))
14498 /* This assures that quieter loop sounds do not overwrite louder ones,
14499 while restarting sound volume comparison with each new game frame. */
14501 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14504 loop_sound_volume[nr] = volume;
14505 loop_sound_frame[nr] = FrameCounter;
14508 PlaySoundExt(nr, volume, stereo_position, type);
14511 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14513 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14514 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14515 y < LEVELY(BY1) ? LEVELY(BY1) :
14516 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14520 static void PlayLevelSoundAction(int x, int y, int action)
14522 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14525 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14527 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14529 if (sound_effect != SND_UNDEFINED)
14530 PlayLevelSound(x, y, sound_effect);
14533 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14536 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14538 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14539 PlayLevelSound(x, y, sound_effect);
14542 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14544 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14546 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14547 PlayLevelSound(x, y, sound_effect);
14550 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14552 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14554 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14555 StopSound(sound_effect);
14558 static int getLevelMusicNr()
14560 if (levelset.music[level_nr] != MUS_UNDEFINED)
14561 return levelset.music[level_nr]; /* from config file */
14563 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14566 static void FadeLevelSounds()
14571 static void FadeLevelMusic()
14573 int music_nr = getLevelMusicNr();
14574 char *curr_music = getCurrentlyPlayingMusicFilename();
14575 char *next_music = getMusicInfoEntryFilename(music_nr);
14577 if (!strEqual(curr_music, next_music))
14581 void FadeLevelSoundsAndMusic()
14587 static void PlayLevelMusic()
14589 int music_nr = getLevelMusicNr();
14590 char *curr_music = getCurrentlyPlayingMusicFilename();
14591 char *next_music = getMusicInfoEntryFilename(music_nr);
14593 if (!strEqual(curr_music, next_music))
14594 PlayMusic(music_nr);
14597 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14599 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14600 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14601 int x = xx - 1 - offset;
14602 int y = yy - 1 - offset;
14607 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14611 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14615 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14619 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14623 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14627 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14631 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14634 case SAMPLE_android_clone:
14635 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14638 case SAMPLE_android_move:
14639 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14642 case SAMPLE_spring:
14643 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14647 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14651 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14654 case SAMPLE_eater_eat:
14655 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14659 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14662 case SAMPLE_collect:
14663 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14666 case SAMPLE_diamond:
14667 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14670 case SAMPLE_squash:
14671 /* !!! CHECK THIS !!! */
14673 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14675 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14679 case SAMPLE_wonderfall:
14680 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14684 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14688 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14692 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14696 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14700 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14704 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14707 case SAMPLE_wonder:
14708 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14712 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14715 case SAMPLE_exit_open:
14716 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14719 case SAMPLE_exit_leave:
14720 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14723 case SAMPLE_dynamite:
14724 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14728 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14732 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14736 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14740 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14744 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14748 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14752 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14757 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14759 int element = map_element_SP_to_RND(element_sp);
14760 int action = map_action_SP_to_RND(action_sp);
14761 int offset = (setup.sp_show_border_elements ? 0 : 1);
14762 int x = xx - offset;
14763 int y = yy - offset;
14765 PlayLevelSoundElementAction(x, y, element, action);
14768 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14770 int element = map_element_MM_to_RND(element_mm);
14771 int action = map_action_MM_to_RND(action_mm);
14773 int x = xx - offset;
14774 int y = yy - offset;
14776 if (!IS_MM_ELEMENT(element))
14777 element = EL_MM_DEFAULT;
14779 PlayLevelSoundElementAction(x, y, element, action);
14782 void PlaySound_MM(int sound_mm)
14784 int sound = map_sound_MM_to_RND(sound_mm);
14786 if (sound == SND_UNDEFINED)
14792 void PlaySoundLoop_MM(int sound_mm)
14794 int sound = map_sound_MM_to_RND(sound_mm);
14796 if (sound == SND_UNDEFINED)
14799 PlaySoundLoop(sound);
14802 void StopSound_MM(int sound_mm)
14804 int sound = map_sound_MM_to_RND(sound_mm);
14806 if (sound == SND_UNDEFINED)
14812 void RaiseScore(int value)
14814 local_player->score += value;
14816 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14818 DisplayGameControlValues();
14821 void RaiseScoreElement(int element)
14826 case EL_BD_DIAMOND:
14827 case EL_EMERALD_YELLOW:
14828 case EL_EMERALD_RED:
14829 case EL_EMERALD_PURPLE:
14830 case EL_SP_INFOTRON:
14831 RaiseScore(level.score[SC_EMERALD]);
14834 RaiseScore(level.score[SC_DIAMOND]);
14837 RaiseScore(level.score[SC_CRYSTAL]);
14840 RaiseScore(level.score[SC_PEARL]);
14843 case EL_BD_BUTTERFLY:
14844 case EL_SP_ELECTRON:
14845 RaiseScore(level.score[SC_BUG]);
14848 case EL_BD_FIREFLY:
14849 case EL_SP_SNIKSNAK:
14850 RaiseScore(level.score[SC_SPACESHIP]);
14853 case EL_DARK_YAMYAM:
14854 RaiseScore(level.score[SC_YAMYAM]);
14857 RaiseScore(level.score[SC_ROBOT]);
14860 RaiseScore(level.score[SC_PACMAN]);
14863 RaiseScore(level.score[SC_NUT]);
14866 case EL_EM_DYNAMITE:
14867 case EL_SP_DISK_RED:
14868 case EL_DYNABOMB_INCREASE_NUMBER:
14869 case EL_DYNABOMB_INCREASE_SIZE:
14870 case EL_DYNABOMB_INCREASE_POWER:
14871 RaiseScore(level.score[SC_DYNAMITE]);
14873 case EL_SHIELD_NORMAL:
14874 case EL_SHIELD_DEADLY:
14875 RaiseScore(level.score[SC_SHIELD]);
14877 case EL_EXTRA_TIME:
14878 RaiseScore(level.extra_time_score);
14892 case EL_DC_KEY_WHITE:
14893 RaiseScore(level.score[SC_KEY]);
14896 RaiseScore(element_info[element].collect_score);
14901 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14903 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14905 /* closing door required in case of envelope style request dialogs */
14907 CloseDoor(DOOR_CLOSE_1);
14909 if (network.enabled)
14910 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14914 FadeSkipNextFadeIn();
14916 SetGameStatus(GAME_MODE_MAIN);
14921 else /* continue playing the game */
14923 if (tape.playing && tape.deactivate_display)
14924 TapeDeactivateDisplayOff(TRUE);
14926 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14928 if (tape.playing && tape.deactivate_display)
14929 TapeDeactivateDisplayOn();
14933 void RequestQuitGame(boolean ask_if_really_quit)
14935 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14936 boolean skip_request = AllPlayersGone || quick_quit;
14938 RequestQuitGameExt(skip_request, quick_quit,
14939 "Do you really want to quit the game?");
14942 void RequestRestartGame(char *message)
14944 game.restart_game_message = NULL;
14946 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14948 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14952 SetGameStatus(GAME_MODE_MAIN);
14959 /* ------------------------------------------------------------------------- */
14960 /* random generator functions */
14961 /* ------------------------------------------------------------------------- */
14963 unsigned int InitEngineRandom_RND(int seed)
14965 game.num_random_calls = 0;
14967 return InitEngineRandom(seed);
14970 unsigned int RND(int max)
14974 game.num_random_calls++;
14976 return GetEngineRandom(max);
14983 /* ------------------------------------------------------------------------- */
14984 /* game engine snapshot handling functions */
14985 /* ------------------------------------------------------------------------- */
14987 struct EngineSnapshotInfo
14989 /* runtime values for custom element collect score */
14990 int collect_score[NUM_CUSTOM_ELEMENTS];
14992 /* runtime values for group element choice position */
14993 int choice_pos[NUM_GROUP_ELEMENTS];
14995 /* runtime values for belt position animations */
14996 int belt_graphic[4][NUM_BELT_PARTS];
14997 int belt_anim_mode[4][NUM_BELT_PARTS];
15000 static struct EngineSnapshotInfo engine_snapshot_rnd;
15001 static char *snapshot_level_identifier = NULL;
15002 static int snapshot_level_nr = -1;
15004 static void SaveEngineSnapshotValues_RND()
15006 static int belt_base_active_element[4] =
15008 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15009 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15010 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15011 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15015 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15017 int element = EL_CUSTOM_START + i;
15019 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15022 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15024 int element = EL_GROUP_START + i;
15026 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15029 for (i = 0; i < 4; i++)
15031 for (j = 0; j < NUM_BELT_PARTS; j++)
15033 int element = belt_base_active_element[i] + j;
15034 int graphic = el2img(element);
15035 int anim_mode = graphic_info[graphic].anim_mode;
15037 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15038 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15043 static void LoadEngineSnapshotValues_RND()
15045 unsigned int num_random_calls = game.num_random_calls;
15048 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15050 int element = EL_CUSTOM_START + i;
15052 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15055 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15057 int element = EL_GROUP_START + i;
15059 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15062 for (i = 0; i < 4; i++)
15064 for (j = 0; j < NUM_BELT_PARTS; j++)
15066 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15067 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15069 graphic_info[graphic].anim_mode = anim_mode;
15073 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15075 InitRND(tape.random_seed);
15076 for (i = 0; i < num_random_calls; i++)
15080 if (game.num_random_calls != num_random_calls)
15082 Error(ERR_INFO, "number of random calls out of sync");
15083 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15084 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15085 Error(ERR_EXIT, "this should not happen -- please debug");
15089 void FreeEngineSnapshotSingle()
15091 FreeSnapshotSingle();
15093 setString(&snapshot_level_identifier, NULL);
15094 snapshot_level_nr = -1;
15097 void FreeEngineSnapshotList()
15099 FreeSnapshotList();
15102 ListNode *SaveEngineSnapshotBuffers()
15104 ListNode *buffers = NULL;
15106 /* copy some special values to a structure better suited for the snapshot */
15108 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15109 SaveEngineSnapshotValues_RND();
15110 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15111 SaveEngineSnapshotValues_EM();
15112 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15113 SaveEngineSnapshotValues_SP(&buffers);
15114 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15115 SaveEngineSnapshotValues_MM(&buffers);
15117 /* save values stored in special snapshot structure */
15119 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15120 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15121 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15122 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15123 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15124 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15125 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15126 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15128 /* save further RND engine values */
15130 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15131 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15132 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15134 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15135 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15136 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15137 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15139 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15140 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15141 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15142 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15143 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15145 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15146 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15147 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15149 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15151 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15153 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15154 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15156 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15158 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15159 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15161 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15164 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15165 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15168 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15170 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15171 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15173 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15175 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15176 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15178 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15180 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15182 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15185 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15186 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15187 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15188 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15189 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15191 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15192 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15195 ListNode *node = engine_snapshot_list_rnd;
15198 while (node != NULL)
15200 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15205 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15211 void SaveEngineSnapshotSingle()
15213 ListNode *buffers = SaveEngineSnapshotBuffers();
15215 /* finally save all snapshot buffers to single snapshot */
15216 SaveSnapshotSingle(buffers);
15218 /* save level identification information */
15219 setString(&snapshot_level_identifier, leveldir_current->identifier);
15220 snapshot_level_nr = level_nr;
15223 boolean CheckSaveEngineSnapshotToList()
15225 boolean save_snapshot =
15226 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15227 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15228 game.snapshot.changed_action) ||
15229 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15230 game.snapshot.collected_item));
15232 game.snapshot.changed_action = FALSE;
15233 game.snapshot.collected_item = FALSE;
15234 game.snapshot.save_snapshot = save_snapshot;
15236 return save_snapshot;
15239 void SaveEngineSnapshotToList()
15241 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15245 ListNode *buffers = SaveEngineSnapshotBuffers();
15247 /* finally save all snapshot buffers to snapshot list */
15248 SaveSnapshotToList(buffers);
15251 void SaveEngineSnapshotToListInitial()
15253 FreeEngineSnapshotList();
15255 SaveEngineSnapshotToList();
15258 void LoadEngineSnapshotValues()
15260 /* restore special values from snapshot structure */
15262 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15263 LoadEngineSnapshotValues_RND();
15264 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15265 LoadEngineSnapshotValues_EM();
15266 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15267 LoadEngineSnapshotValues_SP();
15268 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15269 LoadEngineSnapshotValues_MM();
15272 void LoadEngineSnapshotSingle()
15274 LoadSnapshotSingle();
15276 LoadEngineSnapshotValues();
15279 void LoadEngineSnapshot_Undo(int steps)
15281 LoadSnapshotFromList_Older(steps);
15283 LoadEngineSnapshotValues();
15286 void LoadEngineSnapshot_Redo(int steps)
15288 LoadSnapshotFromList_Newer(steps);
15290 LoadEngineSnapshotValues();
15293 boolean CheckEngineSnapshotSingle()
15295 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15296 snapshot_level_nr == level_nr);
15299 boolean CheckEngineSnapshotList()
15301 return CheckSnapshotList();
15305 /* ---------- new game button stuff ---------------------------------------- */
15312 boolean *setup_value;
15313 boolean allowed_on_tape;
15315 } gamebutton_info[NUM_GAME_BUTTONS] =
15318 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15319 GAME_CTRL_ID_STOP, NULL,
15323 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15324 GAME_CTRL_ID_PAUSE, NULL,
15328 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15329 GAME_CTRL_ID_PLAY, NULL,
15333 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15334 GAME_CTRL_ID_UNDO, NULL,
15338 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15339 GAME_CTRL_ID_REDO, NULL,
15343 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15344 GAME_CTRL_ID_SAVE, NULL,
15348 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15349 GAME_CTRL_ID_PAUSE2, NULL,
15353 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15354 GAME_CTRL_ID_LOAD, NULL,
15358 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15359 GAME_CTRL_ID_PANEL_STOP, NULL,
15363 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15364 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15365 FALSE, "pause game"
15368 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15369 GAME_CTRL_ID_PANEL_PLAY, NULL,
15373 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15374 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15375 TRUE, "background music on/off"
15378 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15379 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15380 TRUE, "sound loops on/off"
15383 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15384 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15385 TRUE, "normal sounds on/off"
15388 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15389 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15390 FALSE, "background music on/off"
15393 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15394 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15395 FALSE, "sound loops on/off"
15398 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15399 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15400 FALSE, "normal sounds on/off"
15404 void CreateGameButtons()
15408 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15410 int graphic = gamebutton_info[i].graphic;
15411 struct GraphicInfo *gfx = &graphic_info[graphic];
15412 struct XY *pos = gamebutton_info[i].pos;
15413 struct GadgetInfo *gi;
15416 unsigned int event_mask;
15417 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15418 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15419 int base_x = (on_tape ? VX : DX);
15420 int base_y = (on_tape ? VY : DY);
15421 int gd_x = gfx->src_x;
15422 int gd_y = gfx->src_y;
15423 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15424 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15425 int gd_xa = gfx->src_x + gfx->active_xoffset;
15426 int gd_ya = gfx->src_y + gfx->active_yoffset;
15427 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15428 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15431 if (gfx->bitmap == NULL)
15433 game_gadget[id] = NULL;
15438 if (id == GAME_CTRL_ID_STOP ||
15439 id == GAME_CTRL_ID_PANEL_STOP ||
15440 id == GAME_CTRL_ID_PLAY ||
15441 id == GAME_CTRL_ID_PANEL_PLAY ||
15442 id == GAME_CTRL_ID_SAVE ||
15443 id == GAME_CTRL_ID_LOAD)
15445 button_type = GD_TYPE_NORMAL_BUTTON;
15447 event_mask = GD_EVENT_RELEASED;
15449 else if (id == GAME_CTRL_ID_UNDO ||
15450 id == GAME_CTRL_ID_REDO)
15452 button_type = GD_TYPE_NORMAL_BUTTON;
15454 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15458 button_type = GD_TYPE_CHECK_BUTTON;
15459 checked = (gamebutton_info[i].setup_value != NULL ?
15460 *gamebutton_info[i].setup_value : FALSE);
15461 event_mask = GD_EVENT_PRESSED;
15464 gi = CreateGadget(GDI_CUSTOM_ID, id,
15465 GDI_IMAGE_ID, graphic,
15466 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15467 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15468 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15469 GDI_WIDTH, gfx->width,
15470 GDI_HEIGHT, gfx->height,
15471 GDI_TYPE, button_type,
15472 GDI_STATE, GD_BUTTON_UNPRESSED,
15473 GDI_CHECKED, checked,
15474 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15475 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15476 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15477 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15478 GDI_DIRECT_DRAW, FALSE,
15479 GDI_EVENT_MASK, event_mask,
15480 GDI_CALLBACK_ACTION, HandleGameButtons,
15484 Error(ERR_EXIT, "cannot create gadget");
15486 game_gadget[id] = gi;
15490 void FreeGameButtons()
15494 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15495 FreeGadget(game_gadget[i]);
15498 static void UnmapGameButtonsAtSamePosition(int id)
15502 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15504 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15505 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15506 UnmapGadget(game_gadget[i]);
15509 static void UnmapGameButtonsAtSamePosition_All()
15511 if (setup.show_snapshot_buttons)
15513 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15514 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15515 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15519 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15520 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15521 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15523 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15524 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15525 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15529 static void MapGameButtonsAtSamePosition(int id)
15533 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15535 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15536 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15537 MapGadget(game_gadget[i]);
15539 UnmapGameButtonsAtSamePosition_All();
15542 void MapUndoRedoButtons()
15544 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15545 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15547 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15548 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15550 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15553 void UnmapUndoRedoButtons()
15555 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15556 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15558 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15559 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15561 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15564 void MapGameButtonsExt(boolean on_tape)
15568 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15569 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15570 i != GAME_CTRL_ID_UNDO &&
15571 i != GAME_CTRL_ID_REDO)
15572 MapGadget(game_gadget[i]);
15574 UnmapGameButtonsAtSamePosition_All();
15576 RedrawGameButtons();
15579 void UnmapGameButtonsExt(boolean on_tape)
15583 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15584 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15585 UnmapGadget(game_gadget[i]);
15588 void RedrawGameButtonsExt(boolean on_tape)
15592 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15593 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15594 RedrawGadget(game_gadget[i]);
15596 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15597 redraw_mask &= ~REDRAW_ALL;
15600 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15605 gi->checked = state;
15608 void RedrawSoundButtonGadget(int id)
15610 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15611 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15612 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15613 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15614 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15615 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15618 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15619 RedrawGadget(game_gadget[id2]);
15622 void MapGameButtons()
15624 MapGameButtonsExt(FALSE);
15627 void UnmapGameButtons()
15629 UnmapGameButtonsExt(FALSE);
15632 void RedrawGameButtons()
15634 RedrawGameButtonsExt(FALSE);
15637 void MapGameButtonsOnTape()
15639 MapGameButtonsExt(TRUE);
15642 void UnmapGameButtonsOnTape()
15644 UnmapGameButtonsExt(TRUE);
15647 void RedrawGameButtonsOnTape()
15649 RedrawGameButtonsExt(TRUE);
15652 void GameUndoRedoExt()
15654 ClearPlayerAction();
15656 tape.pausing = TRUE;
15659 UpdateAndDisplayGameControlValues();
15661 DrawCompleteVideoDisplay();
15662 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15663 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15664 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15669 void GameUndo(int steps)
15671 if (!CheckEngineSnapshotList())
15674 LoadEngineSnapshot_Undo(steps);
15679 void GameRedo(int steps)
15681 if (!CheckEngineSnapshotList())
15684 LoadEngineSnapshot_Redo(steps);
15689 static void HandleGameButtonsExt(int id, int button)
15691 static boolean game_undo_executed = FALSE;
15692 int steps = BUTTON_STEPSIZE(button);
15693 boolean handle_game_buttons =
15694 (game_status == GAME_MODE_PLAYING ||
15695 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15697 if (!handle_game_buttons)
15702 case GAME_CTRL_ID_STOP:
15703 case GAME_CTRL_ID_PANEL_STOP:
15704 if (game_status == GAME_MODE_MAIN)
15710 RequestQuitGame(TRUE);
15714 case GAME_CTRL_ID_PAUSE:
15715 case GAME_CTRL_ID_PAUSE2:
15716 case GAME_CTRL_ID_PANEL_PAUSE:
15717 if (network.enabled && game_status == GAME_MODE_PLAYING)
15720 SendToServer_ContinuePlaying();
15722 SendToServer_PausePlaying();
15725 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15727 game_undo_executed = FALSE;
15731 case GAME_CTRL_ID_PLAY:
15732 case GAME_CTRL_ID_PANEL_PLAY:
15733 if (game_status == GAME_MODE_MAIN)
15735 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15737 else if (tape.pausing)
15739 if (network.enabled)
15740 SendToServer_ContinuePlaying();
15742 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15746 case GAME_CTRL_ID_UNDO:
15747 // Important: When using "save snapshot when collecting an item" mode,
15748 // load last (current) snapshot for first "undo" after pressing "pause"
15749 // (else the last-but-one snapshot would be loaded, because the snapshot
15750 // pointer already points to the last snapshot when pressing "pause",
15751 // which is fine for "every step/move" mode, but not for "every collect")
15752 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15753 !game_undo_executed)
15756 game_undo_executed = TRUE;
15761 case GAME_CTRL_ID_REDO:
15765 case GAME_CTRL_ID_SAVE:
15769 case GAME_CTRL_ID_LOAD:
15773 case SOUND_CTRL_ID_MUSIC:
15774 case SOUND_CTRL_ID_PANEL_MUSIC:
15775 if (setup.sound_music)
15777 setup.sound_music = FALSE;
15781 else if (audio.music_available)
15783 setup.sound = setup.sound_music = TRUE;
15785 SetAudioMode(setup.sound);
15787 if (game_status == GAME_MODE_PLAYING)
15791 RedrawSoundButtonGadget(id);
15795 case SOUND_CTRL_ID_LOOPS:
15796 case SOUND_CTRL_ID_PANEL_LOOPS:
15797 if (setup.sound_loops)
15798 setup.sound_loops = FALSE;
15799 else if (audio.loops_available)
15801 setup.sound = setup.sound_loops = TRUE;
15803 SetAudioMode(setup.sound);
15806 RedrawSoundButtonGadget(id);
15810 case SOUND_CTRL_ID_SIMPLE:
15811 case SOUND_CTRL_ID_PANEL_SIMPLE:
15812 if (setup.sound_simple)
15813 setup.sound_simple = FALSE;
15814 else if (audio.sound_available)
15816 setup.sound = setup.sound_simple = TRUE;
15818 SetAudioMode(setup.sound);
15821 RedrawSoundButtonGadget(id);
15830 static void HandleGameButtons(struct GadgetInfo *gi)
15832 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15835 void HandleSoundButtonKeys(Key key)
15837 if (key == setup.shortcut.sound_simple)
15838 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15839 else if (key == setup.shortcut.sound_loops)
15840 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15841 else if (key == setup.shortcut.sound_music)
15842 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);