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 RemovePlayerWithCleanup(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->friends_still_needed = 0;
3397 for (j = 0; j < MAX_NUM_KEYS; j++)
3398 player->key[j] = FALSE;
3400 player->num_white_keys = 0;
3402 player->dynabomb_count = 0;
3403 player->dynabomb_size = 1;
3404 player->dynabombs_left = 0;
3405 player->dynabomb_xl = FALSE;
3407 player->MovDir = initial_move_dir;
3410 player->GfxDir = initial_move_dir;
3411 player->GfxAction = ACTION_DEFAULT;
3413 player->StepFrame = 0;
3415 player->initial_element = player->element_nr;
3416 player->artwork_element =
3417 (level.use_artwork_element[i] ? level.artwork_element[i] :
3418 player->element_nr);
3419 player->use_murphy = FALSE;
3421 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3422 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3424 player->gravity = level.initial_player_gravity[i];
3426 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3428 player->actual_frame_counter = 0;
3430 player->step_counter = 0;
3432 player->last_move_dir = initial_move_dir;
3434 player->is_active = FALSE;
3436 player->is_waiting = FALSE;
3437 player->is_moving = FALSE;
3438 player->is_auto_moving = FALSE;
3439 player->is_digging = FALSE;
3440 player->is_snapping = FALSE;
3441 player->is_collecting = FALSE;
3442 player->is_pushing = FALSE;
3443 player->is_switching = FALSE;
3444 player->is_dropping = FALSE;
3445 player->is_dropping_pressed = FALSE;
3447 player->is_bored = FALSE;
3448 player->is_sleeping = FALSE;
3450 player->was_waiting = TRUE;
3451 player->was_moving = FALSE;
3452 player->was_snapping = FALSE;
3453 player->was_dropping = FALSE;
3455 player->force_dropping = FALSE;
3457 player->frame_counter_bored = -1;
3458 player->frame_counter_sleeping = -1;
3460 player->anim_delay_counter = 0;
3461 player->post_delay_counter = 0;
3463 player->dir_waiting = initial_move_dir;
3464 player->action_waiting = ACTION_DEFAULT;
3465 player->last_action_waiting = ACTION_DEFAULT;
3466 player->special_action_bored = ACTION_DEFAULT;
3467 player->special_action_sleeping = ACTION_DEFAULT;
3469 player->switch_x = -1;
3470 player->switch_y = -1;
3472 player->drop_x = -1;
3473 player->drop_y = -1;
3475 player->show_envelope = 0;
3477 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3479 player->push_delay = -1; /* initialized when pushing starts */
3480 player->push_delay_value = game.initial_push_delay_value;
3482 player->drop_delay = 0;
3483 player->drop_pressed_delay = 0;
3485 player->last_jx = -1;
3486 player->last_jy = -1;
3490 player->shield_normal_time_left = 0;
3491 player->shield_deadly_time_left = 0;
3493 player->inventory_infinite_element = EL_UNDEFINED;
3494 player->inventory_size = 0;
3496 if (level.use_initial_inventory[i])
3498 for (j = 0; j < level.initial_inventory_size[i]; j++)
3500 int element = level.initial_inventory_content[i][j];
3501 int collect_count = element_info[element].collect_count_initial;
3504 if (!IS_CUSTOM_ELEMENT(element))
3507 if (collect_count == 0)
3508 player->inventory_infinite_element = element;
3510 for (k = 0; k < collect_count; k++)
3511 if (player->inventory_size < MAX_INVENTORY_SIZE)
3512 player->inventory_element[player->inventory_size++] = element;
3516 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3517 SnapField(player, 0, 0);
3519 player->LevelSolved = FALSE;
3520 player->GameOver = FALSE;
3522 player->LevelSolved_GameWon = FALSE;
3523 player->LevelSolved_GameEnd = FALSE;
3524 player->LevelSolved_PanelOff = FALSE;
3525 player->LevelSolved_SaveTape = FALSE;
3526 player->LevelSolved_SaveScore = FALSE;
3528 player->LevelSolved_CountingTime = 0;
3529 player->LevelSolved_CountingScore = 0;
3530 player->LevelSolved_CountingHealth = 0;
3532 map_player_action[i] = i;
3535 network_player_action_received = FALSE;
3537 /* initial null action */
3538 if (network_playing)
3539 SendToServer_MovePlayer(MV_NONE);
3547 TimeLeft = level.time;
3550 ScreenMovDir = MV_NONE;
3554 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3556 AllPlayersGone = FALSE;
3558 game.no_time_limit = (level.time == 0);
3560 game.yamyam_content_nr = 0;
3561 game.robot_wheel_active = FALSE;
3562 game.magic_wall_active = FALSE;
3563 game.magic_wall_time_left = 0;
3564 game.light_time_left = 0;
3565 game.timegate_time_left = 0;
3566 game.switchgate_pos = 0;
3567 game.wind_direction = level.wind_direction_initial;
3569 game.lenses_time_left = 0;
3570 game.magnify_time_left = 0;
3572 game.ball_state = level.ball_state_initial;
3573 game.ball_content_nr = 0;
3575 game.envelope_active = FALSE;
3577 for (i = 0; i < NUM_BELTS; i++)
3579 game.belt_dir[i] = MV_NONE;
3580 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3583 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3584 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3586 #if DEBUG_INIT_PLAYER
3587 DebugPrintPlayerStatus("Player status at level initialization");
3590 SCAN_PLAYFIELD(x, y)
3592 Feld[x][y] = level.field[x][y];
3593 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3594 ChangeDelay[x][y] = 0;
3595 ChangePage[x][y] = -1;
3596 CustomValue[x][y] = 0; /* initialized in InitField() */
3597 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3599 WasJustMoving[x][y] = 0;
3600 WasJustFalling[x][y] = 0;
3601 CheckCollision[x][y] = 0;
3602 CheckImpact[x][y] = 0;
3604 Pushed[x][y] = FALSE;
3606 ChangeCount[x][y] = 0;
3607 ChangeEvent[x][y] = -1;
3609 ExplodePhase[x][y] = 0;
3610 ExplodeDelay[x][y] = 0;
3611 ExplodeField[x][y] = EX_TYPE_NONE;
3613 RunnerVisit[x][y] = 0;
3614 PlayerVisit[x][y] = 0;
3617 GfxRandom[x][y] = INIT_GFX_RANDOM();
3618 GfxElement[x][y] = EL_UNDEFINED;
3619 GfxAction[x][y] = ACTION_DEFAULT;
3620 GfxDir[x][y] = MV_NONE;
3621 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3624 SCAN_PLAYFIELD(x, y)
3626 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3628 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3630 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3633 InitField(x, y, TRUE);
3635 ResetGfxAnimation(x, y);
3640 for (i = 0; i < MAX_PLAYERS; i++)
3642 struct PlayerInfo *player = &stored_player[i];
3644 /* set number of special actions for bored and sleeping animation */
3645 player->num_special_action_bored =
3646 get_num_special_action(player->artwork_element,
3647 ACTION_BORING_1, ACTION_BORING_LAST);
3648 player->num_special_action_sleeping =
3649 get_num_special_action(player->artwork_element,
3650 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3653 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3654 emulate_sb ? EMU_SOKOBAN :
3655 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3657 /* initialize type of slippery elements */
3658 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3660 if (!IS_CUSTOM_ELEMENT(i))
3662 /* default: elements slip down either to the left or right randomly */
3663 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3665 /* SP style elements prefer to slip down on the left side */
3666 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3667 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3669 /* BD style elements prefer to slip down on the left side */
3670 if (game.emulation == EMU_BOULDERDASH)
3671 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3675 /* initialize explosion and ignition delay */
3676 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3678 if (!IS_CUSTOM_ELEMENT(i))
3681 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3682 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3683 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3684 int last_phase = (num_phase + 1) * delay;
3685 int half_phase = (num_phase / 2) * delay;
3687 element_info[i].explosion_delay = last_phase - 1;
3688 element_info[i].ignition_delay = half_phase;
3690 if (i == EL_BLACK_ORB)
3691 element_info[i].ignition_delay = 1;
3695 /* correct non-moving belts to start moving left */
3696 for (i = 0; i < NUM_BELTS; i++)
3697 if (game.belt_dir[i] == MV_NONE)
3698 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3700 #if USE_NEW_PLAYER_ASSIGNMENTS
3701 for (i = 0; i < MAX_PLAYERS; i++)
3703 stored_player[i].connected = FALSE;
3705 /* in network game mode, the local player might not be the first player */
3706 if (stored_player[i].connected_locally)
3707 local_player = &stored_player[i];
3710 if (!network.enabled)
3711 local_player->connected = TRUE;
3715 for (i = 0; i < MAX_PLAYERS; i++)
3716 stored_player[i].connected = tape.player_participates[i];
3718 else if (network.enabled)
3720 /* add team mode players connected over the network (needed for correct
3721 assignment of player figures from level to locally playing players) */
3723 for (i = 0; i < MAX_PLAYERS; i++)
3724 if (stored_player[i].connected_network)
3725 stored_player[i].connected = TRUE;
3727 else if (game.team_mode)
3729 /* try to guess locally connected team mode players (needed for correct
3730 assignment of player figures from level to locally playing players) */
3732 for (i = 0; i < MAX_PLAYERS; i++)
3733 if (setup.input[i].use_joystick ||
3734 setup.input[i].key.left != KSYM_UNDEFINED)
3735 stored_player[i].connected = TRUE;
3738 #if DEBUG_INIT_PLAYER
3739 DebugPrintPlayerStatus("Player status after level initialization");
3742 #if DEBUG_INIT_PLAYER
3744 printf("Reassigning players ...\n");
3747 /* check if any connected player was not found in playfield */
3748 for (i = 0; i < MAX_PLAYERS; i++)
3750 struct PlayerInfo *player = &stored_player[i];
3752 if (player->connected && !player->present)
3754 struct PlayerInfo *field_player = NULL;
3756 #if DEBUG_INIT_PLAYER
3758 printf("- looking for field player for player %d ...\n", i + 1);
3761 /* assign first free player found that is present in the playfield */
3763 /* first try: look for unmapped playfield player that is not connected */
3764 for (j = 0; j < MAX_PLAYERS; j++)
3765 if (field_player == NULL &&
3766 stored_player[j].present &&
3767 !stored_player[j].mapped &&
3768 !stored_player[j].connected)
3769 field_player = &stored_player[j];
3771 /* second try: look for *any* unmapped playfield player */
3772 for (j = 0; j < MAX_PLAYERS; j++)
3773 if (field_player == NULL &&
3774 stored_player[j].present &&
3775 !stored_player[j].mapped)
3776 field_player = &stored_player[j];
3778 if (field_player != NULL)
3780 int jx = field_player->jx, jy = field_player->jy;
3782 #if DEBUG_INIT_PLAYER
3784 printf("- found player %d\n", field_player->index_nr + 1);
3787 player->present = FALSE;
3788 player->active = FALSE;
3790 field_player->present = TRUE;
3791 field_player->active = TRUE;
3794 player->initial_element = field_player->initial_element;
3795 player->artwork_element = field_player->artwork_element;
3797 player->block_last_field = field_player->block_last_field;
3798 player->block_delay_adjustment = field_player->block_delay_adjustment;
3801 StorePlayer[jx][jy] = field_player->element_nr;
3803 field_player->jx = field_player->last_jx = jx;
3804 field_player->jy = field_player->last_jy = jy;
3806 if (local_player == player)
3807 local_player = field_player;
3809 map_player_action[field_player->index_nr] = i;
3811 field_player->mapped = TRUE;
3813 #if DEBUG_INIT_PLAYER
3815 printf("- map_player_action[%d] == %d\n",
3816 field_player->index_nr + 1, i + 1);
3821 if (player->connected && player->present)
3822 player->mapped = TRUE;
3825 #if DEBUG_INIT_PLAYER
3826 DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3831 /* check if any connected player was not found in playfield */
3832 for (i = 0; i < MAX_PLAYERS; i++)
3834 struct PlayerInfo *player = &stored_player[i];
3836 if (player->connected && !player->present)
3838 for (j = 0; j < MAX_PLAYERS; j++)
3840 struct PlayerInfo *field_player = &stored_player[j];
3841 int jx = field_player->jx, jy = field_player->jy;
3843 /* assign first free player found that is present in the playfield */
3844 if (field_player->present && !field_player->connected)
3846 player->present = TRUE;
3847 player->active = TRUE;
3849 field_player->present = FALSE;
3850 field_player->active = FALSE;
3852 player->initial_element = field_player->initial_element;
3853 player->artwork_element = field_player->artwork_element;
3855 player->block_last_field = field_player->block_last_field;
3856 player->block_delay_adjustment = field_player->block_delay_adjustment;
3858 StorePlayer[jx][jy] = player->element_nr;
3860 player->jx = player->last_jx = jx;
3861 player->jy = player->last_jy = jy;
3871 printf("::: local_player->present == %d\n", local_player->present);
3874 /* set focus to local player for network games, else to all players */
3875 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3876 game.centered_player_nr_next = game.centered_player_nr;
3877 game.set_centered_player = FALSE;
3879 if (network_playing && tape.recording)
3881 /* store client dependent player focus when recording network games */
3882 tape.centered_player_nr_next = game.centered_player_nr_next;
3883 tape.set_centered_player = TRUE;
3888 /* when playing a tape, eliminate all players who do not participate */
3890 #if USE_NEW_PLAYER_ASSIGNMENTS
3892 if (!game.team_mode)
3894 for (i = 0; i < MAX_PLAYERS; i++)
3896 if (stored_player[i].active &&
3897 !tape.player_participates[map_player_action[i]])
3899 struct PlayerInfo *player = &stored_player[i];
3900 int jx = player->jx, jy = player->jy;
3902 #if DEBUG_INIT_PLAYER
3904 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3907 player->active = FALSE;
3908 StorePlayer[jx][jy] = 0;
3909 Feld[jx][jy] = EL_EMPTY;
3916 for (i = 0; i < MAX_PLAYERS; i++)
3918 if (stored_player[i].active &&
3919 !tape.player_participates[i])
3921 struct PlayerInfo *player = &stored_player[i];
3922 int jx = player->jx, jy = player->jy;
3924 player->active = FALSE;
3925 StorePlayer[jx][jy] = 0;
3926 Feld[jx][jy] = EL_EMPTY;
3931 else if (!network.enabled && !game.team_mode) /* && !tape.playing */
3933 /* when in single player mode, eliminate all but the local player */
3935 for (i = 0; i < MAX_PLAYERS; i++)
3937 struct PlayerInfo *player = &stored_player[i];
3939 if (player->active && player != local_player)
3941 int jx = player->jx, jy = player->jy;
3943 player->active = FALSE;
3944 player->present = FALSE;
3946 StorePlayer[jx][jy] = 0;
3947 Feld[jx][jy] = EL_EMPTY;
3952 /* when recording the game, store which players take part in the game */
3955 #if USE_NEW_PLAYER_ASSIGNMENTS
3956 for (i = 0; i < MAX_PLAYERS; i++)
3957 if (stored_player[i].connected)
3958 tape.player_participates[i] = TRUE;
3960 for (i = 0; i < MAX_PLAYERS; i++)
3961 if (stored_player[i].active)
3962 tape.player_participates[i] = TRUE;
3966 #if DEBUG_INIT_PLAYER
3967 DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3970 if (BorderElement == EL_EMPTY)
3973 SBX_Right = lev_fieldx - SCR_FIELDX;
3975 SBY_Lower = lev_fieldy - SCR_FIELDY;
3980 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3982 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3985 if (full_lev_fieldx <= SCR_FIELDX)
3986 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3987 if (full_lev_fieldy <= SCR_FIELDY)
3988 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3990 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3992 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3995 /* if local player not found, look for custom element that might create
3996 the player (make some assumptions about the right custom element) */
3997 if (!local_player->present)
3999 int start_x = 0, start_y = 0;
4000 int found_rating = 0;
4001 int found_element = EL_UNDEFINED;
4002 int player_nr = local_player->index_nr;
4004 SCAN_PLAYFIELD(x, y)
4006 int element = Feld[x][y];
4011 if (level.use_start_element[player_nr] &&
4012 level.start_element[player_nr] == element &&
4019 found_element = element;
4022 if (!IS_CUSTOM_ELEMENT(element))
4025 if (CAN_CHANGE(element))
4027 for (i = 0; i < element_info[element].num_change_pages; i++)
4029 /* check for player created from custom element as single target */
4030 content = element_info[element].change_page[i].target_element;
4031 is_player = ELEM_IS_PLAYER(content);
4033 if (is_player && (found_rating < 3 ||
4034 (found_rating == 3 && element < found_element)))
4040 found_element = element;
4045 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4047 /* check for player created from custom element as explosion content */
4048 content = element_info[element].content.e[xx][yy];
4049 is_player = ELEM_IS_PLAYER(content);
4051 if (is_player && (found_rating < 2 ||
4052 (found_rating == 2 && element < found_element)))
4054 start_x = x + xx - 1;
4055 start_y = y + yy - 1;
4058 found_element = element;
4061 if (!CAN_CHANGE(element))
4064 for (i = 0; i < element_info[element].num_change_pages; i++)
4066 /* check for player created from custom element as extended target */
4068 element_info[element].change_page[i].target_content.e[xx][yy];
4070 is_player = ELEM_IS_PLAYER(content);
4072 if (is_player && (found_rating < 1 ||
4073 (found_rating == 1 && element < found_element)))
4075 start_x = x + xx - 1;
4076 start_y = y + yy - 1;
4079 found_element = element;
4085 scroll_x = SCROLL_POSITION_X(start_x);
4086 scroll_y = SCROLL_POSITION_Y(start_y);
4090 scroll_x = SCROLL_POSITION_X(local_player->jx);
4091 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4094 /* !!! FIX THIS (START) !!! */
4095 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4097 InitGameEngine_EM();
4099 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4101 InitGameEngine_SP();
4103 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4105 InitGameEngine_MM();
4109 DrawLevel(REDRAW_FIELD);
4112 /* after drawing the level, correct some elements */
4113 if (game.timegate_time_left == 0)
4114 CloseAllOpenTimegates();
4117 /* blit playfield from scroll buffer to normal back buffer for fading in */
4118 BlitScreenToBitmap(backbuffer);
4119 /* !!! FIX THIS (END) !!! */
4121 DrawMaskedBorder(fade_mask);
4126 // full screen redraw is required at this point in the following cases:
4127 // - special editor door undrawn when game was started from level editor
4128 // - drawing area (playfield) was changed and has to be removed completely
4129 redraw_mask = REDRAW_ALL;
4133 if (!game.restart_level)
4135 /* copy default game door content to main double buffer */
4137 /* !!! CHECK AGAIN !!! */
4138 SetPanelBackground();
4139 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4140 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4143 SetPanelBackground();
4144 SetDrawBackgroundMask(REDRAW_DOOR_1);
4146 UpdateAndDisplayGameControlValues();
4148 if (!game.restart_level)
4154 CreateGameButtons();
4159 /* copy actual game door content to door double buffer for OpenDoor() */
4160 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4162 OpenDoor(DOOR_OPEN_ALL);
4164 KeyboardAutoRepeatOffUnlessAutoplay();
4166 #if DEBUG_INIT_PLAYER
4167 DebugPrintPlayerStatus("Player status (final)");
4176 if (!game.restart_level && !tape.playing)
4178 LevelStats_incPlayed(level_nr);
4180 SaveLevelSetup_SeriesInfo();
4183 game.restart_level = FALSE;
4184 game.restart_game_message = NULL;
4186 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4187 InitGameActions_MM();
4189 SaveEngineSnapshotToListInitial();
4191 if (!game.restart_level)
4193 PlaySound(SND_GAME_STARTING);
4195 if (setup.sound_music)
4200 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4201 int actual_player_x, int actual_player_y)
4203 /* this is used for non-R'n'D game engines to update certain engine values */
4205 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4207 actual_player_x = correctLevelPosX_EM(actual_player_x);
4208 actual_player_y = correctLevelPosY_EM(actual_player_y);
4211 /* needed to determine if sounds are played within the visible screen area */
4212 scroll_x = actual_scroll_x;
4213 scroll_y = actual_scroll_y;
4215 /* needed to get player position for "follow finger" playing input method */
4216 local_player->jx = actual_player_x;
4217 local_player->jy = actual_player_y;
4220 void InitMovDir(int x, int y)
4222 int i, element = Feld[x][y];
4223 static int xy[4][2] =
4230 static int direction[3][4] =
4232 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4233 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4234 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4243 Feld[x][y] = EL_BUG;
4244 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4247 case EL_SPACESHIP_RIGHT:
4248 case EL_SPACESHIP_UP:
4249 case EL_SPACESHIP_LEFT:
4250 case EL_SPACESHIP_DOWN:
4251 Feld[x][y] = EL_SPACESHIP;
4252 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4255 case EL_BD_BUTTERFLY_RIGHT:
4256 case EL_BD_BUTTERFLY_UP:
4257 case EL_BD_BUTTERFLY_LEFT:
4258 case EL_BD_BUTTERFLY_DOWN:
4259 Feld[x][y] = EL_BD_BUTTERFLY;
4260 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4263 case EL_BD_FIREFLY_RIGHT:
4264 case EL_BD_FIREFLY_UP:
4265 case EL_BD_FIREFLY_LEFT:
4266 case EL_BD_FIREFLY_DOWN:
4267 Feld[x][y] = EL_BD_FIREFLY;
4268 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4271 case EL_PACMAN_RIGHT:
4273 case EL_PACMAN_LEFT:
4274 case EL_PACMAN_DOWN:
4275 Feld[x][y] = EL_PACMAN;
4276 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4279 case EL_YAMYAM_LEFT:
4280 case EL_YAMYAM_RIGHT:
4282 case EL_YAMYAM_DOWN:
4283 Feld[x][y] = EL_YAMYAM;
4284 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4287 case EL_SP_SNIKSNAK:
4288 MovDir[x][y] = MV_UP;
4291 case EL_SP_ELECTRON:
4292 MovDir[x][y] = MV_LEFT;
4299 Feld[x][y] = EL_MOLE;
4300 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4304 if (IS_CUSTOM_ELEMENT(element))
4306 struct ElementInfo *ei = &element_info[element];
4307 int move_direction_initial = ei->move_direction_initial;
4308 int move_pattern = ei->move_pattern;
4310 if (move_direction_initial == MV_START_PREVIOUS)
4312 if (MovDir[x][y] != MV_NONE)
4315 move_direction_initial = MV_START_AUTOMATIC;
4318 if (move_direction_initial == MV_START_RANDOM)
4319 MovDir[x][y] = 1 << RND(4);
4320 else if (move_direction_initial & MV_ANY_DIRECTION)
4321 MovDir[x][y] = move_direction_initial;
4322 else if (move_pattern == MV_ALL_DIRECTIONS ||
4323 move_pattern == MV_TURNING_LEFT ||
4324 move_pattern == MV_TURNING_RIGHT ||
4325 move_pattern == MV_TURNING_LEFT_RIGHT ||
4326 move_pattern == MV_TURNING_RIGHT_LEFT ||
4327 move_pattern == MV_TURNING_RANDOM)
4328 MovDir[x][y] = 1 << RND(4);
4329 else if (move_pattern == MV_HORIZONTAL)
4330 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4331 else if (move_pattern == MV_VERTICAL)
4332 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4333 else if (move_pattern & MV_ANY_DIRECTION)
4334 MovDir[x][y] = element_info[element].move_pattern;
4335 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4336 move_pattern == MV_ALONG_RIGHT_SIDE)
4338 /* use random direction as default start direction */
4339 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4340 MovDir[x][y] = 1 << RND(4);
4342 for (i = 0; i < NUM_DIRECTIONS; i++)
4344 int x1 = x + xy[i][0];
4345 int y1 = y + xy[i][1];
4347 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4349 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4350 MovDir[x][y] = direction[0][i];
4352 MovDir[x][y] = direction[1][i];
4361 MovDir[x][y] = 1 << RND(4);
4363 if (element != EL_BUG &&
4364 element != EL_SPACESHIP &&
4365 element != EL_BD_BUTTERFLY &&
4366 element != EL_BD_FIREFLY)
4369 for (i = 0; i < NUM_DIRECTIONS; i++)
4371 int x1 = x + xy[i][0];
4372 int y1 = y + xy[i][1];
4374 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4376 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4378 MovDir[x][y] = direction[0][i];
4381 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4382 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4384 MovDir[x][y] = direction[1][i];
4393 GfxDir[x][y] = MovDir[x][y];
4396 void InitAmoebaNr(int x, int y)
4399 int group_nr = AmoebeNachbarNr(x, y);
4403 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4405 if (AmoebaCnt[i] == 0)
4413 AmoebaNr[x][y] = group_nr;
4414 AmoebaCnt[group_nr]++;
4415 AmoebaCnt2[group_nr]++;
4418 static void PlayerWins(struct PlayerInfo *player)
4420 player->LevelSolved = TRUE;
4421 player->GameOver = TRUE;
4423 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4424 level.native_em_level->lev->score :
4425 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4428 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4429 MM_HEALTH(game_mm.laser_overload_value) :
4432 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4434 player->LevelSolved_CountingScore = player->score_final;
4435 player->LevelSolved_CountingHealth = player->health_final;
4440 static int time_count_steps;
4441 static int time, time_final;
4442 static int score, score_final;
4443 static int health, health_final;
4444 static int game_over_delay_1 = 0;
4445 static int game_over_delay_2 = 0;
4446 static int game_over_delay_3 = 0;
4447 int game_over_delay_value_1 = 50;
4448 int game_over_delay_value_2 = 25;
4449 int game_over_delay_value_3 = 50;
4451 if (!local_player->LevelSolved_GameWon)
4455 /* do not start end game actions before the player stops moving (to exit) */
4456 if (local_player->MovPos)
4459 local_player->LevelSolved_GameWon = TRUE;
4460 local_player->LevelSolved_SaveTape = tape.recording;
4461 local_player->LevelSolved_SaveScore = !tape.playing;
4465 LevelStats_incSolved(level_nr);
4467 SaveLevelSetup_SeriesInfo();
4470 if (tape.auto_play) /* tape might already be stopped here */
4471 tape.auto_play_level_solved = TRUE;
4475 game_over_delay_1 = 0;
4476 game_over_delay_2 = 0;
4477 game_over_delay_3 = game_over_delay_value_3;
4479 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4480 score = score_final = local_player->score_final;
4481 health = health_final = local_player->health_final;
4483 if (level.score[SC_TIME_BONUS] > 0)
4488 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4490 else if (game.no_time_limit && TimePlayed < 999)
4493 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4496 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4498 game_over_delay_1 = game_over_delay_value_1;
4500 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4503 score_final += health * level.score[SC_TIME_BONUS];
4505 game_over_delay_2 = game_over_delay_value_2;
4508 local_player->score_final = score_final;
4509 local_player->health_final = health_final;
4512 if (level_editor_test_game)
4515 score = score_final;
4517 local_player->LevelSolved_CountingTime = time;
4518 local_player->LevelSolved_CountingScore = score;
4520 game_panel_controls[GAME_PANEL_TIME].value = time;
4521 game_panel_controls[GAME_PANEL_SCORE].value = score;
4523 DisplayGameControlValues();
4526 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4528 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4530 /* close exit door after last player */
4531 if ((AllPlayersGone &&
4532 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4533 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4534 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4535 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4536 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4538 int element = Feld[ExitX][ExitY];
4540 Feld[ExitX][ExitY] =
4541 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4542 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4543 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4544 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4545 EL_EM_STEEL_EXIT_CLOSING);
4547 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4550 /* player disappears */
4551 DrawLevelField(ExitX, ExitY);
4554 for (i = 0; i < MAX_PLAYERS; i++)
4556 struct PlayerInfo *player = &stored_player[i];
4558 if (player->present)
4560 RemovePlayer(player);
4562 /* player disappears */
4563 DrawLevelField(player->jx, player->jy);
4568 PlaySound(SND_GAME_WINNING);
4571 if (game_over_delay_1 > 0)
4573 game_over_delay_1--;
4578 if (time != time_final)
4580 int time_to_go = ABS(time_final - time);
4581 int time_count_dir = (time < time_final ? +1 : -1);
4583 if (time_to_go < time_count_steps)
4584 time_count_steps = 1;
4586 time += time_count_steps * time_count_dir;
4587 score += time_count_steps * level.score[SC_TIME_BONUS];
4589 local_player->LevelSolved_CountingTime = time;
4590 local_player->LevelSolved_CountingScore = score;
4592 game_panel_controls[GAME_PANEL_TIME].value = time;
4593 game_panel_controls[GAME_PANEL_SCORE].value = score;
4595 DisplayGameControlValues();
4597 if (time == time_final)
4598 StopSound(SND_GAME_LEVELTIME_BONUS);
4599 else if (setup.sound_loops)
4600 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4602 PlaySound(SND_GAME_LEVELTIME_BONUS);
4607 if (game_over_delay_2 > 0)
4609 game_over_delay_2--;
4614 if (health != health_final)
4616 int health_count_dir = (health < health_final ? +1 : -1);
4618 health += health_count_dir;
4619 score += level.score[SC_TIME_BONUS];
4621 local_player->LevelSolved_CountingHealth = health;
4622 local_player->LevelSolved_CountingScore = score;
4624 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4625 game_panel_controls[GAME_PANEL_SCORE].value = score;
4627 DisplayGameControlValues();
4629 if (health == health_final)
4630 StopSound(SND_GAME_LEVELTIME_BONUS);
4631 else if (setup.sound_loops)
4632 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4634 PlaySound(SND_GAME_LEVELTIME_BONUS);
4639 local_player->LevelSolved_PanelOff = TRUE;
4641 if (game_over_delay_3 > 0)
4643 game_over_delay_3--;
4654 int last_level_nr = level_nr;
4656 local_player->LevelSolved_GameEnd = TRUE;
4658 if (local_player->LevelSolved_SaveTape)
4660 /* make sure that request dialog to save tape does not open door again */
4661 if (!global.use_envelope_request)
4662 CloseDoor(DOOR_CLOSE_1);
4664 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4667 /* if no tape is to be saved, close both doors simultaneously */
4668 CloseDoor(DOOR_CLOSE_ALL);
4670 if (level_editor_test_game)
4672 SetGameStatus(GAME_MODE_MAIN);
4679 if (!local_player->LevelSolved_SaveScore)
4681 SetGameStatus(GAME_MODE_MAIN);
4688 if (level_nr == leveldir_current->handicap_level)
4690 leveldir_current->handicap_level++;
4692 SaveLevelSetup_SeriesInfo();
4695 if (setup.increment_levels &&
4696 level_nr < leveldir_current->last_level)
4698 level_nr++; /* advance to next level */
4699 TapeErase(); /* start with empty tape */
4701 if (setup.auto_play_next_level)
4703 LoadLevel(level_nr);
4705 SaveLevelSetup_SeriesInfo();
4709 hi_pos = NewHiScore(last_level_nr);
4711 if (hi_pos >= 0 && !setup.skip_scores_after_game)
4713 SetGameStatus(GAME_MODE_SCORES);
4715 DrawHallOfFame(last_level_nr, hi_pos);
4717 else if (!setup.auto_play_next_level || !setup.increment_levels)
4719 SetGameStatus(GAME_MODE_MAIN);
4725 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4729 int NewHiScore(int level_nr)
4733 boolean one_score_entry_per_name = !program.many_scores_per_name;
4735 LoadScore(level_nr);
4737 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4738 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4741 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4743 if (local_player->score_final > highscore[k].Score)
4745 /* player has made it to the hall of fame */
4747 if (k < MAX_SCORE_ENTRIES - 1)
4749 int m = MAX_SCORE_ENTRIES - 1;
4751 if (one_score_entry_per_name)
4753 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4754 if (strEqual(setup.player_name, highscore[l].Name))
4757 if (m == k) /* player's new highscore overwrites his old one */
4761 for (l = m; l > k; l--)
4763 strcpy(highscore[l].Name, highscore[l - 1].Name);
4764 highscore[l].Score = highscore[l - 1].Score;
4770 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4771 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4772 highscore[k].Score = local_player->score_final;
4777 else if (one_score_entry_per_name &&
4778 !strncmp(setup.player_name, highscore[k].Name,
4779 MAX_PLAYER_NAME_LEN))
4780 break; /* player already there with a higher score */
4784 SaveScore(level_nr);
4789 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4791 int element = Feld[x][y];
4792 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4793 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4794 int horiz_move = (dx != 0);
4795 int sign = (horiz_move ? dx : dy);
4796 int step = sign * element_info[element].move_stepsize;
4798 /* special values for move stepsize for spring and things on conveyor belt */
4801 if (CAN_FALL(element) &&
4802 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4803 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4804 else if (element == EL_SPRING)
4805 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4811 inline static int getElementMoveStepsize(int x, int y)
4813 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4816 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4818 if (player->GfxAction != action || player->GfxDir != dir)
4820 player->GfxAction = action;
4821 player->GfxDir = dir;
4823 player->StepFrame = 0;
4827 static void ResetGfxFrame(int x, int y)
4829 // profiling showed that "autotest" spends 10~20% of its time in this function
4830 if (DrawingDeactivatedField())
4833 int element = Feld[x][y];
4834 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4836 if (graphic_info[graphic].anim_global_sync)
4837 GfxFrame[x][y] = FrameCounter;
4838 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4839 GfxFrame[x][y] = CustomValue[x][y];
4840 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4841 GfxFrame[x][y] = element_info[element].collect_score;
4842 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4843 GfxFrame[x][y] = ChangeDelay[x][y];
4846 static void ResetGfxAnimation(int x, int y)
4848 GfxAction[x][y] = ACTION_DEFAULT;
4849 GfxDir[x][y] = MovDir[x][y];
4852 ResetGfxFrame(x, y);
4855 static void ResetRandomAnimationValue(int x, int y)
4857 GfxRandom[x][y] = INIT_GFX_RANDOM();
4860 void InitMovingField(int x, int y, int direction)
4862 int element = Feld[x][y];
4863 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4864 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4867 boolean is_moving_before, is_moving_after;
4869 /* check if element was/is moving or being moved before/after mode change */
4870 is_moving_before = (WasJustMoving[x][y] != 0);
4871 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4873 /* reset animation only for moving elements which change direction of moving
4874 or which just started or stopped moving
4875 (else CEs with property "can move" / "not moving" are reset each frame) */
4876 if (is_moving_before != is_moving_after ||
4877 direction != MovDir[x][y])
4878 ResetGfxAnimation(x, y);
4880 MovDir[x][y] = direction;
4881 GfxDir[x][y] = direction;
4883 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4884 direction == MV_DOWN && CAN_FALL(element) ?
4885 ACTION_FALLING : ACTION_MOVING);
4887 /* this is needed for CEs with property "can move" / "not moving" */
4889 if (is_moving_after)
4891 if (Feld[newx][newy] == EL_EMPTY)
4892 Feld[newx][newy] = EL_BLOCKED;
4894 MovDir[newx][newy] = MovDir[x][y];
4896 CustomValue[newx][newy] = CustomValue[x][y];
4898 GfxFrame[newx][newy] = GfxFrame[x][y];
4899 GfxRandom[newx][newy] = GfxRandom[x][y];
4900 GfxAction[newx][newy] = GfxAction[x][y];
4901 GfxDir[newx][newy] = GfxDir[x][y];
4905 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4907 int direction = MovDir[x][y];
4908 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4909 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4915 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4917 int oldx = x, oldy = y;
4918 int direction = MovDir[x][y];
4920 if (direction == MV_LEFT)
4922 else if (direction == MV_RIGHT)
4924 else if (direction == MV_UP)
4926 else if (direction == MV_DOWN)
4929 *comes_from_x = oldx;
4930 *comes_from_y = oldy;
4933 int MovingOrBlocked2Element(int x, int y)
4935 int element = Feld[x][y];
4937 if (element == EL_BLOCKED)
4941 Blocked2Moving(x, y, &oldx, &oldy);
4942 return Feld[oldx][oldy];
4948 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4950 /* like MovingOrBlocked2Element(), but if element is moving
4951 and (x,y) is the field the moving element is just leaving,
4952 return EL_BLOCKED instead of the element value */
4953 int element = Feld[x][y];
4955 if (IS_MOVING(x, y))
4957 if (element == EL_BLOCKED)
4961 Blocked2Moving(x, y, &oldx, &oldy);
4962 return Feld[oldx][oldy];
4971 static void RemoveField(int x, int y)
4973 Feld[x][y] = EL_EMPTY;
4979 CustomValue[x][y] = 0;
4982 ChangeDelay[x][y] = 0;
4983 ChangePage[x][y] = -1;
4984 Pushed[x][y] = FALSE;
4986 GfxElement[x][y] = EL_UNDEFINED;
4987 GfxAction[x][y] = ACTION_DEFAULT;
4988 GfxDir[x][y] = MV_NONE;
4991 void RemoveMovingField(int x, int y)
4993 int oldx = x, oldy = y, newx = x, newy = y;
4994 int element = Feld[x][y];
4995 int next_element = EL_UNDEFINED;
4997 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5000 if (IS_MOVING(x, y))
5002 Moving2Blocked(x, y, &newx, &newy);
5004 if (Feld[newx][newy] != EL_BLOCKED)
5006 /* element is moving, but target field is not free (blocked), but
5007 already occupied by something different (example: acid pool);
5008 in this case, only remove the moving field, but not the target */
5010 RemoveField(oldx, oldy);
5012 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5014 TEST_DrawLevelField(oldx, oldy);
5019 else if (element == EL_BLOCKED)
5021 Blocked2Moving(x, y, &oldx, &oldy);
5022 if (!IS_MOVING(oldx, oldy))
5026 if (element == EL_BLOCKED &&
5027 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5028 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5029 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5030 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5031 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5032 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5033 next_element = get_next_element(Feld[oldx][oldy]);
5035 RemoveField(oldx, oldy);
5036 RemoveField(newx, newy);
5038 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5040 if (next_element != EL_UNDEFINED)
5041 Feld[oldx][oldy] = next_element;
5043 TEST_DrawLevelField(oldx, oldy);
5044 TEST_DrawLevelField(newx, newy);
5047 void DrawDynamite(int x, int y)
5049 int sx = SCREENX(x), sy = SCREENY(y);
5050 int graphic = el2img(Feld[x][y]);
5053 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5056 if (IS_WALKABLE_INSIDE(Back[x][y]))
5060 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5061 else if (Store[x][y])
5062 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5064 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5066 if (Back[x][y] || Store[x][y])
5067 DrawGraphicThruMask(sx, sy, graphic, frame);
5069 DrawGraphic(sx, sy, graphic, frame);
5072 void CheckDynamite(int x, int y)
5074 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5078 if (MovDelay[x][y] != 0)
5081 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5087 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5092 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5094 boolean num_checked_players = 0;
5097 for (i = 0; i < MAX_PLAYERS; i++)
5099 if (stored_player[i].active)
5101 int sx = stored_player[i].jx;
5102 int sy = stored_player[i].jy;
5104 if (num_checked_players == 0)
5111 *sx1 = MIN(*sx1, sx);
5112 *sy1 = MIN(*sy1, sy);
5113 *sx2 = MAX(*sx2, sx);
5114 *sy2 = MAX(*sy2, sy);
5117 num_checked_players++;
5122 static boolean checkIfAllPlayersFitToScreen_RND()
5124 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5126 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5128 return (sx2 - sx1 < SCR_FIELDX &&
5129 sy2 - sy1 < SCR_FIELDY);
5132 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5134 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5136 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5138 *sx = (sx1 + sx2) / 2;
5139 *sy = (sy1 + sy2) / 2;
5142 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5143 boolean center_screen, boolean quick_relocation)
5145 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5146 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5147 boolean no_delay = (tape.warp_forward);
5148 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5149 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5150 int new_scroll_x, new_scroll_y;
5152 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5154 /* case 1: quick relocation inside visible screen (without scrolling) */
5161 if (!level.shifted_relocation || center_screen)
5163 /* relocation _with_ centering of screen */
5165 new_scroll_x = SCROLL_POSITION_X(x);
5166 new_scroll_y = SCROLL_POSITION_Y(y);
5170 /* relocation _without_ centering of screen */
5172 int center_scroll_x = SCROLL_POSITION_X(old_x);
5173 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5174 int offset_x = x + (scroll_x - center_scroll_x);
5175 int offset_y = y + (scroll_y - center_scroll_y);
5177 /* for new screen position, apply previous offset to center position */
5178 new_scroll_x = SCROLL_POSITION_X(offset_x);
5179 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5182 if (quick_relocation)
5184 /* case 2: quick relocation (redraw without visible scrolling) */
5186 scroll_x = new_scroll_x;
5187 scroll_y = new_scroll_y;
5194 /* case 3: visible relocation (with scrolling to new position) */
5196 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5198 SetVideoFrameDelay(wait_delay_value);
5200 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5203 int fx = FX, fy = FY;
5205 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5206 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5208 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5214 fx += dx * TILEX / 2;
5215 fy += dy * TILEY / 2;
5217 ScrollLevel(dx, dy);
5220 /* scroll in two steps of half tile size to make things smoother */
5221 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5223 /* scroll second step to align at full tile size */
5224 BlitScreenToBitmap(window);
5230 SetVideoFrameDelay(frame_delay_value_old);
5233 void RelocatePlayer(int jx, int jy, int el_player_raw)
5235 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5236 int player_nr = GET_PLAYER_NR(el_player);
5237 struct PlayerInfo *player = &stored_player[player_nr];
5238 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5239 boolean no_delay = (tape.warp_forward);
5240 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5241 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5242 int old_jx = player->jx;
5243 int old_jy = player->jy;
5244 int old_element = Feld[old_jx][old_jy];
5245 int element = Feld[jx][jy];
5246 boolean player_relocated = (old_jx != jx || old_jy != jy);
5248 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5249 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5250 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5251 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5252 int leave_side_horiz = move_dir_horiz;
5253 int leave_side_vert = move_dir_vert;
5254 int enter_side = enter_side_horiz | enter_side_vert;
5255 int leave_side = leave_side_horiz | leave_side_vert;
5257 if (player->GameOver) /* do not reanimate dead player */
5260 if (!player_relocated) /* no need to relocate the player */
5263 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5265 RemoveField(jx, jy); /* temporarily remove newly placed player */
5266 DrawLevelField(jx, jy);
5269 if (player->present)
5271 while (player->MovPos)
5273 ScrollPlayer(player, SCROLL_GO_ON);
5274 ScrollScreen(NULL, SCROLL_GO_ON);
5276 AdvanceFrameAndPlayerCounters(player->index_nr);
5280 BackToFront_WithFrameDelay(wait_delay_value);
5283 DrawPlayer(player); /* needed here only to cleanup last field */
5284 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5286 player->is_moving = FALSE;
5289 if (IS_CUSTOM_ELEMENT(old_element))
5290 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5292 player->index_bit, leave_side);
5294 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5296 player->index_bit, leave_side);
5298 Feld[jx][jy] = el_player;
5299 InitPlayerField(jx, jy, el_player, TRUE);
5301 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5302 possible that the relocation target field did not contain a player element,
5303 but a walkable element, to which the new player was relocated -- in this
5304 case, restore that (already initialized!) element on the player field */
5305 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5307 Feld[jx][jy] = element; /* restore previously existing element */
5310 /* only visually relocate centered player */
5311 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5312 FALSE, level.instant_relocation);
5314 TestIfPlayerTouchesBadThing(jx, jy);
5315 TestIfPlayerTouchesCustomElement(jx, jy);
5317 if (IS_CUSTOM_ELEMENT(element))
5318 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5319 player->index_bit, enter_side);
5321 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5322 player->index_bit, enter_side);
5324 if (player->is_switching)
5326 /* ensure that relocation while still switching an element does not cause
5327 a new element to be treated as also switched directly after relocation
5328 (this is important for teleporter switches that teleport the player to
5329 a place where another teleporter switch is in the same direction, which
5330 would then incorrectly be treated as immediately switched before the
5331 direction key that caused the switch was released) */
5333 player->switch_x += jx - old_jx;
5334 player->switch_y += jy - old_jy;
5338 void Explode(int ex, int ey, int phase, int mode)
5344 /* !!! eliminate this variable !!! */
5345 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5347 if (game.explosions_delayed)
5349 ExplodeField[ex][ey] = mode;
5353 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5355 int center_element = Feld[ex][ey];
5356 int artwork_element, explosion_element; /* set these values later */
5358 /* remove things displayed in background while burning dynamite */
5359 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5362 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5364 /* put moving element to center field (and let it explode there) */
5365 center_element = MovingOrBlocked2Element(ex, ey);
5366 RemoveMovingField(ex, ey);
5367 Feld[ex][ey] = center_element;
5370 /* now "center_element" is finally determined -- set related values now */
5371 artwork_element = center_element; /* for custom player artwork */
5372 explosion_element = center_element; /* for custom player artwork */
5374 if (IS_PLAYER(ex, ey))
5376 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5378 artwork_element = stored_player[player_nr].artwork_element;
5380 if (level.use_explosion_element[player_nr])
5382 explosion_element = level.explosion_element[player_nr];
5383 artwork_element = explosion_element;
5387 if (mode == EX_TYPE_NORMAL ||
5388 mode == EX_TYPE_CENTER ||
5389 mode == EX_TYPE_CROSS)
5390 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5392 last_phase = element_info[explosion_element].explosion_delay + 1;
5394 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5396 int xx = x - ex + 1;
5397 int yy = y - ey + 1;
5400 if (!IN_LEV_FIELD(x, y) ||
5401 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5402 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5405 element = Feld[x][y];
5407 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5409 element = MovingOrBlocked2Element(x, y);
5411 if (!IS_EXPLOSION_PROOF(element))
5412 RemoveMovingField(x, y);
5415 /* indestructible elements can only explode in center (but not flames) */
5416 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5417 mode == EX_TYPE_BORDER)) ||
5418 element == EL_FLAMES)
5421 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5422 behaviour, for example when touching a yamyam that explodes to rocks
5423 with active deadly shield, a rock is created under the player !!! */
5424 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5426 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5427 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5428 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5430 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5433 if (IS_ACTIVE_BOMB(element))
5435 /* re-activate things under the bomb like gate or penguin */
5436 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5443 /* save walkable background elements while explosion on same tile */
5444 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5445 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5446 Back[x][y] = element;
5448 /* ignite explodable elements reached by other explosion */
5449 if (element == EL_EXPLOSION)
5450 element = Store2[x][y];
5452 if (AmoebaNr[x][y] &&
5453 (element == EL_AMOEBA_FULL ||
5454 element == EL_BD_AMOEBA ||
5455 element == EL_AMOEBA_GROWING))
5457 AmoebaCnt[AmoebaNr[x][y]]--;
5458 AmoebaCnt2[AmoebaNr[x][y]]--;
5463 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5465 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5467 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5469 if (PLAYERINFO(ex, ey)->use_murphy)
5470 Store[x][y] = EL_EMPTY;
5473 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5474 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5475 else if (ELEM_IS_PLAYER(center_element))
5476 Store[x][y] = EL_EMPTY;
5477 else if (center_element == EL_YAMYAM)
5478 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5479 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5480 Store[x][y] = element_info[center_element].content.e[xx][yy];
5482 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5483 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5484 otherwise) -- FIX THIS !!! */
5485 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5486 Store[x][y] = element_info[element].content.e[1][1];
5488 else if (!CAN_EXPLODE(element))
5489 Store[x][y] = element_info[element].content.e[1][1];
5492 Store[x][y] = EL_EMPTY;
5494 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5495 center_element == EL_AMOEBA_TO_DIAMOND)
5496 Store2[x][y] = element;
5498 Feld[x][y] = EL_EXPLOSION;
5499 GfxElement[x][y] = artwork_element;
5501 ExplodePhase[x][y] = 1;
5502 ExplodeDelay[x][y] = last_phase;
5507 if (center_element == EL_YAMYAM)
5508 game.yamyam_content_nr =
5509 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5521 GfxFrame[x][y] = 0; /* restart explosion animation */
5523 last_phase = ExplodeDelay[x][y];
5525 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5527 /* this can happen if the player leaves an explosion just in time */
5528 if (GfxElement[x][y] == EL_UNDEFINED)
5529 GfxElement[x][y] = EL_EMPTY;
5531 border_element = Store2[x][y];
5532 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5533 border_element = StorePlayer[x][y];
5535 if (phase == element_info[border_element].ignition_delay ||
5536 phase == last_phase)
5538 boolean border_explosion = FALSE;
5540 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5541 !PLAYER_EXPLOSION_PROTECTED(x, y))
5543 KillPlayerUnlessExplosionProtected(x, y);
5544 border_explosion = TRUE;
5546 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5548 Feld[x][y] = Store2[x][y];
5551 border_explosion = TRUE;
5553 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5555 AmoebeUmwandeln(x, y);
5557 border_explosion = TRUE;
5560 /* if an element just explodes due to another explosion (chain-reaction),
5561 do not immediately end the new explosion when it was the last frame of
5562 the explosion (as it would be done in the following "if"-statement!) */
5563 if (border_explosion && phase == last_phase)
5567 if (phase == last_phase)
5571 element = Feld[x][y] = Store[x][y];
5572 Store[x][y] = Store2[x][y] = 0;
5573 GfxElement[x][y] = EL_UNDEFINED;
5575 /* player can escape from explosions and might therefore be still alive */
5576 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5577 element <= EL_PLAYER_IS_EXPLODING_4)
5579 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5580 int explosion_element = EL_PLAYER_1 + player_nr;
5581 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5582 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5584 if (level.use_explosion_element[player_nr])
5585 explosion_element = level.explosion_element[player_nr];
5587 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5588 element_info[explosion_element].content.e[xx][yy]);
5591 /* restore probably existing indestructible background element */
5592 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5593 element = Feld[x][y] = Back[x][y];
5596 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5597 GfxDir[x][y] = MV_NONE;
5598 ChangeDelay[x][y] = 0;
5599 ChangePage[x][y] = -1;
5601 CustomValue[x][y] = 0;
5603 InitField_WithBug2(x, y, FALSE);
5605 TEST_DrawLevelField(x, y);
5607 TestIfElementTouchesCustomElement(x, y);
5609 if (GFX_CRUMBLED(element))
5610 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5612 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5613 StorePlayer[x][y] = 0;
5615 if (ELEM_IS_PLAYER(element))
5616 RelocatePlayer(x, y, element);
5618 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5620 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5621 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5624 TEST_DrawLevelFieldCrumbled(x, y);
5626 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5628 DrawLevelElement(x, y, Back[x][y]);
5629 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5631 else if (IS_WALKABLE_UNDER(Back[x][y]))
5633 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5634 DrawLevelElementThruMask(x, y, Back[x][y]);
5636 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5637 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5641 void DynaExplode(int ex, int ey)
5644 int dynabomb_element = Feld[ex][ey];
5645 int dynabomb_size = 1;
5646 boolean dynabomb_xl = FALSE;
5647 struct PlayerInfo *player;
5648 static int xy[4][2] =
5656 if (IS_ACTIVE_BOMB(dynabomb_element))
5658 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5659 dynabomb_size = player->dynabomb_size;
5660 dynabomb_xl = player->dynabomb_xl;
5661 player->dynabombs_left++;
5664 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5666 for (i = 0; i < NUM_DIRECTIONS; i++)
5668 for (j = 1; j <= dynabomb_size; j++)
5670 int x = ex + j * xy[i][0];
5671 int y = ey + j * xy[i][1];
5674 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5677 element = Feld[x][y];
5679 /* do not restart explosions of fields with active bombs */
5680 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5683 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5685 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5686 !IS_DIGGABLE(element) && !dynabomb_xl)
5692 void Bang(int x, int y)
5694 int element = MovingOrBlocked2Element(x, y);
5695 int explosion_type = EX_TYPE_NORMAL;
5697 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5699 struct PlayerInfo *player = PLAYERINFO(x, y);
5701 element = Feld[x][y] = player->initial_element;
5703 if (level.use_explosion_element[player->index_nr])
5705 int explosion_element = level.explosion_element[player->index_nr];
5707 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5708 explosion_type = EX_TYPE_CROSS;
5709 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5710 explosion_type = EX_TYPE_CENTER;
5718 case EL_BD_BUTTERFLY:
5721 case EL_DARK_YAMYAM:
5725 RaiseScoreElement(element);
5728 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5729 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5730 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5731 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5732 case EL_DYNABOMB_INCREASE_NUMBER:
5733 case EL_DYNABOMB_INCREASE_SIZE:
5734 case EL_DYNABOMB_INCREASE_POWER:
5735 explosion_type = EX_TYPE_DYNA;
5738 case EL_DC_LANDMINE:
5739 explosion_type = EX_TYPE_CENTER;
5744 case EL_LAMP_ACTIVE:
5745 case EL_AMOEBA_TO_DIAMOND:
5746 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5747 explosion_type = EX_TYPE_CENTER;
5751 if (element_info[element].explosion_type == EXPLODES_CROSS)
5752 explosion_type = EX_TYPE_CROSS;
5753 else if (element_info[element].explosion_type == EXPLODES_1X1)
5754 explosion_type = EX_TYPE_CENTER;
5758 if (explosion_type == EX_TYPE_DYNA)
5761 Explode(x, y, EX_PHASE_START, explosion_type);
5763 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5766 void SplashAcid(int x, int y)
5768 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5769 (!IN_LEV_FIELD(x - 1, y - 2) ||
5770 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5771 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5773 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5774 (!IN_LEV_FIELD(x + 1, y - 2) ||
5775 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5776 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5778 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5781 static void InitBeltMovement()
5783 static int belt_base_element[4] =
5785 EL_CONVEYOR_BELT_1_LEFT,
5786 EL_CONVEYOR_BELT_2_LEFT,
5787 EL_CONVEYOR_BELT_3_LEFT,
5788 EL_CONVEYOR_BELT_4_LEFT
5790 static int belt_base_active_element[4] =
5792 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5793 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5794 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5795 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5800 /* set frame order for belt animation graphic according to belt direction */
5801 for (i = 0; i < NUM_BELTS; i++)
5805 for (j = 0; j < NUM_BELT_PARTS; j++)
5807 int element = belt_base_active_element[belt_nr] + j;
5808 int graphic_1 = el2img(element);
5809 int graphic_2 = el2panelimg(element);
5811 if (game.belt_dir[i] == MV_LEFT)
5813 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5814 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5818 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5819 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5824 SCAN_PLAYFIELD(x, y)
5826 int element = Feld[x][y];
5828 for (i = 0; i < NUM_BELTS; i++)
5830 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5832 int e_belt_nr = getBeltNrFromBeltElement(element);
5835 if (e_belt_nr == belt_nr)
5837 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5839 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5846 static void ToggleBeltSwitch(int x, int y)
5848 static int belt_base_element[4] =
5850 EL_CONVEYOR_BELT_1_LEFT,
5851 EL_CONVEYOR_BELT_2_LEFT,
5852 EL_CONVEYOR_BELT_3_LEFT,
5853 EL_CONVEYOR_BELT_4_LEFT
5855 static int belt_base_active_element[4] =
5857 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5858 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5859 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5860 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5862 static int belt_base_switch_element[4] =
5864 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5865 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5866 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5867 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5869 static int belt_move_dir[4] =
5877 int element = Feld[x][y];
5878 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5879 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5880 int belt_dir = belt_move_dir[belt_dir_nr];
5883 if (!IS_BELT_SWITCH(element))
5886 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5887 game.belt_dir[belt_nr] = belt_dir;
5889 if (belt_dir_nr == 3)
5892 /* set frame order for belt animation graphic according to belt direction */
5893 for (i = 0; i < NUM_BELT_PARTS; i++)
5895 int element = belt_base_active_element[belt_nr] + i;
5896 int graphic_1 = el2img(element);
5897 int graphic_2 = el2panelimg(element);
5899 if (belt_dir == MV_LEFT)
5901 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5902 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5906 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5907 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5911 SCAN_PLAYFIELD(xx, yy)
5913 int element = Feld[xx][yy];
5915 if (IS_BELT_SWITCH(element))
5917 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5919 if (e_belt_nr == belt_nr)
5921 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5922 TEST_DrawLevelField(xx, yy);
5925 else if (IS_BELT(element) && belt_dir != MV_NONE)
5927 int e_belt_nr = getBeltNrFromBeltElement(element);
5929 if (e_belt_nr == belt_nr)
5931 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5933 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5934 TEST_DrawLevelField(xx, yy);
5937 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5939 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5941 if (e_belt_nr == belt_nr)
5943 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5945 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5946 TEST_DrawLevelField(xx, yy);
5952 static void ToggleSwitchgateSwitch(int x, int y)
5956 game.switchgate_pos = !game.switchgate_pos;
5958 SCAN_PLAYFIELD(xx, yy)
5960 int element = Feld[xx][yy];
5962 if (element == EL_SWITCHGATE_SWITCH_UP)
5964 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5965 TEST_DrawLevelField(xx, yy);
5967 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5969 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5970 TEST_DrawLevelField(xx, yy);
5972 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5974 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5975 TEST_DrawLevelField(xx, yy);
5977 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5979 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5980 TEST_DrawLevelField(xx, yy);
5982 else if (element == EL_SWITCHGATE_OPEN ||
5983 element == EL_SWITCHGATE_OPENING)
5985 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5987 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5989 else if (element == EL_SWITCHGATE_CLOSED ||
5990 element == EL_SWITCHGATE_CLOSING)
5992 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5994 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5999 static int getInvisibleActiveFromInvisibleElement(int element)
6001 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6002 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6003 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6007 static int getInvisibleFromInvisibleActiveElement(int element)
6009 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6010 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6011 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6015 static void RedrawAllLightSwitchesAndInvisibleElements()
6019 SCAN_PLAYFIELD(x, y)
6021 int element = Feld[x][y];
6023 if (element == EL_LIGHT_SWITCH &&
6024 game.light_time_left > 0)
6026 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6027 TEST_DrawLevelField(x, y);
6029 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6030 game.light_time_left == 0)
6032 Feld[x][y] = EL_LIGHT_SWITCH;
6033 TEST_DrawLevelField(x, y);
6035 else if (element == EL_EMC_DRIPPER &&
6036 game.light_time_left > 0)
6038 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6039 TEST_DrawLevelField(x, y);
6041 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6042 game.light_time_left == 0)
6044 Feld[x][y] = EL_EMC_DRIPPER;
6045 TEST_DrawLevelField(x, y);
6047 else if (element == EL_INVISIBLE_STEELWALL ||
6048 element == EL_INVISIBLE_WALL ||
6049 element == EL_INVISIBLE_SAND)
6051 if (game.light_time_left > 0)
6052 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6054 TEST_DrawLevelField(x, y);
6056 /* uncrumble neighbour fields, if needed */
6057 if (element == EL_INVISIBLE_SAND)
6058 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6060 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6061 element == EL_INVISIBLE_WALL_ACTIVE ||
6062 element == EL_INVISIBLE_SAND_ACTIVE)
6064 if (game.light_time_left == 0)
6065 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6067 TEST_DrawLevelField(x, y);
6069 /* re-crumble neighbour fields, if needed */
6070 if (element == EL_INVISIBLE_SAND)
6071 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6076 static void RedrawAllInvisibleElementsForLenses()
6080 SCAN_PLAYFIELD(x, y)
6082 int element = Feld[x][y];
6084 if (element == EL_EMC_DRIPPER &&
6085 game.lenses_time_left > 0)
6087 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6088 TEST_DrawLevelField(x, y);
6090 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6091 game.lenses_time_left == 0)
6093 Feld[x][y] = EL_EMC_DRIPPER;
6094 TEST_DrawLevelField(x, y);
6096 else if (element == EL_INVISIBLE_STEELWALL ||
6097 element == EL_INVISIBLE_WALL ||
6098 element == EL_INVISIBLE_SAND)
6100 if (game.lenses_time_left > 0)
6101 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6103 TEST_DrawLevelField(x, y);
6105 /* uncrumble neighbour fields, if needed */
6106 if (element == EL_INVISIBLE_SAND)
6107 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6109 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6110 element == EL_INVISIBLE_WALL_ACTIVE ||
6111 element == EL_INVISIBLE_SAND_ACTIVE)
6113 if (game.lenses_time_left == 0)
6114 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6116 TEST_DrawLevelField(x, y);
6118 /* re-crumble neighbour fields, if needed */
6119 if (element == EL_INVISIBLE_SAND)
6120 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6125 static void RedrawAllInvisibleElementsForMagnifier()
6129 SCAN_PLAYFIELD(x, y)
6131 int element = Feld[x][y];
6133 if (element == EL_EMC_FAKE_GRASS &&
6134 game.magnify_time_left > 0)
6136 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6137 TEST_DrawLevelField(x, y);
6139 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6140 game.magnify_time_left == 0)
6142 Feld[x][y] = EL_EMC_FAKE_GRASS;
6143 TEST_DrawLevelField(x, y);
6145 else if (IS_GATE_GRAY(element) &&
6146 game.magnify_time_left > 0)
6148 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6149 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6150 IS_EM_GATE_GRAY(element) ?
6151 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6152 IS_EMC_GATE_GRAY(element) ?
6153 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6154 IS_DC_GATE_GRAY(element) ?
6155 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6157 TEST_DrawLevelField(x, y);
6159 else if (IS_GATE_GRAY_ACTIVE(element) &&
6160 game.magnify_time_left == 0)
6162 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6163 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6164 IS_EM_GATE_GRAY_ACTIVE(element) ?
6165 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6166 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6167 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6168 IS_DC_GATE_GRAY_ACTIVE(element) ?
6169 EL_DC_GATE_WHITE_GRAY :
6171 TEST_DrawLevelField(x, y);
6176 static void ToggleLightSwitch(int x, int y)
6178 int element = Feld[x][y];
6180 game.light_time_left =
6181 (element == EL_LIGHT_SWITCH ?
6182 level.time_light * FRAMES_PER_SECOND : 0);
6184 RedrawAllLightSwitchesAndInvisibleElements();
6187 static void ActivateTimegateSwitch(int x, int y)
6191 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6193 SCAN_PLAYFIELD(xx, yy)
6195 int element = Feld[xx][yy];
6197 if (element == EL_TIMEGATE_CLOSED ||
6198 element == EL_TIMEGATE_CLOSING)
6200 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6201 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6205 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6207 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6208 TEST_DrawLevelField(xx, yy);
6214 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6215 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6218 void Impact(int x, int y)
6220 boolean last_line = (y == lev_fieldy - 1);
6221 boolean object_hit = FALSE;
6222 boolean impact = (last_line || object_hit);
6223 int element = Feld[x][y];
6224 int smashed = EL_STEELWALL;
6226 if (!last_line) /* check if element below was hit */
6228 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6231 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6232 MovDir[x][y + 1] != MV_DOWN ||
6233 MovPos[x][y + 1] <= TILEY / 2));
6235 /* do not smash moving elements that left the smashed field in time */
6236 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6237 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6240 #if USE_QUICKSAND_IMPACT_BUGFIX
6241 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6243 RemoveMovingField(x, y + 1);
6244 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6245 Feld[x][y + 2] = EL_ROCK;
6246 TEST_DrawLevelField(x, y + 2);
6251 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6253 RemoveMovingField(x, y + 1);
6254 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6255 Feld[x][y + 2] = EL_ROCK;
6256 TEST_DrawLevelField(x, y + 2);
6263 smashed = MovingOrBlocked2Element(x, y + 1);
6265 impact = (last_line || object_hit);
6268 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6270 SplashAcid(x, y + 1);
6274 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6275 /* only reset graphic animation if graphic really changes after impact */
6277 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6279 ResetGfxAnimation(x, y);
6280 TEST_DrawLevelField(x, y);
6283 if (impact && CAN_EXPLODE_IMPACT(element))
6288 else if (impact && element == EL_PEARL &&
6289 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6291 ResetGfxAnimation(x, y);
6293 Feld[x][y] = EL_PEARL_BREAKING;
6294 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6297 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6299 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6304 if (impact && element == EL_AMOEBA_DROP)
6306 if (object_hit && IS_PLAYER(x, y + 1))
6307 KillPlayerUnlessEnemyProtected(x, y + 1);
6308 else if (object_hit && smashed == EL_PENGUIN)
6312 Feld[x][y] = EL_AMOEBA_GROWING;
6313 Store[x][y] = EL_AMOEBA_WET;
6315 ResetRandomAnimationValue(x, y);
6320 if (object_hit) /* check which object was hit */
6322 if ((CAN_PASS_MAGIC_WALL(element) &&
6323 (smashed == EL_MAGIC_WALL ||
6324 smashed == EL_BD_MAGIC_WALL)) ||
6325 (CAN_PASS_DC_MAGIC_WALL(element) &&
6326 smashed == EL_DC_MAGIC_WALL))
6329 int activated_magic_wall =
6330 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6331 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6332 EL_DC_MAGIC_WALL_ACTIVE);
6334 /* activate magic wall / mill */
6335 SCAN_PLAYFIELD(xx, yy)
6337 if (Feld[xx][yy] == smashed)
6338 Feld[xx][yy] = activated_magic_wall;
6341 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6342 game.magic_wall_active = TRUE;
6344 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6345 SND_MAGIC_WALL_ACTIVATING :
6346 smashed == EL_BD_MAGIC_WALL ?
6347 SND_BD_MAGIC_WALL_ACTIVATING :
6348 SND_DC_MAGIC_WALL_ACTIVATING));
6351 if (IS_PLAYER(x, y + 1))
6353 if (CAN_SMASH_PLAYER(element))
6355 KillPlayerUnlessEnemyProtected(x, y + 1);
6359 else if (smashed == EL_PENGUIN)
6361 if (CAN_SMASH_PLAYER(element))
6367 else if (element == EL_BD_DIAMOND)
6369 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6375 else if (((element == EL_SP_INFOTRON ||
6376 element == EL_SP_ZONK) &&
6377 (smashed == EL_SP_SNIKSNAK ||
6378 smashed == EL_SP_ELECTRON ||
6379 smashed == EL_SP_DISK_ORANGE)) ||
6380 (element == EL_SP_INFOTRON &&
6381 smashed == EL_SP_DISK_YELLOW))
6386 else if (CAN_SMASH_EVERYTHING(element))
6388 if (IS_CLASSIC_ENEMY(smashed) ||
6389 CAN_EXPLODE_SMASHED(smashed))
6394 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6396 if (smashed == EL_LAMP ||
6397 smashed == EL_LAMP_ACTIVE)
6402 else if (smashed == EL_NUT)
6404 Feld[x][y + 1] = EL_NUT_BREAKING;
6405 PlayLevelSound(x, y, SND_NUT_BREAKING);
6406 RaiseScoreElement(EL_NUT);
6409 else if (smashed == EL_PEARL)
6411 ResetGfxAnimation(x, y);
6413 Feld[x][y + 1] = EL_PEARL_BREAKING;
6414 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6417 else if (smashed == EL_DIAMOND)
6419 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6420 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6423 else if (IS_BELT_SWITCH(smashed))
6425 ToggleBeltSwitch(x, y + 1);
6427 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6428 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6429 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6430 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6432 ToggleSwitchgateSwitch(x, y + 1);
6434 else if (smashed == EL_LIGHT_SWITCH ||
6435 smashed == EL_LIGHT_SWITCH_ACTIVE)
6437 ToggleLightSwitch(x, y + 1);
6441 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6443 CheckElementChangeBySide(x, y + 1, smashed, element,
6444 CE_SWITCHED, CH_SIDE_TOP);
6445 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6451 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6456 /* play sound of magic wall / mill */
6458 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6459 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6460 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6462 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6463 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6464 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6465 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6466 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6467 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6472 /* play sound of object that hits the ground */
6473 if (last_line || object_hit)
6474 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6477 inline static void TurnRoundExt(int x, int y)
6489 { 0, 0 }, { 0, 0 }, { 0, 0 },
6494 int left, right, back;
6498 { MV_DOWN, MV_UP, MV_RIGHT },
6499 { MV_UP, MV_DOWN, MV_LEFT },
6501 { MV_LEFT, MV_RIGHT, MV_DOWN },
6505 { MV_RIGHT, MV_LEFT, MV_UP }
6508 int element = Feld[x][y];
6509 int move_pattern = element_info[element].move_pattern;
6511 int old_move_dir = MovDir[x][y];
6512 int left_dir = turn[old_move_dir].left;
6513 int right_dir = turn[old_move_dir].right;
6514 int back_dir = turn[old_move_dir].back;
6516 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6517 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6518 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6519 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6521 int left_x = x + left_dx, left_y = y + left_dy;
6522 int right_x = x + right_dx, right_y = y + right_dy;
6523 int move_x = x + move_dx, move_y = y + move_dy;
6527 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6529 TestIfBadThingTouchesOtherBadThing(x, y);
6531 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6532 MovDir[x][y] = right_dir;
6533 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6534 MovDir[x][y] = left_dir;
6536 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6538 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6541 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6543 TestIfBadThingTouchesOtherBadThing(x, y);
6545 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6546 MovDir[x][y] = left_dir;
6547 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6548 MovDir[x][y] = right_dir;
6550 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6552 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6555 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6557 TestIfBadThingTouchesOtherBadThing(x, y);
6559 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6560 MovDir[x][y] = left_dir;
6561 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6562 MovDir[x][y] = right_dir;
6564 if (MovDir[x][y] != old_move_dir)
6567 else if (element == EL_YAMYAM)
6569 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6570 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6572 if (can_turn_left && can_turn_right)
6573 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6574 else if (can_turn_left)
6575 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6576 else if (can_turn_right)
6577 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6579 MovDir[x][y] = back_dir;
6581 MovDelay[x][y] = 16 + 16 * RND(3);
6583 else if (element == EL_DARK_YAMYAM)
6585 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6587 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6590 if (can_turn_left && can_turn_right)
6591 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6592 else if (can_turn_left)
6593 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6594 else if (can_turn_right)
6595 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6597 MovDir[x][y] = back_dir;
6599 MovDelay[x][y] = 16 + 16 * RND(3);
6601 else if (element == EL_PACMAN)
6603 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6604 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6606 if (can_turn_left && can_turn_right)
6607 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6608 else if (can_turn_left)
6609 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6610 else if (can_turn_right)
6611 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6613 MovDir[x][y] = back_dir;
6615 MovDelay[x][y] = 6 + RND(40);
6617 else if (element == EL_PIG)
6619 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6620 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6621 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6622 boolean should_turn_left, should_turn_right, should_move_on;
6624 int rnd = RND(rnd_value);
6626 should_turn_left = (can_turn_left &&
6628 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6629 y + back_dy + left_dy)));
6630 should_turn_right = (can_turn_right &&
6632 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6633 y + back_dy + right_dy)));
6634 should_move_on = (can_move_on &&
6637 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6638 y + move_dy + left_dy) ||
6639 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6640 y + move_dy + right_dy)));
6642 if (should_turn_left || should_turn_right || should_move_on)
6644 if (should_turn_left && should_turn_right && should_move_on)
6645 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6646 rnd < 2 * rnd_value / 3 ? right_dir :
6648 else if (should_turn_left && should_turn_right)
6649 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6650 else if (should_turn_left && should_move_on)
6651 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6652 else if (should_turn_right && should_move_on)
6653 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6654 else if (should_turn_left)
6655 MovDir[x][y] = left_dir;
6656 else if (should_turn_right)
6657 MovDir[x][y] = right_dir;
6658 else if (should_move_on)
6659 MovDir[x][y] = old_move_dir;
6661 else if (can_move_on && rnd > rnd_value / 8)
6662 MovDir[x][y] = old_move_dir;
6663 else if (can_turn_left && can_turn_right)
6664 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6665 else if (can_turn_left && rnd > rnd_value / 8)
6666 MovDir[x][y] = left_dir;
6667 else if (can_turn_right && rnd > rnd_value/8)
6668 MovDir[x][y] = right_dir;
6670 MovDir[x][y] = back_dir;
6672 xx = x + move_xy[MovDir[x][y]].dx;
6673 yy = y + move_xy[MovDir[x][y]].dy;
6675 if (!IN_LEV_FIELD(xx, yy) ||
6676 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6677 MovDir[x][y] = old_move_dir;
6681 else if (element == EL_DRAGON)
6683 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6684 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6685 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6687 int rnd = RND(rnd_value);
6689 if (can_move_on && rnd > rnd_value / 8)
6690 MovDir[x][y] = old_move_dir;
6691 else if (can_turn_left && can_turn_right)
6692 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6693 else if (can_turn_left && rnd > rnd_value / 8)
6694 MovDir[x][y] = left_dir;
6695 else if (can_turn_right && rnd > rnd_value / 8)
6696 MovDir[x][y] = right_dir;
6698 MovDir[x][y] = back_dir;
6700 xx = x + move_xy[MovDir[x][y]].dx;
6701 yy = y + move_xy[MovDir[x][y]].dy;
6703 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6704 MovDir[x][y] = old_move_dir;
6708 else if (element == EL_MOLE)
6710 boolean can_move_on =
6711 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6712 IS_AMOEBOID(Feld[move_x][move_y]) ||
6713 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6716 boolean can_turn_left =
6717 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6718 IS_AMOEBOID(Feld[left_x][left_y])));
6720 boolean can_turn_right =
6721 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6722 IS_AMOEBOID(Feld[right_x][right_y])));
6724 if (can_turn_left && can_turn_right)
6725 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6726 else if (can_turn_left)
6727 MovDir[x][y] = left_dir;
6729 MovDir[x][y] = right_dir;
6732 if (MovDir[x][y] != old_move_dir)
6735 else if (element == EL_BALLOON)
6737 MovDir[x][y] = game.wind_direction;
6740 else if (element == EL_SPRING)
6742 if (MovDir[x][y] & MV_HORIZONTAL)
6744 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6745 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6747 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6748 ResetGfxAnimation(move_x, move_y);
6749 TEST_DrawLevelField(move_x, move_y);
6751 MovDir[x][y] = back_dir;
6753 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6754 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6755 MovDir[x][y] = MV_NONE;
6760 else if (element == EL_ROBOT ||
6761 element == EL_SATELLITE ||
6762 element == EL_PENGUIN ||
6763 element == EL_EMC_ANDROID)
6765 int attr_x = -1, attr_y = -1;
6776 for (i = 0; i < MAX_PLAYERS; i++)
6778 struct PlayerInfo *player = &stored_player[i];
6779 int jx = player->jx, jy = player->jy;
6781 if (!player->active)
6785 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6793 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6794 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6795 game.engine_version < VERSION_IDENT(3,1,0,0)))
6801 if (element == EL_PENGUIN)
6804 static int xy[4][2] =
6812 for (i = 0; i < NUM_DIRECTIONS; i++)
6814 int ex = x + xy[i][0];
6815 int ey = y + xy[i][1];
6817 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6818 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6819 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6820 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6829 MovDir[x][y] = MV_NONE;
6831 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6832 else if (attr_x > x)
6833 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6835 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6836 else if (attr_y > y)
6837 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6839 if (element == EL_ROBOT)
6843 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6844 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6845 Moving2Blocked(x, y, &newx, &newy);
6847 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6848 MovDelay[x][y] = 8 + 8 * !RND(3);
6850 MovDelay[x][y] = 16;
6852 else if (element == EL_PENGUIN)
6858 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6860 boolean first_horiz = RND(2);
6861 int new_move_dir = MovDir[x][y];
6864 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6865 Moving2Blocked(x, y, &newx, &newy);
6867 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6871 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6872 Moving2Blocked(x, y, &newx, &newy);
6874 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6877 MovDir[x][y] = old_move_dir;
6881 else if (element == EL_SATELLITE)
6887 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6889 boolean first_horiz = RND(2);
6890 int new_move_dir = MovDir[x][y];
6893 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6894 Moving2Blocked(x, y, &newx, &newy);
6896 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6900 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6901 Moving2Blocked(x, y, &newx, &newy);
6903 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6906 MovDir[x][y] = old_move_dir;
6910 else if (element == EL_EMC_ANDROID)
6912 static int check_pos[16] =
6914 -1, /* 0 => (invalid) */
6915 7, /* 1 => MV_LEFT */
6916 3, /* 2 => MV_RIGHT */
6917 -1, /* 3 => (invalid) */
6919 0, /* 5 => MV_LEFT | MV_UP */
6920 2, /* 6 => MV_RIGHT | MV_UP */
6921 -1, /* 7 => (invalid) */
6922 5, /* 8 => MV_DOWN */
6923 6, /* 9 => MV_LEFT | MV_DOWN */
6924 4, /* 10 => MV_RIGHT | MV_DOWN */
6925 -1, /* 11 => (invalid) */
6926 -1, /* 12 => (invalid) */
6927 -1, /* 13 => (invalid) */
6928 -1, /* 14 => (invalid) */
6929 -1, /* 15 => (invalid) */
6937 { -1, -1, MV_LEFT | MV_UP },
6939 { +1, -1, MV_RIGHT | MV_UP },
6940 { +1, 0, MV_RIGHT },
6941 { +1, +1, MV_RIGHT | MV_DOWN },
6943 { -1, +1, MV_LEFT | MV_DOWN },
6946 int start_pos, check_order;
6947 boolean can_clone = FALSE;
6950 /* check if there is any free field around current position */
6951 for (i = 0; i < 8; i++)
6953 int newx = x + check_xy[i].dx;
6954 int newy = y + check_xy[i].dy;
6956 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6964 if (can_clone) /* randomly find an element to clone */
6968 start_pos = check_pos[RND(8)];
6969 check_order = (RND(2) ? -1 : +1);
6971 for (i = 0; i < 8; i++)
6973 int pos_raw = start_pos + i * check_order;
6974 int pos = (pos_raw + 8) % 8;
6975 int newx = x + check_xy[pos].dx;
6976 int newy = y + check_xy[pos].dy;
6978 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6980 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6981 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6983 Store[x][y] = Feld[newx][newy];
6992 if (can_clone) /* randomly find a direction to move */
6996 start_pos = check_pos[RND(8)];
6997 check_order = (RND(2) ? -1 : +1);
6999 for (i = 0; i < 8; i++)
7001 int pos_raw = start_pos + i * check_order;
7002 int pos = (pos_raw + 8) % 8;
7003 int newx = x + check_xy[pos].dx;
7004 int newy = y + check_xy[pos].dy;
7005 int new_move_dir = check_xy[pos].dir;
7007 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7009 MovDir[x][y] = new_move_dir;
7010 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7019 if (can_clone) /* cloning and moving successful */
7022 /* cannot clone -- try to move towards player */
7024 start_pos = check_pos[MovDir[x][y] & 0x0f];
7025 check_order = (RND(2) ? -1 : +1);
7027 for (i = 0; i < 3; i++)
7029 /* first check start_pos, then previous/next or (next/previous) pos */
7030 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7031 int pos = (pos_raw + 8) % 8;
7032 int newx = x + check_xy[pos].dx;
7033 int newy = y + check_xy[pos].dy;
7034 int new_move_dir = check_xy[pos].dir;
7036 if (IS_PLAYER(newx, newy))
7039 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7041 MovDir[x][y] = new_move_dir;
7042 MovDelay[x][y] = level.android_move_time * 8 + 1;
7049 else if (move_pattern == MV_TURNING_LEFT ||
7050 move_pattern == MV_TURNING_RIGHT ||
7051 move_pattern == MV_TURNING_LEFT_RIGHT ||
7052 move_pattern == MV_TURNING_RIGHT_LEFT ||
7053 move_pattern == MV_TURNING_RANDOM ||
7054 move_pattern == MV_ALL_DIRECTIONS)
7056 boolean can_turn_left =
7057 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7058 boolean can_turn_right =
7059 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7061 if (element_info[element].move_stepsize == 0) /* "not moving" */
7064 if (move_pattern == MV_TURNING_LEFT)
7065 MovDir[x][y] = left_dir;
7066 else if (move_pattern == MV_TURNING_RIGHT)
7067 MovDir[x][y] = right_dir;
7068 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7069 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7070 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7071 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7072 else if (move_pattern == MV_TURNING_RANDOM)
7073 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7074 can_turn_right && !can_turn_left ? right_dir :
7075 RND(2) ? left_dir : right_dir);
7076 else if (can_turn_left && can_turn_right)
7077 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7078 else if (can_turn_left)
7079 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7080 else if (can_turn_right)
7081 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7083 MovDir[x][y] = back_dir;
7085 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7087 else if (move_pattern == MV_HORIZONTAL ||
7088 move_pattern == MV_VERTICAL)
7090 if (move_pattern & old_move_dir)
7091 MovDir[x][y] = back_dir;
7092 else if (move_pattern == MV_HORIZONTAL)
7093 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7094 else if (move_pattern == MV_VERTICAL)
7095 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7097 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7099 else if (move_pattern & MV_ANY_DIRECTION)
7101 MovDir[x][y] = move_pattern;
7102 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7104 else if (move_pattern & MV_WIND_DIRECTION)
7106 MovDir[x][y] = game.wind_direction;
7107 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7109 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7111 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7112 MovDir[x][y] = left_dir;
7113 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7114 MovDir[x][y] = right_dir;
7116 if (MovDir[x][y] != old_move_dir)
7117 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7119 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7121 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7122 MovDir[x][y] = right_dir;
7123 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7124 MovDir[x][y] = left_dir;
7126 if (MovDir[x][y] != old_move_dir)
7127 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7129 else if (move_pattern == MV_TOWARDS_PLAYER ||
7130 move_pattern == MV_AWAY_FROM_PLAYER)
7132 int attr_x = -1, attr_y = -1;
7134 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7145 for (i = 0; i < MAX_PLAYERS; i++)
7147 struct PlayerInfo *player = &stored_player[i];
7148 int jx = player->jx, jy = player->jy;
7150 if (!player->active)
7154 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7162 MovDir[x][y] = MV_NONE;
7164 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7165 else if (attr_x > x)
7166 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7168 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7169 else if (attr_y > y)
7170 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7172 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7174 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7176 boolean first_horiz = RND(2);
7177 int new_move_dir = MovDir[x][y];
7179 if (element_info[element].move_stepsize == 0) /* "not moving" */
7181 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7182 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7188 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7189 Moving2Blocked(x, y, &newx, &newy);
7191 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7195 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7196 Moving2Blocked(x, y, &newx, &newy);
7198 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7201 MovDir[x][y] = old_move_dir;
7204 else if (move_pattern == MV_WHEN_PUSHED ||
7205 move_pattern == MV_WHEN_DROPPED)
7207 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7208 MovDir[x][y] = MV_NONE;
7212 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7214 static int test_xy[7][2] =
7224 static int test_dir[7] =
7234 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7235 int move_preference = -1000000; /* start with very low preference */
7236 int new_move_dir = MV_NONE;
7237 int start_test = RND(4);
7240 for (i = 0; i < NUM_DIRECTIONS; i++)
7242 int move_dir = test_dir[start_test + i];
7243 int move_dir_preference;
7245 xx = x + test_xy[start_test + i][0];
7246 yy = y + test_xy[start_test + i][1];
7248 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7249 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7251 new_move_dir = move_dir;
7256 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7259 move_dir_preference = -1 * RunnerVisit[xx][yy];
7260 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7261 move_dir_preference = PlayerVisit[xx][yy];
7263 if (move_dir_preference > move_preference)
7265 /* prefer field that has not been visited for the longest time */
7266 move_preference = move_dir_preference;
7267 new_move_dir = move_dir;
7269 else if (move_dir_preference == move_preference &&
7270 move_dir == old_move_dir)
7272 /* prefer last direction when all directions are preferred equally */
7273 move_preference = move_dir_preference;
7274 new_move_dir = move_dir;
7278 MovDir[x][y] = new_move_dir;
7279 if (old_move_dir != new_move_dir)
7280 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7284 static void TurnRound(int x, int y)
7286 int direction = MovDir[x][y];
7290 GfxDir[x][y] = MovDir[x][y];
7292 if (direction != MovDir[x][y])
7296 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7298 ResetGfxFrame(x, y);
7301 static boolean JustBeingPushed(int x, int y)
7305 for (i = 0; i < MAX_PLAYERS; i++)
7307 struct PlayerInfo *player = &stored_player[i];
7309 if (player->active && player->is_pushing && player->MovPos)
7311 int next_jx = player->jx + (player->jx - player->last_jx);
7312 int next_jy = player->jy + (player->jy - player->last_jy);
7314 if (x == next_jx && y == next_jy)
7322 void StartMoving(int x, int y)
7324 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7325 int element = Feld[x][y];
7330 if (MovDelay[x][y] == 0)
7331 GfxAction[x][y] = ACTION_DEFAULT;
7333 if (CAN_FALL(element) && y < lev_fieldy - 1)
7335 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7336 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7337 if (JustBeingPushed(x, y))
7340 if (element == EL_QUICKSAND_FULL)
7342 if (IS_FREE(x, y + 1))
7344 InitMovingField(x, y, MV_DOWN);
7345 started_moving = TRUE;
7347 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7348 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7349 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7350 Store[x][y] = EL_ROCK;
7352 Store[x][y] = EL_ROCK;
7355 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7357 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7359 if (!MovDelay[x][y])
7361 MovDelay[x][y] = TILEY + 1;
7363 ResetGfxAnimation(x, y);
7364 ResetGfxAnimation(x, y + 1);
7369 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7370 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7377 Feld[x][y] = EL_QUICKSAND_EMPTY;
7378 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7379 Store[x][y + 1] = Store[x][y];
7382 PlayLevelSoundAction(x, y, ACTION_FILLING);
7384 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7386 if (!MovDelay[x][y])
7388 MovDelay[x][y] = TILEY + 1;
7390 ResetGfxAnimation(x, y);
7391 ResetGfxAnimation(x, y + 1);
7396 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7397 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7404 Feld[x][y] = EL_QUICKSAND_EMPTY;
7405 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7406 Store[x][y + 1] = Store[x][y];
7409 PlayLevelSoundAction(x, y, ACTION_FILLING);
7412 else if (element == EL_QUICKSAND_FAST_FULL)
7414 if (IS_FREE(x, y + 1))
7416 InitMovingField(x, y, MV_DOWN);
7417 started_moving = TRUE;
7419 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7420 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7421 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7422 Store[x][y] = EL_ROCK;
7424 Store[x][y] = EL_ROCK;
7427 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7429 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7431 if (!MovDelay[x][y])
7433 MovDelay[x][y] = TILEY + 1;
7435 ResetGfxAnimation(x, y);
7436 ResetGfxAnimation(x, y + 1);
7441 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7442 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7449 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7450 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7451 Store[x][y + 1] = Store[x][y];
7454 PlayLevelSoundAction(x, y, ACTION_FILLING);
7456 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7458 if (!MovDelay[x][y])
7460 MovDelay[x][y] = TILEY + 1;
7462 ResetGfxAnimation(x, y);
7463 ResetGfxAnimation(x, y + 1);
7468 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7469 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7476 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7477 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7478 Store[x][y + 1] = Store[x][y];
7481 PlayLevelSoundAction(x, y, ACTION_FILLING);
7484 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7485 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7487 InitMovingField(x, y, MV_DOWN);
7488 started_moving = TRUE;
7490 Feld[x][y] = EL_QUICKSAND_FILLING;
7491 Store[x][y] = element;
7493 PlayLevelSoundAction(x, y, ACTION_FILLING);
7495 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7496 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7498 InitMovingField(x, y, MV_DOWN);
7499 started_moving = TRUE;
7501 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7502 Store[x][y] = element;
7504 PlayLevelSoundAction(x, y, ACTION_FILLING);
7506 else if (element == EL_MAGIC_WALL_FULL)
7508 if (IS_FREE(x, y + 1))
7510 InitMovingField(x, y, MV_DOWN);
7511 started_moving = TRUE;
7513 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7514 Store[x][y] = EL_CHANGED(Store[x][y]);
7516 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7518 if (!MovDelay[x][y])
7519 MovDelay[x][y] = TILEY / 4 + 1;
7528 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7529 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7530 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7534 else if (element == EL_BD_MAGIC_WALL_FULL)
7536 if (IS_FREE(x, y + 1))
7538 InitMovingField(x, y, MV_DOWN);
7539 started_moving = TRUE;
7541 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7542 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7544 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7546 if (!MovDelay[x][y])
7547 MovDelay[x][y] = TILEY / 4 + 1;
7556 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7557 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7558 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7562 else if (element == EL_DC_MAGIC_WALL_FULL)
7564 if (IS_FREE(x, y + 1))
7566 InitMovingField(x, y, MV_DOWN);
7567 started_moving = TRUE;
7569 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7570 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7572 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7574 if (!MovDelay[x][y])
7575 MovDelay[x][y] = TILEY / 4 + 1;
7584 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7585 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7586 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7590 else if ((CAN_PASS_MAGIC_WALL(element) &&
7591 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7592 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7593 (CAN_PASS_DC_MAGIC_WALL(element) &&
7594 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7597 InitMovingField(x, y, MV_DOWN);
7598 started_moving = TRUE;
7601 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7602 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7603 EL_DC_MAGIC_WALL_FILLING);
7604 Store[x][y] = element;
7606 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7608 SplashAcid(x, y + 1);
7610 InitMovingField(x, y, MV_DOWN);
7611 started_moving = TRUE;
7613 Store[x][y] = EL_ACID;
7616 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7617 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7618 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7619 CAN_FALL(element) && WasJustFalling[x][y] &&
7620 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7622 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7623 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7624 (Feld[x][y + 1] == EL_BLOCKED)))
7626 /* this is needed for a special case not covered by calling "Impact()"
7627 from "ContinueMoving()": if an element moves to a tile directly below
7628 another element which was just falling on that tile (which was empty
7629 in the previous frame), the falling element above would just stop
7630 instead of smashing the element below (in previous version, the above
7631 element was just checked for "moving" instead of "falling", resulting
7632 in incorrect smashes caused by horizontal movement of the above
7633 element; also, the case of the player being the element to smash was
7634 simply not covered here... :-/ ) */
7636 CheckCollision[x][y] = 0;
7637 CheckImpact[x][y] = 0;
7641 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7643 if (MovDir[x][y] == MV_NONE)
7645 InitMovingField(x, y, MV_DOWN);
7646 started_moving = TRUE;
7649 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7651 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7652 MovDir[x][y] = MV_DOWN;
7654 InitMovingField(x, y, MV_DOWN);
7655 started_moving = TRUE;
7657 else if (element == EL_AMOEBA_DROP)
7659 Feld[x][y] = EL_AMOEBA_GROWING;
7660 Store[x][y] = EL_AMOEBA_WET;
7662 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7663 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7664 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7665 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7667 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7668 (IS_FREE(x - 1, y + 1) ||
7669 Feld[x - 1][y + 1] == EL_ACID));
7670 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7671 (IS_FREE(x + 1, y + 1) ||
7672 Feld[x + 1][y + 1] == EL_ACID));
7673 boolean can_fall_any = (can_fall_left || can_fall_right);
7674 boolean can_fall_both = (can_fall_left && can_fall_right);
7675 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7677 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7679 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7680 can_fall_right = FALSE;
7681 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7682 can_fall_left = FALSE;
7683 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7684 can_fall_right = FALSE;
7685 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7686 can_fall_left = FALSE;
7688 can_fall_any = (can_fall_left || can_fall_right);
7689 can_fall_both = FALSE;
7694 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7695 can_fall_right = FALSE; /* slip down on left side */
7697 can_fall_left = !(can_fall_right = RND(2));
7699 can_fall_both = FALSE;
7704 /* if not determined otherwise, prefer left side for slipping down */
7705 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7706 started_moving = TRUE;
7709 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7711 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7712 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7713 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7714 int belt_dir = game.belt_dir[belt_nr];
7716 if ((belt_dir == MV_LEFT && left_is_free) ||
7717 (belt_dir == MV_RIGHT && right_is_free))
7719 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7721 InitMovingField(x, y, belt_dir);
7722 started_moving = TRUE;
7724 Pushed[x][y] = TRUE;
7725 Pushed[nextx][y] = TRUE;
7727 GfxAction[x][y] = ACTION_DEFAULT;
7731 MovDir[x][y] = 0; /* if element was moving, stop it */
7736 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7737 if (CAN_MOVE(element) && !started_moving)
7739 int move_pattern = element_info[element].move_pattern;
7742 Moving2Blocked(x, y, &newx, &newy);
7744 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7747 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7748 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7750 WasJustMoving[x][y] = 0;
7751 CheckCollision[x][y] = 0;
7753 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7755 if (Feld[x][y] != element) /* element has changed */
7759 if (!MovDelay[x][y]) /* start new movement phase */
7761 /* all objects that can change their move direction after each step
7762 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7764 if (element != EL_YAMYAM &&
7765 element != EL_DARK_YAMYAM &&
7766 element != EL_PACMAN &&
7767 !(move_pattern & MV_ANY_DIRECTION) &&
7768 move_pattern != MV_TURNING_LEFT &&
7769 move_pattern != MV_TURNING_RIGHT &&
7770 move_pattern != MV_TURNING_LEFT_RIGHT &&
7771 move_pattern != MV_TURNING_RIGHT_LEFT &&
7772 move_pattern != MV_TURNING_RANDOM)
7776 if (MovDelay[x][y] && (element == EL_BUG ||
7777 element == EL_SPACESHIP ||
7778 element == EL_SP_SNIKSNAK ||
7779 element == EL_SP_ELECTRON ||
7780 element == EL_MOLE))
7781 TEST_DrawLevelField(x, y);
7785 if (MovDelay[x][y]) /* wait some time before next movement */
7789 if (element == EL_ROBOT ||
7790 element == EL_YAMYAM ||
7791 element == EL_DARK_YAMYAM)
7793 DrawLevelElementAnimationIfNeeded(x, y, element);
7794 PlayLevelSoundAction(x, y, ACTION_WAITING);
7796 else if (element == EL_SP_ELECTRON)
7797 DrawLevelElementAnimationIfNeeded(x, y, element);
7798 else if (element == EL_DRAGON)
7801 int dir = MovDir[x][y];
7802 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7803 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7804 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7805 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7806 dir == MV_UP ? IMG_FLAMES_1_UP :
7807 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7808 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7810 GfxAction[x][y] = ACTION_ATTACKING;
7812 if (IS_PLAYER(x, y))
7813 DrawPlayerField(x, y);
7815 TEST_DrawLevelField(x, y);
7817 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7819 for (i = 1; i <= 3; i++)
7821 int xx = x + i * dx;
7822 int yy = y + i * dy;
7823 int sx = SCREENX(xx);
7824 int sy = SCREENY(yy);
7825 int flame_graphic = graphic + (i - 1);
7827 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7832 int flamed = MovingOrBlocked2Element(xx, yy);
7834 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7837 RemoveMovingField(xx, yy);
7839 ChangeDelay[xx][yy] = 0;
7841 Feld[xx][yy] = EL_FLAMES;
7843 if (IN_SCR_FIELD(sx, sy))
7845 TEST_DrawLevelFieldCrumbled(xx, yy);
7846 DrawGraphic(sx, sy, flame_graphic, frame);
7851 if (Feld[xx][yy] == EL_FLAMES)
7852 Feld[xx][yy] = EL_EMPTY;
7853 TEST_DrawLevelField(xx, yy);
7858 if (MovDelay[x][y]) /* element still has to wait some time */
7860 PlayLevelSoundAction(x, y, ACTION_WAITING);
7866 /* now make next step */
7868 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7870 if (DONT_COLLIDE_WITH(element) &&
7871 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7872 !PLAYER_ENEMY_PROTECTED(newx, newy))
7874 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7879 else if (CAN_MOVE_INTO_ACID(element) &&
7880 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7881 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7882 (MovDir[x][y] == MV_DOWN ||
7883 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7885 SplashAcid(newx, newy);
7886 Store[x][y] = EL_ACID;
7888 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7890 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7891 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7892 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7893 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7896 TEST_DrawLevelField(x, y);
7898 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7899 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7900 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7902 local_player->friends_still_needed--;
7903 if (!local_player->friends_still_needed &&
7904 !local_player->GameOver && AllPlayersGone)
7905 PlayerWins(local_player);
7909 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7911 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7912 TEST_DrawLevelField(newx, newy);
7914 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7916 else if (!IS_FREE(newx, newy))
7918 GfxAction[x][y] = ACTION_WAITING;
7920 if (IS_PLAYER(x, y))
7921 DrawPlayerField(x, y);
7923 TEST_DrawLevelField(x, y);
7928 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7930 if (IS_FOOD_PIG(Feld[newx][newy]))
7932 if (IS_MOVING(newx, newy))
7933 RemoveMovingField(newx, newy);
7936 Feld[newx][newy] = EL_EMPTY;
7937 TEST_DrawLevelField(newx, newy);
7940 PlayLevelSound(x, y, SND_PIG_DIGGING);
7942 else if (!IS_FREE(newx, newy))
7944 if (IS_PLAYER(x, y))
7945 DrawPlayerField(x, y);
7947 TEST_DrawLevelField(x, y);
7952 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7954 if (Store[x][y] != EL_EMPTY)
7956 boolean can_clone = FALSE;
7959 /* check if element to clone is still there */
7960 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7962 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7970 /* cannot clone or target field not free anymore -- do not clone */
7971 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7972 Store[x][y] = EL_EMPTY;
7975 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7977 if (IS_MV_DIAGONAL(MovDir[x][y]))
7979 int diagonal_move_dir = MovDir[x][y];
7980 int stored = Store[x][y];
7981 int change_delay = 8;
7984 /* android is moving diagonally */
7986 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7988 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7989 GfxElement[x][y] = EL_EMC_ANDROID;
7990 GfxAction[x][y] = ACTION_SHRINKING;
7991 GfxDir[x][y] = diagonal_move_dir;
7992 ChangeDelay[x][y] = change_delay;
7994 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7997 DrawLevelGraphicAnimation(x, y, graphic);
7998 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8000 if (Feld[newx][newy] == EL_ACID)
8002 SplashAcid(newx, newy);
8007 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8009 Store[newx][newy] = EL_EMC_ANDROID;
8010 GfxElement[newx][newy] = EL_EMC_ANDROID;
8011 GfxAction[newx][newy] = ACTION_GROWING;
8012 GfxDir[newx][newy] = diagonal_move_dir;
8013 ChangeDelay[newx][newy] = change_delay;
8015 graphic = el_act_dir2img(GfxElement[newx][newy],
8016 GfxAction[newx][newy], GfxDir[newx][newy]);
8018 DrawLevelGraphicAnimation(newx, newy, graphic);
8019 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8025 Feld[newx][newy] = EL_EMPTY;
8026 TEST_DrawLevelField(newx, newy);
8028 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8031 else if (!IS_FREE(newx, newy))
8036 else if (IS_CUSTOM_ELEMENT(element) &&
8037 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8039 if (!DigFieldByCE(newx, newy, element))
8042 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8044 RunnerVisit[x][y] = FrameCounter;
8045 PlayerVisit[x][y] /= 8; /* expire player visit path */
8048 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8050 if (!IS_FREE(newx, newy))
8052 if (IS_PLAYER(x, y))
8053 DrawPlayerField(x, y);
8055 TEST_DrawLevelField(x, y);
8061 boolean wanna_flame = !RND(10);
8062 int dx = newx - x, dy = newy - y;
8063 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8064 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8065 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8066 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8067 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8068 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8071 IS_CLASSIC_ENEMY(element1) ||
8072 IS_CLASSIC_ENEMY(element2)) &&
8073 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8074 element1 != EL_FLAMES && element2 != EL_FLAMES)
8076 ResetGfxAnimation(x, y);
8077 GfxAction[x][y] = ACTION_ATTACKING;
8079 if (IS_PLAYER(x, y))
8080 DrawPlayerField(x, y);
8082 TEST_DrawLevelField(x, y);
8084 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8086 MovDelay[x][y] = 50;
8088 Feld[newx][newy] = EL_FLAMES;
8089 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8090 Feld[newx1][newy1] = EL_FLAMES;
8091 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8092 Feld[newx2][newy2] = EL_FLAMES;
8098 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8099 Feld[newx][newy] == EL_DIAMOND)
8101 if (IS_MOVING(newx, newy))
8102 RemoveMovingField(newx, newy);
8105 Feld[newx][newy] = EL_EMPTY;
8106 TEST_DrawLevelField(newx, newy);
8109 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8111 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8112 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8114 if (AmoebaNr[newx][newy])
8116 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8117 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8118 Feld[newx][newy] == EL_BD_AMOEBA)
8119 AmoebaCnt[AmoebaNr[newx][newy]]--;
8122 if (IS_MOVING(newx, newy))
8124 RemoveMovingField(newx, newy);
8128 Feld[newx][newy] = EL_EMPTY;
8129 TEST_DrawLevelField(newx, newy);
8132 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8134 else if ((element == EL_PACMAN || element == EL_MOLE)
8135 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8137 if (AmoebaNr[newx][newy])
8139 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8140 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8141 Feld[newx][newy] == EL_BD_AMOEBA)
8142 AmoebaCnt[AmoebaNr[newx][newy]]--;
8145 if (element == EL_MOLE)
8147 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8148 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8150 ResetGfxAnimation(x, y);
8151 GfxAction[x][y] = ACTION_DIGGING;
8152 TEST_DrawLevelField(x, y);
8154 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8156 return; /* wait for shrinking amoeba */
8158 else /* element == EL_PACMAN */
8160 Feld[newx][newy] = EL_EMPTY;
8161 TEST_DrawLevelField(newx, newy);
8162 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8165 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8166 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8167 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8169 /* wait for shrinking amoeba to completely disappear */
8172 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8174 /* object was running against a wall */
8178 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8179 DrawLevelElementAnimation(x, y, element);
8181 if (DONT_TOUCH(element))
8182 TestIfBadThingTouchesPlayer(x, y);
8187 InitMovingField(x, y, MovDir[x][y]);
8189 PlayLevelSoundAction(x, y, ACTION_MOVING);
8193 ContinueMoving(x, y);
8196 void ContinueMoving(int x, int y)
8198 int element = Feld[x][y];
8199 struct ElementInfo *ei = &element_info[element];
8200 int direction = MovDir[x][y];
8201 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8202 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8203 int newx = x + dx, newy = y + dy;
8204 int stored = Store[x][y];
8205 int stored_new = Store[newx][newy];
8206 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8207 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8208 boolean last_line = (newy == lev_fieldy - 1);
8210 MovPos[x][y] += getElementMoveStepsize(x, y);
8212 if (pushed_by_player) /* special case: moving object pushed by player */
8213 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8215 if (ABS(MovPos[x][y]) < TILEX)
8217 TEST_DrawLevelField(x, y);
8219 return; /* element is still moving */
8222 /* element reached destination field */
8224 Feld[x][y] = EL_EMPTY;
8225 Feld[newx][newy] = element;
8226 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8228 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8230 element = Feld[newx][newy] = EL_ACID;
8232 else if (element == EL_MOLE)
8234 Feld[x][y] = EL_SAND;
8236 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8238 else if (element == EL_QUICKSAND_FILLING)
8240 element = Feld[newx][newy] = get_next_element(element);
8241 Store[newx][newy] = Store[x][y];
8243 else if (element == EL_QUICKSAND_EMPTYING)
8245 Feld[x][y] = get_next_element(element);
8246 element = Feld[newx][newy] = Store[x][y];
8248 else if (element == EL_QUICKSAND_FAST_FILLING)
8250 element = Feld[newx][newy] = get_next_element(element);
8251 Store[newx][newy] = Store[x][y];
8253 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8255 Feld[x][y] = get_next_element(element);
8256 element = Feld[newx][newy] = Store[x][y];
8258 else if (element == EL_MAGIC_WALL_FILLING)
8260 element = Feld[newx][newy] = get_next_element(element);
8261 if (!game.magic_wall_active)
8262 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8263 Store[newx][newy] = Store[x][y];
8265 else if (element == EL_MAGIC_WALL_EMPTYING)
8267 Feld[x][y] = get_next_element(element);
8268 if (!game.magic_wall_active)
8269 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8270 element = Feld[newx][newy] = Store[x][y];
8272 InitField(newx, newy, FALSE);
8274 else if (element == EL_BD_MAGIC_WALL_FILLING)
8276 element = Feld[newx][newy] = get_next_element(element);
8277 if (!game.magic_wall_active)
8278 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8279 Store[newx][newy] = Store[x][y];
8281 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8283 Feld[x][y] = get_next_element(element);
8284 if (!game.magic_wall_active)
8285 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8286 element = Feld[newx][newy] = Store[x][y];
8288 InitField(newx, newy, FALSE);
8290 else if (element == EL_DC_MAGIC_WALL_FILLING)
8292 element = Feld[newx][newy] = get_next_element(element);
8293 if (!game.magic_wall_active)
8294 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8295 Store[newx][newy] = Store[x][y];
8297 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8299 Feld[x][y] = get_next_element(element);
8300 if (!game.magic_wall_active)
8301 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8302 element = Feld[newx][newy] = Store[x][y];
8304 InitField(newx, newy, FALSE);
8306 else if (element == EL_AMOEBA_DROPPING)
8308 Feld[x][y] = get_next_element(element);
8309 element = Feld[newx][newy] = Store[x][y];
8311 else if (element == EL_SOKOBAN_OBJECT)
8314 Feld[x][y] = Back[x][y];
8316 if (Back[newx][newy])
8317 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8319 Back[x][y] = Back[newx][newy] = 0;
8322 Store[x][y] = EL_EMPTY;
8327 MovDelay[newx][newy] = 0;
8329 if (CAN_CHANGE_OR_HAS_ACTION(element))
8331 /* copy element change control values to new field */
8332 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8333 ChangePage[newx][newy] = ChangePage[x][y];
8334 ChangeCount[newx][newy] = ChangeCount[x][y];
8335 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8338 CustomValue[newx][newy] = CustomValue[x][y];
8340 ChangeDelay[x][y] = 0;
8341 ChangePage[x][y] = -1;
8342 ChangeCount[x][y] = 0;
8343 ChangeEvent[x][y] = -1;
8345 CustomValue[x][y] = 0;
8347 /* copy animation control values to new field */
8348 GfxFrame[newx][newy] = GfxFrame[x][y];
8349 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8350 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8351 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8353 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8355 /* some elements can leave other elements behind after moving */
8356 if (ei->move_leave_element != EL_EMPTY &&
8357 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8358 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8360 int move_leave_element = ei->move_leave_element;
8362 /* this makes it possible to leave the removed element again */
8363 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8364 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8366 Feld[x][y] = move_leave_element;
8368 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8369 MovDir[x][y] = direction;
8371 InitField(x, y, FALSE);
8373 if (GFX_CRUMBLED(Feld[x][y]))
8374 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8376 if (ELEM_IS_PLAYER(move_leave_element))
8377 RelocatePlayer(x, y, move_leave_element);
8380 /* do this after checking for left-behind element */
8381 ResetGfxAnimation(x, y); /* reset animation values for old field */
8383 if (!CAN_MOVE(element) ||
8384 (CAN_FALL(element) && direction == MV_DOWN &&
8385 (element == EL_SPRING ||
8386 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8387 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8388 GfxDir[x][y] = MovDir[newx][newy] = 0;
8390 TEST_DrawLevelField(x, y);
8391 TEST_DrawLevelField(newx, newy);
8393 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8395 /* prevent pushed element from moving on in pushed direction */
8396 if (pushed_by_player && CAN_MOVE(element) &&
8397 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8398 !(element_info[element].move_pattern & direction))
8399 TurnRound(newx, newy);
8401 /* prevent elements on conveyor belt from moving on in last direction */
8402 if (pushed_by_conveyor && CAN_FALL(element) &&
8403 direction & MV_HORIZONTAL)
8404 MovDir[newx][newy] = 0;
8406 if (!pushed_by_player)
8408 int nextx = newx + dx, nexty = newy + dy;
8409 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8411 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8413 if (CAN_FALL(element) && direction == MV_DOWN)
8414 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8416 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8417 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8419 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8420 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8423 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8425 TestIfBadThingTouchesPlayer(newx, newy);
8426 TestIfBadThingTouchesFriend(newx, newy);
8428 if (!IS_CUSTOM_ELEMENT(element))
8429 TestIfBadThingTouchesOtherBadThing(newx, newy);
8431 else if (element == EL_PENGUIN)
8432 TestIfFriendTouchesBadThing(newx, newy);
8434 if (DONT_GET_HIT_BY(element))
8436 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8439 /* give the player one last chance (one more frame) to move away */
8440 if (CAN_FALL(element) && direction == MV_DOWN &&
8441 (last_line || (!IS_FREE(x, newy + 1) &&
8442 (!IS_PLAYER(x, newy + 1) ||
8443 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8446 if (pushed_by_player && !game.use_change_when_pushing_bug)
8448 int push_side = MV_DIR_OPPOSITE(direction);
8449 struct PlayerInfo *player = PLAYERINFO(x, y);
8451 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8452 player->index_bit, push_side);
8453 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8454 player->index_bit, push_side);
8457 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8458 MovDelay[newx][newy] = 1;
8460 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8462 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8463 TestIfElementHitsCustomElement(newx, newy, direction);
8464 TestIfPlayerTouchesCustomElement(newx, newy);
8465 TestIfElementTouchesCustomElement(newx, newy);
8467 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8468 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8469 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8470 MV_DIR_OPPOSITE(direction));
8473 int AmoebeNachbarNr(int ax, int ay)
8476 int element = Feld[ax][ay];
8478 static int xy[4][2] =
8486 for (i = 0; i < NUM_DIRECTIONS; i++)
8488 int x = ax + xy[i][0];
8489 int y = ay + xy[i][1];
8491 if (!IN_LEV_FIELD(x, y))
8494 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8495 group_nr = AmoebaNr[x][y];
8501 void AmoebenVereinigen(int ax, int ay)
8503 int i, x, y, xx, yy;
8504 int new_group_nr = AmoebaNr[ax][ay];
8505 static int xy[4][2] =
8513 if (new_group_nr == 0)
8516 for (i = 0; i < NUM_DIRECTIONS; i++)
8521 if (!IN_LEV_FIELD(x, y))
8524 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8525 Feld[x][y] == EL_BD_AMOEBA ||
8526 Feld[x][y] == EL_AMOEBA_DEAD) &&
8527 AmoebaNr[x][y] != new_group_nr)
8529 int old_group_nr = AmoebaNr[x][y];
8531 if (old_group_nr == 0)
8534 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8535 AmoebaCnt[old_group_nr] = 0;
8536 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8537 AmoebaCnt2[old_group_nr] = 0;
8539 SCAN_PLAYFIELD(xx, yy)
8541 if (AmoebaNr[xx][yy] == old_group_nr)
8542 AmoebaNr[xx][yy] = new_group_nr;
8548 void AmoebeUmwandeln(int ax, int ay)
8552 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8554 int group_nr = AmoebaNr[ax][ay];
8559 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8560 printf("AmoebeUmwandeln(): This should never happen!\n");
8565 SCAN_PLAYFIELD(x, y)
8567 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8570 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8574 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8575 SND_AMOEBA_TURNING_TO_GEM :
8576 SND_AMOEBA_TURNING_TO_ROCK));
8581 static int xy[4][2] =
8589 for (i = 0; i < NUM_DIRECTIONS; i++)
8594 if (!IN_LEV_FIELD(x, y))
8597 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8599 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8600 SND_AMOEBA_TURNING_TO_GEM :
8601 SND_AMOEBA_TURNING_TO_ROCK));
8608 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8611 int group_nr = AmoebaNr[ax][ay];
8612 boolean done = FALSE;
8617 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8618 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8623 SCAN_PLAYFIELD(x, y)
8625 if (AmoebaNr[x][y] == group_nr &&
8626 (Feld[x][y] == EL_AMOEBA_DEAD ||
8627 Feld[x][y] == EL_BD_AMOEBA ||
8628 Feld[x][y] == EL_AMOEBA_GROWING))
8631 Feld[x][y] = new_element;
8632 InitField(x, y, FALSE);
8633 TEST_DrawLevelField(x, y);
8639 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8640 SND_BD_AMOEBA_TURNING_TO_ROCK :
8641 SND_BD_AMOEBA_TURNING_TO_GEM));
8644 void AmoebeWaechst(int x, int y)
8646 static unsigned int sound_delay = 0;
8647 static unsigned int sound_delay_value = 0;
8649 if (!MovDelay[x][y]) /* start new growing cycle */
8653 if (DelayReached(&sound_delay, sound_delay_value))
8655 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8656 sound_delay_value = 30;
8660 if (MovDelay[x][y]) /* wait some time before growing bigger */
8663 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8665 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8666 6 - MovDelay[x][y]);
8668 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8671 if (!MovDelay[x][y])
8673 Feld[x][y] = Store[x][y];
8675 TEST_DrawLevelField(x, y);
8680 void AmoebaDisappearing(int x, int y)
8682 static unsigned int sound_delay = 0;
8683 static unsigned int sound_delay_value = 0;
8685 if (!MovDelay[x][y]) /* start new shrinking cycle */
8689 if (DelayReached(&sound_delay, sound_delay_value))
8690 sound_delay_value = 30;
8693 if (MovDelay[x][y]) /* wait some time before shrinking */
8696 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8698 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8699 6 - MovDelay[x][y]);
8701 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8704 if (!MovDelay[x][y])
8706 Feld[x][y] = EL_EMPTY;
8707 TEST_DrawLevelField(x, y);
8709 /* don't let mole enter this field in this cycle;
8710 (give priority to objects falling to this field from above) */
8716 void AmoebeAbleger(int ax, int ay)
8719 int element = Feld[ax][ay];
8720 int graphic = el2img(element);
8721 int newax = ax, neway = ay;
8722 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8723 static int xy[4][2] =
8731 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8733 Feld[ax][ay] = EL_AMOEBA_DEAD;
8734 TEST_DrawLevelField(ax, ay);
8738 if (IS_ANIMATED(graphic))
8739 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8741 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8742 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8744 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8747 if (MovDelay[ax][ay])
8751 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8754 int x = ax + xy[start][0];
8755 int y = ay + xy[start][1];
8757 if (!IN_LEV_FIELD(x, y))
8760 if (IS_FREE(x, y) ||
8761 CAN_GROW_INTO(Feld[x][y]) ||
8762 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8763 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8769 if (newax == ax && neway == ay)
8772 else /* normal or "filled" (BD style) amoeba */
8775 boolean waiting_for_player = FALSE;
8777 for (i = 0; i < NUM_DIRECTIONS; i++)
8779 int j = (start + i) % 4;
8780 int x = ax + xy[j][0];
8781 int y = ay + xy[j][1];
8783 if (!IN_LEV_FIELD(x, y))
8786 if (IS_FREE(x, y) ||
8787 CAN_GROW_INTO(Feld[x][y]) ||
8788 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8789 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8795 else if (IS_PLAYER(x, y))
8796 waiting_for_player = TRUE;
8799 if (newax == ax && neway == ay) /* amoeba cannot grow */
8801 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8803 Feld[ax][ay] = EL_AMOEBA_DEAD;
8804 TEST_DrawLevelField(ax, ay);
8805 AmoebaCnt[AmoebaNr[ax][ay]]--;
8807 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8809 if (element == EL_AMOEBA_FULL)
8810 AmoebeUmwandeln(ax, ay);
8811 else if (element == EL_BD_AMOEBA)
8812 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8817 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8819 /* amoeba gets larger by growing in some direction */
8821 int new_group_nr = AmoebaNr[ax][ay];
8824 if (new_group_nr == 0)
8826 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8827 printf("AmoebeAbleger(): This should never happen!\n");
8832 AmoebaNr[newax][neway] = new_group_nr;
8833 AmoebaCnt[new_group_nr]++;
8834 AmoebaCnt2[new_group_nr]++;
8836 /* if amoeba touches other amoeba(s) after growing, unify them */
8837 AmoebenVereinigen(newax, neway);
8839 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8841 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8847 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8848 (neway == lev_fieldy - 1 && newax != ax))
8850 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8851 Store[newax][neway] = element;
8853 else if (neway == ay || element == EL_EMC_DRIPPER)
8855 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8857 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8861 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8862 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8863 Store[ax][ay] = EL_AMOEBA_DROP;
8864 ContinueMoving(ax, ay);
8868 TEST_DrawLevelField(newax, neway);
8871 void Life(int ax, int ay)
8875 int element = Feld[ax][ay];
8876 int graphic = el2img(element);
8877 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8879 boolean changed = FALSE;
8881 if (IS_ANIMATED(graphic))
8882 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8887 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8888 MovDelay[ax][ay] = life_time;
8890 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8893 if (MovDelay[ax][ay])
8897 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8899 int xx = ax+x1, yy = ay+y1;
8902 if (!IN_LEV_FIELD(xx, yy))
8905 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8907 int x = xx+x2, y = yy+y2;
8909 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8912 if (((Feld[x][y] == element ||
8913 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8915 (IS_FREE(x, y) && Stop[x][y]))
8919 if (xx == ax && yy == ay) /* field in the middle */
8921 if (nachbarn < life_parameter[0] ||
8922 nachbarn > life_parameter[1])
8924 Feld[xx][yy] = EL_EMPTY;
8926 TEST_DrawLevelField(xx, yy);
8927 Stop[xx][yy] = TRUE;
8931 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8932 { /* free border field */
8933 if (nachbarn >= life_parameter[2] &&
8934 nachbarn <= life_parameter[3])
8936 Feld[xx][yy] = element;
8937 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8939 TEST_DrawLevelField(xx, yy);
8940 Stop[xx][yy] = TRUE;
8947 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8948 SND_GAME_OF_LIFE_GROWING);
8951 static void InitRobotWheel(int x, int y)
8953 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8956 static void RunRobotWheel(int x, int y)
8958 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8961 static void StopRobotWheel(int x, int y)
8963 if (ZX == x && ZY == y)
8967 game.robot_wheel_active = FALSE;
8971 static void InitTimegateWheel(int x, int y)
8973 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8976 static void RunTimegateWheel(int x, int y)
8978 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8981 static void InitMagicBallDelay(int x, int y)
8983 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8986 static void ActivateMagicBall(int bx, int by)
8990 if (level.ball_random)
8992 int pos_border = RND(8); /* select one of the eight border elements */
8993 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8994 int xx = pos_content % 3;
8995 int yy = pos_content / 3;
9000 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9001 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9005 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9007 int xx = x - bx + 1;
9008 int yy = y - by + 1;
9010 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9011 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9015 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9018 void CheckExit(int x, int y)
9020 if (local_player->gems_still_needed > 0 ||
9021 local_player->sokobanfields_still_needed > 0 ||
9022 local_player->lights_still_needed > 0)
9024 int element = Feld[x][y];
9025 int graphic = el2img(element);
9027 if (IS_ANIMATED(graphic))
9028 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9033 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9036 Feld[x][y] = EL_EXIT_OPENING;
9038 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9041 void CheckExitEM(int x, int y)
9043 if (local_player->gems_still_needed > 0 ||
9044 local_player->sokobanfields_still_needed > 0 ||
9045 local_player->lights_still_needed > 0)
9047 int element = Feld[x][y];
9048 int graphic = el2img(element);
9050 if (IS_ANIMATED(graphic))
9051 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9056 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9059 Feld[x][y] = EL_EM_EXIT_OPENING;
9061 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9064 void CheckExitSteel(int x, int y)
9066 if (local_player->gems_still_needed > 0 ||
9067 local_player->sokobanfields_still_needed > 0 ||
9068 local_player->lights_still_needed > 0)
9070 int element = Feld[x][y];
9071 int graphic = el2img(element);
9073 if (IS_ANIMATED(graphic))
9074 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9079 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9082 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9084 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9087 void CheckExitSteelEM(int x, int y)
9089 if (local_player->gems_still_needed > 0 ||
9090 local_player->sokobanfields_still_needed > 0 ||
9091 local_player->lights_still_needed > 0)
9093 int element = Feld[x][y];
9094 int graphic = el2img(element);
9096 if (IS_ANIMATED(graphic))
9097 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9102 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9105 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9107 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9110 void CheckExitSP(int x, int y)
9112 if (local_player->gems_still_needed > 0)
9114 int element = Feld[x][y];
9115 int graphic = el2img(element);
9117 if (IS_ANIMATED(graphic))
9118 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9123 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9126 Feld[x][y] = EL_SP_EXIT_OPENING;
9128 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9131 static void CloseAllOpenTimegates()
9135 SCAN_PLAYFIELD(x, y)
9137 int element = Feld[x][y];
9139 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9141 Feld[x][y] = EL_TIMEGATE_CLOSING;
9143 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9148 void DrawTwinkleOnField(int x, int y)
9150 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9153 if (Feld[x][y] == EL_BD_DIAMOND)
9156 if (MovDelay[x][y] == 0) /* next animation frame */
9157 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9159 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9163 DrawLevelElementAnimation(x, y, Feld[x][y]);
9165 if (MovDelay[x][y] != 0)
9167 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9168 10 - MovDelay[x][y]);
9170 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9175 void MauerWaechst(int x, int y)
9179 if (!MovDelay[x][y]) /* next animation frame */
9180 MovDelay[x][y] = 3 * delay;
9182 if (MovDelay[x][y]) /* wait some time before next frame */
9186 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9188 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9189 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9191 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9194 if (!MovDelay[x][y])
9196 if (MovDir[x][y] == MV_LEFT)
9198 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9199 TEST_DrawLevelField(x - 1, y);
9201 else if (MovDir[x][y] == MV_RIGHT)
9203 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9204 TEST_DrawLevelField(x + 1, y);
9206 else if (MovDir[x][y] == MV_UP)
9208 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9209 TEST_DrawLevelField(x, y - 1);
9213 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9214 TEST_DrawLevelField(x, y + 1);
9217 Feld[x][y] = Store[x][y];
9219 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9220 TEST_DrawLevelField(x, y);
9225 void MauerAbleger(int ax, int ay)
9227 int element = Feld[ax][ay];
9228 int graphic = el2img(element);
9229 boolean oben_frei = FALSE, unten_frei = FALSE;
9230 boolean links_frei = FALSE, rechts_frei = FALSE;
9231 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9232 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9233 boolean new_wall = FALSE;
9235 if (IS_ANIMATED(graphic))
9236 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9238 if (!MovDelay[ax][ay]) /* start building new wall */
9239 MovDelay[ax][ay] = 6;
9241 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9244 if (MovDelay[ax][ay])
9248 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9250 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9252 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9254 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9257 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9258 element == EL_EXPANDABLE_WALL_ANY)
9262 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9263 Store[ax][ay-1] = element;
9264 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9265 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9266 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9267 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9272 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9273 Store[ax][ay+1] = element;
9274 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9275 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9276 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9277 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9282 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9283 element == EL_EXPANDABLE_WALL_ANY ||
9284 element == EL_EXPANDABLE_WALL ||
9285 element == EL_BD_EXPANDABLE_WALL)
9289 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9290 Store[ax-1][ay] = element;
9291 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9292 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9293 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9294 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9300 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9301 Store[ax+1][ay] = element;
9302 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9303 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9304 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9305 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9310 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9311 TEST_DrawLevelField(ax, ay);
9313 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9315 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9316 unten_massiv = TRUE;
9317 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9318 links_massiv = TRUE;
9319 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9320 rechts_massiv = TRUE;
9322 if (((oben_massiv && unten_massiv) ||
9323 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9324 element == EL_EXPANDABLE_WALL) &&
9325 ((links_massiv && rechts_massiv) ||
9326 element == EL_EXPANDABLE_WALL_VERTICAL))
9327 Feld[ax][ay] = EL_WALL;
9330 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9333 void MauerAblegerStahl(int ax, int ay)
9335 int element = Feld[ax][ay];
9336 int graphic = el2img(element);
9337 boolean oben_frei = FALSE, unten_frei = FALSE;
9338 boolean links_frei = FALSE, rechts_frei = FALSE;
9339 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9340 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9341 boolean new_wall = FALSE;
9343 if (IS_ANIMATED(graphic))
9344 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9346 if (!MovDelay[ax][ay]) /* start building new wall */
9347 MovDelay[ax][ay] = 6;
9349 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9352 if (MovDelay[ax][ay])
9356 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9358 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9360 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9362 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9365 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9366 element == EL_EXPANDABLE_STEELWALL_ANY)
9370 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9371 Store[ax][ay-1] = element;
9372 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9373 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9374 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9375 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9380 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9381 Store[ax][ay+1] = element;
9382 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9383 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9384 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9385 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9390 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9391 element == EL_EXPANDABLE_STEELWALL_ANY)
9395 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9396 Store[ax-1][ay] = element;
9397 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9398 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9399 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9400 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9406 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9407 Store[ax+1][ay] = element;
9408 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9409 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9410 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9411 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9416 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9418 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9419 unten_massiv = TRUE;
9420 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9421 links_massiv = TRUE;
9422 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9423 rechts_massiv = TRUE;
9425 if (((oben_massiv && unten_massiv) ||
9426 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9427 ((links_massiv && rechts_massiv) ||
9428 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9429 Feld[ax][ay] = EL_STEELWALL;
9432 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9435 void CheckForDragon(int x, int y)
9438 boolean dragon_found = FALSE;
9439 static int xy[4][2] =
9447 for (i = 0; i < NUM_DIRECTIONS; i++)
9449 for (j = 0; j < 4; j++)
9451 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9453 if (IN_LEV_FIELD(xx, yy) &&
9454 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9456 if (Feld[xx][yy] == EL_DRAGON)
9457 dragon_found = TRUE;
9466 for (i = 0; i < NUM_DIRECTIONS; i++)
9468 for (j = 0; j < 3; j++)
9470 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9472 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9474 Feld[xx][yy] = EL_EMPTY;
9475 TEST_DrawLevelField(xx, yy);
9484 static void InitBuggyBase(int x, int y)
9486 int element = Feld[x][y];
9487 int activating_delay = FRAMES_PER_SECOND / 4;
9490 (element == EL_SP_BUGGY_BASE ?
9491 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9492 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9494 element == EL_SP_BUGGY_BASE_ACTIVE ?
9495 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9498 static void WarnBuggyBase(int x, int y)
9501 static int xy[4][2] =
9509 for (i = 0; i < NUM_DIRECTIONS; i++)
9511 int xx = x + xy[i][0];
9512 int yy = y + xy[i][1];
9514 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9516 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9523 static void InitTrap(int x, int y)
9525 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9528 static void ActivateTrap(int x, int y)
9530 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9533 static void ChangeActiveTrap(int x, int y)
9535 int graphic = IMG_TRAP_ACTIVE;
9537 /* if new animation frame was drawn, correct crumbled sand border */
9538 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9539 TEST_DrawLevelFieldCrumbled(x, y);
9542 static int getSpecialActionElement(int element, int number, int base_element)
9544 return (element != EL_EMPTY ? element :
9545 number != -1 ? base_element + number - 1 :
9549 static int getModifiedActionNumber(int value_old, int operator, int operand,
9550 int value_min, int value_max)
9552 int value_new = (operator == CA_MODE_SET ? operand :
9553 operator == CA_MODE_ADD ? value_old + operand :
9554 operator == CA_MODE_SUBTRACT ? value_old - operand :
9555 operator == CA_MODE_MULTIPLY ? value_old * operand :
9556 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9557 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9560 return (value_new < value_min ? value_min :
9561 value_new > value_max ? value_max :
9565 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9567 struct ElementInfo *ei = &element_info[element];
9568 struct ElementChangeInfo *change = &ei->change_page[page];
9569 int target_element = change->target_element;
9570 int action_type = change->action_type;
9571 int action_mode = change->action_mode;
9572 int action_arg = change->action_arg;
9573 int action_element = change->action_element;
9576 if (!change->has_action)
9579 /* ---------- determine action paramater values -------------------------- */
9581 int level_time_value =
9582 (level.time > 0 ? TimeLeft :
9585 int action_arg_element_raw =
9586 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9587 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9588 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9589 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9590 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9591 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9592 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9594 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9596 int action_arg_direction =
9597 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9598 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9599 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9600 change->actual_trigger_side :
9601 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9602 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9605 int action_arg_number_min =
9606 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9609 int action_arg_number_max =
9610 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9611 action_type == CA_SET_LEVEL_GEMS ? 999 :
9612 action_type == CA_SET_LEVEL_TIME ? 9999 :
9613 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9614 action_type == CA_SET_CE_VALUE ? 9999 :
9615 action_type == CA_SET_CE_SCORE ? 9999 :
9618 int action_arg_number_reset =
9619 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9620 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9621 action_type == CA_SET_LEVEL_TIME ? level.time :
9622 action_type == CA_SET_LEVEL_SCORE ? 0 :
9623 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9624 action_type == CA_SET_CE_SCORE ? 0 :
9627 int action_arg_number =
9628 (action_arg <= CA_ARG_MAX ? action_arg :
9629 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9630 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9631 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9632 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9633 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9634 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9635 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9636 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9637 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9638 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9639 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9640 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9641 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9642 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9643 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9644 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9645 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9646 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9647 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9648 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9649 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9652 int action_arg_number_old =
9653 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9654 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9655 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9656 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9657 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9660 int action_arg_number_new =
9661 getModifiedActionNumber(action_arg_number_old,
9662 action_mode, action_arg_number,
9663 action_arg_number_min, action_arg_number_max);
9665 int trigger_player_bits =
9666 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9667 change->actual_trigger_player_bits : change->trigger_player);
9669 int action_arg_player_bits =
9670 (action_arg >= CA_ARG_PLAYER_1 &&
9671 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9672 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9673 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9676 /* ---------- execute action -------------------------------------------- */
9678 switch (action_type)
9685 /* ---------- level actions ------------------------------------------- */
9687 case CA_RESTART_LEVEL:
9689 game.restart_level = TRUE;
9694 case CA_SHOW_ENVELOPE:
9696 int element = getSpecialActionElement(action_arg_element,
9697 action_arg_number, EL_ENVELOPE_1);
9699 if (IS_ENVELOPE(element))
9700 local_player->show_envelope = element;
9705 case CA_SET_LEVEL_TIME:
9707 if (level.time > 0) /* only modify limited time value */
9709 TimeLeft = action_arg_number_new;
9711 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9713 DisplayGameControlValues();
9715 if (!TimeLeft && setup.time_limit)
9716 for (i = 0; i < MAX_PLAYERS; i++)
9717 KillPlayer(&stored_player[i]);
9723 case CA_SET_LEVEL_SCORE:
9725 local_player->score = action_arg_number_new;
9727 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9729 DisplayGameControlValues();
9734 case CA_SET_LEVEL_GEMS:
9736 local_player->gems_still_needed = action_arg_number_new;
9738 game.snapshot.collected_item = TRUE;
9740 game_panel_controls[GAME_PANEL_GEMS].value =
9741 local_player->gems_still_needed;
9743 DisplayGameControlValues();
9748 case CA_SET_LEVEL_WIND:
9750 game.wind_direction = action_arg_direction;
9755 case CA_SET_LEVEL_RANDOM_SEED:
9757 /* ensure that setting a new random seed while playing is predictable */
9758 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9763 /* ---------- player actions ------------------------------------------ */
9765 case CA_MOVE_PLAYER:
9767 /* automatically move to the next field in specified direction */
9768 for (i = 0; i < MAX_PLAYERS; i++)
9769 if (trigger_player_bits & (1 << i))
9770 stored_player[i].programmed_action = action_arg_direction;
9775 case CA_EXIT_PLAYER:
9777 for (i = 0; i < MAX_PLAYERS; i++)
9778 if (action_arg_player_bits & (1 << i))
9779 PlayerWins(&stored_player[i]);
9784 case CA_KILL_PLAYER:
9786 for (i = 0; i < MAX_PLAYERS; i++)
9787 if (action_arg_player_bits & (1 << i))
9788 KillPlayer(&stored_player[i]);
9793 case CA_SET_PLAYER_KEYS:
9795 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9796 int element = getSpecialActionElement(action_arg_element,
9797 action_arg_number, EL_KEY_1);
9799 if (IS_KEY(element))
9801 for (i = 0; i < MAX_PLAYERS; i++)
9803 if (trigger_player_bits & (1 << i))
9805 stored_player[i].key[KEY_NR(element)] = key_state;
9807 DrawGameDoorValues();
9815 case CA_SET_PLAYER_SPEED:
9817 for (i = 0; i < MAX_PLAYERS; i++)
9819 if (trigger_player_bits & (1 << i))
9821 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9823 if (action_arg == CA_ARG_SPEED_FASTER &&
9824 stored_player[i].cannot_move)
9826 action_arg_number = STEPSIZE_VERY_SLOW;
9828 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9829 action_arg == CA_ARG_SPEED_FASTER)
9831 action_arg_number = 2;
9832 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9835 else if (action_arg == CA_ARG_NUMBER_RESET)
9837 action_arg_number = level.initial_player_stepsize[i];
9841 getModifiedActionNumber(move_stepsize,
9844 action_arg_number_min,
9845 action_arg_number_max);
9847 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9854 case CA_SET_PLAYER_SHIELD:
9856 for (i = 0; i < MAX_PLAYERS; i++)
9858 if (trigger_player_bits & (1 << i))
9860 if (action_arg == CA_ARG_SHIELD_OFF)
9862 stored_player[i].shield_normal_time_left = 0;
9863 stored_player[i].shield_deadly_time_left = 0;
9865 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9867 stored_player[i].shield_normal_time_left = 999999;
9869 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9871 stored_player[i].shield_normal_time_left = 999999;
9872 stored_player[i].shield_deadly_time_left = 999999;
9880 case CA_SET_PLAYER_GRAVITY:
9882 for (i = 0; i < MAX_PLAYERS; i++)
9884 if (trigger_player_bits & (1 << i))
9886 stored_player[i].gravity =
9887 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9888 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9889 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9890 stored_player[i].gravity);
9897 case CA_SET_PLAYER_ARTWORK:
9899 for (i = 0; i < MAX_PLAYERS; i++)
9901 if (trigger_player_bits & (1 << i))
9903 int artwork_element = action_arg_element;
9905 if (action_arg == CA_ARG_ELEMENT_RESET)
9907 (level.use_artwork_element[i] ? level.artwork_element[i] :
9908 stored_player[i].element_nr);
9910 if (stored_player[i].artwork_element != artwork_element)
9911 stored_player[i].Frame = 0;
9913 stored_player[i].artwork_element = artwork_element;
9915 SetPlayerWaiting(&stored_player[i], FALSE);
9917 /* set number of special actions for bored and sleeping animation */
9918 stored_player[i].num_special_action_bored =
9919 get_num_special_action(artwork_element,
9920 ACTION_BORING_1, ACTION_BORING_LAST);
9921 stored_player[i].num_special_action_sleeping =
9922 get_num_special_action(artwork_element,
9923 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9930 case CA_SET_PLAYER_INVENTORY:
9932 for (i = 0; i < MAX_PLAYERS; i++)
9934 struct PlayerInfo *player = &stored_player[i];
9937 if (trigger_player_bits & (1 << i))
9939 int inventory_element = action_arg_element;
9941 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9942 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9943 action_arg == CA_ARG_ELEMENT_ACTION)
9945 int element = inventory_element;
9946 int collect_count = element_info[element].collect_count_initial;
9948 if (!IS_CUSTOM_ELEMENT(element))
9951 if (collect_count == 0)
9952 player->inventory_infinite_element = element;
9954 for (k = 0; k < collect_count; k++)
9955 if (player->inventory_size < MAX_INVENTORY_SIZE)
9956 player->inventory_element[player->inventory_size++] =
9959 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9960 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9961 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9963 if (player->inventory_infinite_element != EL_UNDEFINED &&
9964 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9965 action_arg_element_raw))
9966 player->inventory_infinite_element = EL_UNDEFINED;
9968 for (k = 0, j = 0; j < player->inventory_size; j++)
9970 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9971 action_arg_element_raw))
9972 player->inventory_element[k++] = player->inventory_element[j];
9975 player->inventory_size = k;
9977 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9979 if (player->inventory_size > 0)
9981 for (j = 0; j < player->inventory_size - 1; j++)
9982 player->inventory_element[j] = player->inventory_element[j + 1];
9984 player->inventory_size--;
9987 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9989 if (player->inventory_size > 0)
9990 player->inventory_size--;
9992 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9994 player->inventory_infinite_element = EL_UNDEFINED;
9995 player->inventory_size = 0;
9997 else if (action_arg == CA_ARG_INVENTORY_RESET)
9999 player->inventory_infinite_element = EL_UNDEFINED;
10000 player->inventory_size = 0;
10002 if (level.use_initial_inventory[i])
10004 for (j = 0; j < level.initial_inventory_size[i]; j++)
10006 int element = level.initial_inventory_content[i][j];
10007 int collect_count = element_info[element].collect_count_initial;
10009 if (!IS_CUSTOM_ELEMENT(element))
10012 if (collect_count == 0)
10013 player->inventory_infinite_element = element;
10015 for (k = 0; k < collect_count; k++)
10016 if (player->inventory_size < MAX_INVENTORY_SIZE)
10017 player->inventory_element[player->inventory_size++] =
10028 /* ---------- CE actions ---------------------------------------------- */
10030 case CA_SET_CE_VALUE:
10032 int last_ce_value = CustomValue[x][y];
10034 CustomValue[x][y] = action_arg_number_new;
10036 if (CustomValue[x][y] != last_ce_value)
10038 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10039 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10041 if (CustomValue[x][y] == 0)
10043 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10044 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10051 case CA_SET_CE_SCORE:
10053 int last_ce_score = ei->collect_score;
10055 ei->collect_score = action_arg_number_new;
10057 if (ei->collect_score != last_ce_score)
10059 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10060 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10062 if (ei->collect_score == 0)
10066 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10067 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10070 This is a very special case that seems to be a mixture between
10071 CheckElementChange() and CheckTriggeredElementChange(): while
10072 the first one only affects single elements that are triggered
10073 directly, the second one affects multiple elements in the playfield
10074 that are triggered indirectly by another element. This is a third
10075 case: Changing the CE score always affects multiple identical CEs,
10076 so every affected CE must be checked, not only the single CE for
10077 which the CE score was changed in the first place (as every instance
10078 of that CE shares the same CE score, and therefore also can change)!
10080 SCAN_PLAYFIELD(xx, yy)
10082 if (Feld[xx][yy] == element)
10083 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10084 CE_SCORE_GETS_ZERO);
10092 case CA_SET_CE_ARTWORK:
10094 int artwork_element = action_arg_element;
10095 boolean reset_frame = FALSE;
10098 if (action_arg == CA_ARG_ELEMENT_RESET)
10099 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10102 if (ei->gfx_element != artwork_element)
10103 reset_frame = TRUE;
10105 ei->gfx_element = artwork_element;
10107 SCAN_PLAYFIELD(xx, yy)
10109 if (Feld[xx][yy] == element)
10113 ResetGfxAnimation(xx, yy);
10114 ResetRandomAnimationValue(xx, yy);
10117 TEST_DrawLevelField(xx, yy);
10124 /* ---------- engine actions ------------------------------------------ */
10126 case CA_SET_ENGINE_SCAN_MODE:
10128 InitPlayfieldScanMode(action_arg);
10138 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10140 int old_element = Feld[x][y];
10141 int new_element = GetElementFromGroupElement(element);
10142 int previous_move_direction = MovDir[x][y];
10143 int last_ce_value = CustomValue[x][y];
10144 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10145 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10146 boolean add_player_onto_element = (new_element_is_player &&
10147 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10148 IS_WALKABLE(old_element));
10150 if (!add_player_onto_element)
10152 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10153 RemoveMovingField(x, y);
10157 Feld[x][y] = new_element;
10159 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10160 MovDir[x][y] = previous_move_direction;
10162 if (element_info[new_element].use_last_ce_value)
10163 CustomValue[x][y] = last_ce_value;
10165 InitField_WithBug1(x, y, FALSE);
10167 new_element = Feld[x][y]; /* element may have changed */
10169 ResetGfxAnimation(x, y);
10170 ResetRandomAnimationValue(x, y);
10172 TEST_DrawLevelField(x, y);
10174 if (GFX_CRUMBLED(new_element))
10175 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10178 /* check if element under the player changes from accessible to unaccessible
10179 (needed for special case of dropping element which then changes) */
10180 /* (must be checked after creating new element for walkable group elements) */
10181 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10182 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10189 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10190 if (new_element_is_player)
10191 RelocatePlayer(x, y, new_element);
10194 ChangeCount[x][y]++; /* count number of changes in the same frame */
10196 TestIfBadThingTouchesPlayer(x, y);
10197 TestIfPlayerTouchesCustomElement(x, y);
10198 TestIfElementTouchesCustomElement(x, y);
10201 static void CreateField(int x, int y, int element)
10203 CreateFieldExt(x, y, element, FALSE);
10206 static void CreateElementFromChange(int x, int y, int element)
10208 element = GET_VALID_RUNTIME_ELEMENT(element);
10210 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10212 int old_element = Feld[x][y];
10214 /* prevent changed element from moving in same engine frame
10215 unless both old and new element can either fall or move */
10216 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10217 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10221 CreateFieldExt(x, y, element, TRUE);
10224 static boolean ChangeElement(int x, int y, int element, int page)
10226 struct ElementInfo *ei = &element_info[element];
10227 struct ElementChangeInfo *change = &ei->change_page[page];
10228 int ce_value = CustomValue[x][y];
10229 int ce_score = ei->collect_score;
10230 int target_element;
10231 int old_element = Feld[x][y];
10233 /* always use default change event to prevent running into a loop */
10234 if (ChangeEvent[x][y] == -1)
10235 ChangeEvent[x][y] = CE_DELAY;
10237 if (ChangeEvent[x][y] == CE_DELAY)
10239 /* reset actual trigger element, trigger player and action element */
10240 change->actual_trigger_element = EL_EMPTY;
10241 change->actual_trigger_player = EL_EMPTY;
10242 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10243 change->actual_trigger_side = CH_SIDE_NONE;
10244 change->actual_trigger_ce_value = 0;
10245 change->actual_trigger_ce_score = 0;
10248 /* do not change elements more than a specified maximum number of changes */
10249 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10252 ChangeCount[x][y]++; /* count number of changes in the same frame */
10254 if (change->explode)
10261 if (change->use_target_content)
10263 boolean complete_replace = TRUE;
10264 boolean can_replace[3][3];
10267 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10270 boolean is_walkable;
10271 boolean is_diggable;
10272 boolean is_collectible;
10273 boolean is_removable;
10274 boolean is_destructible;
10275 int ex = x + xx - 1;
10276 int ey = y + yy - 1;
10277 int content_element = change->target_content.e[xx][yy];
10280 can_replace[xx][yy] = TRUE;
10282 if (ex == x && ey == y) /* do not check changing element itself */
10285 if (content_element == EL_EMPTY_SPACE)
10287 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10292 if (!IN_LEV_FIELD(ex, ey))
10294 can_replace[xx][yy] = FALSE;
10295 complete_replace = FALSE;
10302 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10303 e = MovingOrBlocked2Element(ex, ey);
10305 is_empty = (IS_FREE(ex, ey) ||
10306 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10308 is_walkable = (is_empty || IS_WALKABLE(e));
10309 is_diggable = (is_empty || IS_DIGGABLE(e));
10310 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10311 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10312 is_removable = (is_diggable || is_collectible);
10314 can_replace[xx][yy] =
10315 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10316 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10317 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10318 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10319 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10320 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10321 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10323 if (!can_replace[xx][yy])
10324 complete_replace = FALSE;
10327 if (!change->only_if_complete || complete_replace)
10329 boolean something_has_changed = FALSE;
10331 if (change->only_if_complete && change->use_random_replace &&
10332 RND(100) < change->random_percentage)
10335 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10337 int ex = x + xx - 1;
10338 int ey = y + yy - 1;
10339 int content_element;
10341 if (can_replace[xx][yy] && (!change->use_random_replace ||
10342 RND(100) < change->random_percentage))
10344 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10345 RemoveMovingField(ex, ey);
10347 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10349 content_element = change->target_content.e[xx][yy];
10350 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10351 ce_value, ce_score);
10353 CreateElementFromChange(ex, ey, target_element);
10355 something_has_changed = TRUE;
10357 /* for symmetry reasons, freeze newly created border elements */
10358 if (ex != x || ey != y)
10359 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10363 if (something_has_changed)
10365 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10366 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10372 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10373 ce_value, ce_score);
10375 if (element == EL_DIAGONAL_GROWING ||
10376 element == EL_DIAGONAL_SHRINKING)
10378 target_element = Store[x][y];
10380 Store[x][y] = EL_EMPTY;
10383 CreateElementFromChange(x, y, target_element);
10385 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10386 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10389 /* this uses direct change before indirect change */
10390 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10395 static void HandleElementChange(int x, int y, int page)
10397 int element = MovingOrBlocked2Element(x, y);
10398 struct ElementInfo *ei = &element_info[element];
10399 struct ElementChangeInfo *change = &ei->change_page[page];
10400 boolean handle_action_before_change = FALSE;
10403 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10404 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10407 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10408 x, y, element, element_info[element].token_name);
10409 printf("HandleElementChange(): This should never happen!\n");
10414 /* this can happen with classic bombs on walkable, changing elements */
10415 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10420 if (ChangeDelay[x][y] == 0) /* initialize element change */
10422 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10424 if (change->can_change)
10426 /* !!! not clear why graphic animation should be reset at all here !!! */
10427 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10428 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10431 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10433 When using an animation frame delay of 1 (this only happens with
10434 "sp_zonk.moving.left/right" in the classic graphics), the default
10435 (non-moving) animation shows wrong animation frames (while the
10436 moving animation, like "sp_zonk.moving.left/right", is correct,
10437 so this graphical bug never shows up with the classic graphics).
10438 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10439 be drawn instead of the correct frames 0,1,2,3. This is caused by
10440 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10441 an element change: First when the change delay ("ChangeDelay[][]")
10442 counter has reached zero after decrementing, then a second time in
10443 the next frame (after "GfxFrame[][]" was already incremented) when
10444 "ChangeDelay[][]" is reset to the initial delay value again.
10446 This causes frame 0 to be drawn twice, while the last frame won't
10447 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10449 As some animations may already be cleverly designed around this bug
10450 (at least the "Snake Bite" snake tail animation does this), it cannot
10451 simply be fixed here without breaking such existing animations.
10452 Unfortunately, it cannot easily be detected if a graphics set was
10453 designed "before" or "after" the bug was fixed. As a workaround,
10454 a new graphics set option "game.graphics_engine_version" was added
10455 to be able to specify the game's major release version for which the
10456 graphics set was designed, which can then be used to decide if the
10457 bugfix should be used (version 4 and above) or not (version 3 or
10458 below, or if no version was specified at all, as with old sets).
10460 (The wrong/fixed animation frames can be tested with the test level set
10461 "test_gfxframe" and level "000", which contains a specially prepared
10462 custom element at level position (x/y) == (11/9) which uses the zonk
10463 animation mentioned above. Using "game.graphics_engine_version: 4"
10464 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10465 This can also be seen from the debug output for this test element.)
10468 /* when a custom element is about to change (for example by change delay),
10469 do not reset graphic animation when the custom element is moving */
10470 if (game.graphics_engine_version < 4 &&
10473 ResetGfxAnimation(x, y);
10474 ResetRandomAnimationValue(x, y);
10477 if (change->pre_change_function)
10478 change->pre_change_function(x, y);
10482 ChangeDelay[x][y]--;
10484 if (ChangeDelay[x][y] != 0) /* continue element change */
10486 if (change->can_change)
10488 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10490 if (IS_ANIMATED(graphic))
10491 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10493 if (change->change_function)
10494 change->change_function(x, y);
10497 else /* finish element change */
10499 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10501 page = ChangePage[x][y];
10502 ChangePage[x][y] = -1;
10504 change = &ei->change_page[page];
10507 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10509 ChangeDelay[x][y] = 1; /* try change after next move step */
10510 ChangePage[x][y] = page; /* remember page to use for change */
10515 /* special case: set new level random seed before changing element */
10516 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10517 handle_action_before_change = TRUE;
10519 if (change->has_action && handle_action_before_change)
10520 ExecuteCustomElementAction(x, y, element, page);
10522 if (change->can_change)
10524 if (ChangeElement(x, y, element, page))
10526 if (change->post_change_function)
10527 change->post_change_function(x, y);
10531 if (change->has_action && !handle_action_before_change)
10532 ExecuteCustomElementAction(x, y, element, page);
10536 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10537 int trigger_element,
10539 int trigger_player,
10543 boolean change_done_any = FALSE;
10544 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10547 if (!(trigger_events[trigger_element][trigger_event]))
10550 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10552 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10554 int element = EL_CUSTOM_START + i;
10555 boolean change_done = FALSE;
10558 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10559 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10562 for (p = 0; p < element_info[element].num_change_pages; p++)
10564 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10566 if (change->can_change_or_has_action &&
10567 change->has_event[trigger_event] &&
10568 change->trigger_side & trigger_side &&
10569 change->trigger_player & trigger_player &&
10570 change->trigger_page & trigger_page_bits &&
10571 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10573 change->actual_trigger_element = trigger_element;
10574 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10575 change->actual_trigger_player_bits = trigger_player;
10576 change->actual_trigger_side = trigger_side;
10577 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10578 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10580 if ((change->can_change && !change_done) || change->has_action)
10584 SCAN_PLAYFIELD(x, y)
10586 if (Feld[x][y] == element)
10588 if (change->can_change && !change_done)
10590 /* if element already changed in this frame, not only prevent
10591 another element change (checked in ChangeElement()), but
10592 also prevent additional element actions for this element */
10594 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10595 !level.use_action_after_change_bug)
10598 ChangeDelay[x][y] = 1;
10599 ChangeEvent[x][y] = trigger_event;
10601 HandleElementChange(x, y, p);
10603 else if (change->has_action)
10605 /* if element already changed in this frame, not only prevent
10606 another element change (checked in ChangeElement()), but
10607 also prevent additional element actions for this element */
10609 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10610 !level.use_action_after_change_bug)
10613 ExecuteCustomElementAction(x, y, element, p);
10614 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10619 if (change->can_change)
10621 change_done = TRUE;
10622 change_done_any = TRUE;
10629 RECURSION_LOOP_DETECTION_END();
10631 return change_done_any;
10634 static boolean CheckElementChangeExt(int x, int y,
10636 int trigger_element,
10638 int trigger_player,
10641 boolean change_done = FALSE;
10644 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10645 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10648 if (Feld[x][y] == EL_BLOCKED)
10650 Blocked2Moving(x, y, &x, &y);
10651 element = Feld[x][y];
10654 /* check if element has already changed or is about to change after moving */
10655 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10656 Feld[x][y] != element) ||
10658 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10659 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10660 ChangePage[x][y] != -1)))
10663 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10665 for (p = 0; p < element_info[element].num_change_pages; p++)
10667 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10669 /* check trigger element for all events where the element that is checked
10670 for changing interacts with a directly adjacent element -- this is
10671 different to element changes that affect other elements to change on the
10672 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10673 boolean check_trigger_element =
10674 (trigger_event == CE_TOUCHING_X ||
10675 trigger_event == CE_HITTING_X ||
10676 trigger_event == CE_HIT_BY_X ||
10677 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10679 if (change->can_change_or_has_action &&
10680 change->has_event[trigger_event] &&
10681 change->trigger_side & trigger_side &&
10682 change->trigger_player & trigger_player &&
10683 (!check_trigger_element ||
10684 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10686 change->actual_trigger_element = trigger_element;
10687 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10688 change->actual_trigger_player_bits = trigger_player;
10689 change->actual_trigger_side = trigger_side;
10690 change->actual_trigger_ce_value = CustomValue[x][y];
10691 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10693 /* special case: trigger element not at (x,y) position for some events */
10694 if (check_trigger_element)
10706 { 0, 0 }, { 0, 0 }, { 0, 0 },
10710 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10711 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10713 change->actual_trigger_ce_value = CustomValue[xx][yy];
10714 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10717 if (change->can_change && !change_done)
10719 ChangeDelay[x][y] = 1;
10720 ChangeEvent[x][y] = trigger_event;
10722 HandleElementChange(x, y, p);
10724 change_done = TRUE;
10726 else if (change->has_action)
10728 ExecuteCustomElementAction(x, y, element, p);
10729 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10734 RECURSION_LOOP_DETECTION_END();
10736 return change_done;
10739 static void PlayPlayerSound(struct PlayerInfo *player)
10741 int jx = player->jx, jy = player->jy;
10742 int sound_element = player->artwork_element;
10743 int last_action = player->last_action_waiting;
10744 int action = player->action_waiting;
10746 if (player->is_waiting)
10748 if (action != last_action)
10749 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10751 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10755 if (action != last_action)
10756 StopSound(element_info[sound_element].sound[last_action]);
10758 if (last_action == ACTION_SLEEPING)
10759 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10763 static void PlayAllPlayersSound()
10767 for (i = 0; i < MAX_PLAYERS; i++)
10768 if (stored_player[i].active)
10769 PlayPlayerSound(&stored_player[i]);
10772 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10774 boolean last_waiting = player->is_waiting;
10775 int move_dir = player->MovDir;
10777 player->dir_waiting = move_dir;
10778 player->last_action_waiting = player->action_waiting;
10782 if (!last_waiting) /* not waiting -> waiting */
10784 player->is_waiting = TRUE;
10786 player->frame_counter_bored =
10788 game.player_boring_delay_fixed +
10789 GetSimpleRandom(game.player_boring_delay_random);
10790 player->frame_counter_sleeping =
10792 game.player_sleeping_delay_fixed +
10793 GetSimpleRandom(game.player_sleeping_delay_random);
10795 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10798 if (game.player_sleeping_delay_fixed +
10799 game.player_sleeping_delay_random > 0 &&
10800 player->anim_delay_counter == 0 &&
10801 player->post_delay_counter == 0 &&
10802 FrameCounter >= player->frame_counter_sleeping)
10803 player->is_sleeping = TRUE;
10804 else if (game.player_boring_delay_fixed +
10805 game.player_boring_delay_random > 0 &&
10806 FrameCounter >= player->frame_counter_bored)
10807 player->is_bored = TRUE;
10809 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10810 player->is_bored ? ACTION_BORING :
10813 if (player->is_sleeping && player->use_murphy)
10815 /* special case for sleeping Murphy when leaning against non-free tile */
10817 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10818 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10819 !IS_MOVING(player->jx - 1, player->jy)))
10820 move_dir = MV_LEFT;
10821 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10822 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10823 !IS_MOVING(player->jx + 1, player->jy)))
10824 move_dir = MV_RIGHT;
10826 player->is_sleeping = FALSE;
10828 player->dir_waiting = move_dir;
10831 if (player->is_sleeping)
10833 if (player->num_special_action_sleeping > 0)
10835 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10837 int last_special_action = player->special_action_sleeping;
10838 int num_special_action = player->num_special_action_sleeping;
10839 int special_action =
10840 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10841 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10842 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10843 last_special_action + 1 : ACTION_SLEEPING);
10844 int special_graphic =
10845 el_act_dir2img(player->artwork_element, special_action, move_dir);
10847 player->anim_delay_counter =
10848 graphic_info[special_graphic].anim_delay_fixed +
10849 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10850 player->post_delay_counter =
10851 graphic_info[special_graphic].post_delay_fixed +
10852 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10854 player->special_action_sleeping = special_action;
10857 if (player->anim_delay_counter > 0)
10859 player->action_waiting = player->special_action_sleeping;
10860 player->anim_delay_counter--;
10862 else if (player->post_delay_counter > 0)
10864 player->post_delay_counter--;
10868 else if (player->is_bored)
10870 if (player->num_special_action_bored > 0)
10872 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10874 int special_action =
10875 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10876 int special_graphic =
10877 el_act_dir2img(player->artwork_element, special_action, move_dir);
10879 player->anim_delay_counter =
10880 graphic_info[special_graphic].anim_delay_fixed +
10881 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10882 player->post_delay_counter =
10883 graphic_info[special_graphic].post_delay_fixed +
10884 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10886 player->special_action_bored = special_action;
10889 if (player->anim_delay_counter > 0)
10891 player->action_waiting = player->special_action_bored;
10892 player->anim_delay_counter--;
10894 else if (player->post_delay_counter > 0)
10896 player->post_delay_counter--;
10901 else if (last_waiting) /* waiting -> not waiting */
10903 player->is_waiting = FALSE;
10904 player->is_bored = FALSE;
10905 player->is_sleeping = FALSE;
10907 player->frame_counter_bored = -1;
10908 player->frame_counter_sleeping = -1;
10910 player->anim_delay_counter = 0;
10911 player->post_delay_counter = 0;
10913 player->dir_waiting = player->MovDir;
10914 player->action_waiting = ACTION_DEFAULT;
10916 player->special_action_bored = ACTION_DEFAULT;
10917 player->special_action_sleeping = ACTION_DEFAULT;
10921 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10923 if ((!player->is_moving && player->was_moving) ||
10924 (player->MovPos == 0 && player->was_moving) ||
10925 (player->is_snapping && !player->was_snapping) ||
10926 (player->is_dropping && !player->was_dropping))
10928 if (!CheckSaveEngineSnapshotToList())
10931 player->was_moving = FALSE;
10932 player->was_snapping = TRUE;
10933 player->was_dropping = TRUE;
10937 if (player->is_moving)
10938 player->was_moving = TRUE;
10940 if (!player->is_snapping)
10941 player->was_snapping = FALSE;
10943 if (!player->is_dropping)
10944 player->was_dropping = FALSE;
10948 static void CheckSingleStepMode(struct PlayerInfo *player)
10950 if (tape.single_step && tape.recording && !tape.pausing)
10952 /* as it is called "single step mode", just return to pause mode when the
10953 player stopped moving after one tile (or never starts moving at all) */
10954 if (!player->is_moving &&
10955 !player->is_pushing &&
10956 !player->is_dropping_pressed)
10958 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10959 SnapField(player, 0, 0); /* stop snapping */
10963 CheckSaveEngineSnapshot(player);
10966 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10968 int left = player_action & JOY_LEFT;
10969 int right = player_action & JOY_RIGHT;
10970 int up = player_action & JOY_UP;
10971 int down = player_action & JOY_DOWN;
10972 int button1 = player_action & JOY_BUTTON_1;
10973 int button2 = player_action & JOY_BUTTON_2;
10974 int dx = (left ? -1 : right ? 1 : 0);
10975 int dy = (up ? -1 : down ? 1 : 0);
10977 if (!player->active || tape.pausing)
10983 SnapField(player, dx, dy);
10987 DropElement(player);
10989 MovePlayer(player, dx, dy);
10992 CheckSingleStepMode(player);
10994 SetPlayerWaiting(player, FALSE);
10996 return player_action;
11000 /* no actions for this player (no input at player's configured device) */
11002 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11003 SnapField(player, 0, 0);
11004 CheckGravityMovementWhenNotMoving(player);
11006 if (player->MovPos == 0)
11007 SetPlayerWaiting(player, TRUE);
11009 if (player->MovPos == 0) /* needed for tape.playing */
11010 player->is_moving = FALSE;
11012 player->is_dropping = FALSE;
11013 player->is_dropping_pressed = FALSE;
11014 player->drop_pressed_delay = 0;
11016 CheckSingleStepMode(player);
11022 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11025 if (!tape.use_mouse)
11028 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11029 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11030 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11033 static void SetTapeActionFromMouseAction(byte *tape_action,
11034 struct MouseActionInfo *mouse_action)
11036 if (!tape.use_mouse)
11039 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11040 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11041 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11044 static void CheckLevelTime()
11048 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11049 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11051 if (level.native_em_level->lev->home == 0) /* all players at home */
11053 PlayerWins(local_player);
11055 AllPlayersGone = TRUE;
11057 level.native_em_level->lev->home = -1;
11060 if (level.native_em_level->ply[0]->alive == 0 &&
11061 level.native_em_level->ply[1]->alive == 0 &&
11062 level.native_em_level->ply[2]->alive == 0 &&
11063 level.native_em_level->ply[3]->alive == 0) /* all dead */
11064 AllPlayersGone = TRUE;
11066 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11068 if (game_sp.LevelSolved &&
11069 !game_sp.GameOver) /* game won */
11071 PlayerWins(local_player);
11073 game_sp.GameOver = TRUE;
11075 AllPlayersGone = TRUE;
11078 if (game_sp.GameOver) /* game lost */
11079 AllPlayersGone = TRUE;
11081 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11083 if (game_mm.level_solved &&
11084 !game_mm.game_over) /* game won */
11086 PlayerWins(local_player);
11088 game_mm.game_over = TRUE;
11090 AllPlayersGone = TRUE;
11093 if (game_mm.game_over) /* game lost */
11094 AllPlayersGone = TRUE;
11097 if (TimeFrames >= FRAMES_PER_SECOND)
11102 for (i = 0; i < MAX_PLAYERS; i++)
11104 struct PlayerInfo *player = &stored_player[i];
11106 if (SHIELD_ON(player))
11108 player->shield_normal_time_left--;
11110 if (player->shield_deadly_time_left > 0)
11111 player->shield_deadly_time_left--;
11115 if (!local_player->LevelSolved && !level.use_step_counter)
11123 if (TimeLeft <= 10 && setup.time_limit)
11124 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11126 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11127 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11129 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11131 if (!TimeLeft && setup.time_limit)
11133 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11134 level.native_em_level->lev->killed_out_of_time = TRUE;
11136 for (i = 0; i < MAX_PLAYERS; i++)
11137 KillPlayer(&stored_player[i]);
11140 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11142 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11145 level.native_em_level->lev->time =
11146 (game.no_time_limit ? TimePlayed : TimeLeft);
11149 if (tape.recording || tape.playing)
11150 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11153 if (tape.recording || tape.playing)
11154 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11156 UpdateAndDisplayGameControlValues();
11159 void AdvanceFrameAndPlayerCounters(int player_nr)
11163 /* advance frame counters (global frame counter and time frame counter) */
11167 /* advance player counters (counters for move delay, move animation etc.) */
11168 for (i = 0; i < MAX_PLAYERS; i++)
11170 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11171 int move_delay_value = stored_player[i].move_delay_value;
11172 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11174 if (!advance_player_counters) /* not all players may be affected */
11177 if (move_frames == 0) /* less than one move per game frame */
11179 int stepsize = TILEX / move_delay_value;
11180 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11181 int count = (stored_player[i].is_moving ?
11182 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11184 if (count % delay == 0)
11188 stored_player[i].Frame += move_frames;
11190 if (stored_player[i].MovPos != 0)
11191 stored_player[i].StepFrame += move_frames;
11193 if (stored_player[i].move_delay > 0)
11194 stored_player[i].move_delay--;
11196 /* due to bugs in previous versions, counter must count up, not down */
11197 if (stored_player[i].push_delay != -1)
11198 stored_player[i].push_delay++;
11200 if (stored_player[i].drop_delay > 0)
11201 stored_player[i].drop_delay--;
11203 if (stored_player[i].is_dropping_pressed)
11204 stored_player[i].drop_pressed_delay++;
11208 void StartGameActions(boolean init_network_game, boolean record_tape,
11211 unsigned int new_random_seed = InitRND(random_seed);
11214 TapeStartRecording(new_random_seed);
11216 if (init_network_game)
11218 SendToServer_StartPlaying();
11226 void GameActionsExt()
11229 static unsigned int game_frame_delay = 0;
11231 unsigned int game_frame_delay_value;
11232 byte *recorded_player_action;
11233 byte summarized_player_action = 0;
11234 byte tape_action[MAX_PLAYERS];
11237 /* detect endless loops, caused by custom element programming */
11238 if (recursion_loop_detected && recursion_loop_depth == 0)
11240 char *message = getStringCat3("Internal Error! Element ",
11241 EL_NAME(recursion_loop_element),
11242 " caused endless loop! Quit the game?");
11244 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11245 EL_NAME(recursion_loop_element));
11247 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11249 recursion_loop_detected = FALSE; /* if game should be continued */
11256 if (game.restart_level)
11257 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11259 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11260 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11262 if (level.native_em_level->lev->home == 0) /* all players at home */
11264 PlayerWins(local_player);
11266 AllPlayersGone = TRUE;
11268 level.native_em_level->lev->home = -1;
11271 if (level.native_em_level->ply[0]->alive == 0 &&
11272 level.native_em_level->ply[1]->alive == 0 &&
11273 level.native_em_level->ply[2]->alive == 0 &&
11274 level.native_em_level->ply[3]->alive == 0) /* all dead */
11275 AllPlayersGone = TRUE;
11277 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11279 if (game_sp.LevelSolved &&
11280 !game_sp.GameOver) /* game won */
11282 PlayerWins(local_player);
11284 game_sp.GameOver = TRUE;
11286 AllPlayersGone = TRUE;
11289 if (game_sp.GameOver) /* game lost */
11290 AllPlayersGone = TRUE;
11292 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11294 if (game_mm.level_solved &&
11295 !game_mm.game_over) /* game won */
11297 PlayerWins(local_player);
11299 game_mm.game_over = TRUE;
11301 AllPlayersGone = TRUE;
11304 if (game_mm.game_over) /* game lost */
11305 AllPlayersGone = TRUE;
11308 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11311 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11314 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11317 game_frame_delay_value =
11318 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11320 if (tape.playing && tape.warp_forward && !tape.pausing)
11321 game_frame_delay_value = 0;
11323 SetVideoFrameDelay(game_frame_delay_value);
11327 /* ---------- main game synchronization point ---------- */
11329 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11331 printf("::: skip == %d\n", skip);
11334 /* ---------- main game synchronization point ---------- */
11336 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11340 if (network_playing && !network_player_action_received)
11342 /* try to get network player actions in time */
11344 /* last chance to get network player actions without main loop delay */
11345 HandleNetworking();
11347 /* game was quit by network peer */
11348 if (game_status != GAME_MODE_PLAYING)
11351 if (!network_player_action_received)
11352 return; /* failed to get network player actions in time */
11354 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11360 /* at this point we know that we really continue executing the game */
11362 network_player_action_received = FALSE;
11364 /* when playing tape, read previously recorded player input from tape data */
11365 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11367 local_player->effective_mouse_action = local_player->mouse_action;
11369 if (recorded_player_action != NULL)
11370 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11371 recorded_player_action);
11373 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11377 if (tape.set_centered_player)
11379 game.centered_player_nr_next = tape.centered_player_nr_next;
11380 game.set_centered_player = TRUE;
11383 for (i = 0; i < MAX_PLAYERS; i++)
11385 summarized_player_action |= stored_player[i].action;
11387 if (!network_playing && (game.team_mode || tape.playing))
11388 stored_player[i].effective_action = stored_player[i].action;
11391 if (network_playing)
11392 SendToServer_MovePlayer(summarized_player_action);
11394 // summarize all actions at local players mapped input device position
11395 // (this allows using different input devices in single player mode)
11396 if (!network.enabled && !game.team_mode)
11397 stored_player[map_player_action[local_player->index_nr]].effective_action =
11398 summarized_player_action;
11400 if (tape.recording &&
11402 setup.input_on_focus &&
11403 game.centered_player_nr != -1)
11405 for (i = 0; i < MAX_PLAYERS; i++)
11406 stored_player[i].effective_action =
11407 (i == game.centered_player_nr ? summarized_player_action : 0);
11410 if (recorded_player_action != NULL)
11411 for (i = 0; i < MAX_PLAYERS; i++)
11412 stored_player[i].effective_action = recorded_player_action[i];
11414 for (i = 0; i < MAX_PLAYERS; i++)
11416 tape_action[i] = stored_player[i].effective_action;
11418 /* (this may happen in the RND game engine if a player was not present on
11419 the playfield on level start, but appeared later from a custom element */
11420 if (setup.team_mode &&
11423 !tape.player_participates[i])
11424 tape.player_participates[i] = TRUE;
11427 SetTapeActionFromMouseAction(tape_action,
11428 &local_player->effective_mouse_action);
11430 /* only record actions from input devices, but not programmed actions */
11431 if (tape.recording)
11432 TapeRecordAction(tape_action);
11434 #if USE_NEW_PLAYER_ASSIGNMENTS
11435 // !!! also map player actions in single player mode !!!
11436 // if (game.team_mode)
11439 byte mapped_action[MAX_PLAYERS];
11441 #if DEBUG_PLAYER_ACTIONS
11443 for (i = 0; i < MAX_PLAYERS; i++)
11444 printf(" %d, ", stored_player[i].effective_action);
11447 for (i = 0; i < MAX_PLAYERS; i++)
11448 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11450 for (i = 0; i < MAX_PLAYERS; i++)
11451 stored_player[i].effective_action = mapped_action[i];
11453 #if DEBUG_PLAYER_ACTIONS
11455 for (i = 0; i < MAX_PLAYERS; i++)
11456 printf(" %d, ", stored_player[i].effective_action);
11460 #if DEBUG_PLAYER_ACTIONS
11464 for (i = 0; i < MAX_PLAYERS; i++)
11465 printf(" %d, ", stored_player[i].effective_action);
11471 for (i = 0; i < MAX_PLAYERS; i++)
11473 // allow engine snapshot in case of changed movement attempt
11474 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11475 (stored_player[i].effective_action & KEY_MOTION))
11476 game.snapshot.changed_action = TRUE;
11478 // allow engine snapshot in case of snapping/dropping attempt
11479 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11480 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11481 game.snapshot.changed_action = TRUE;
11483 game.snapshot.last_action[i] = stored_player[i].effective_action;
11486 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11488 GameActions_EM_Main();
11490 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11492 GameActions_SP_Main();
11494 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11496 GameActions_MM_Main();
11500 GameActions_RND_Main();
11503 BlitScreenToBitmap(backbuffer);
11507 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11509 if (global.show_frames_per_second)
11511 static unsigned int fps_counter = 0;
11512 static int fps_frames = 0;
11513 unsigned int fps_delay_ms = Counter() - fps_counter;
11517 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11519 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11522 fps_counter = Counter();
11524 /* always draw FPS to screen after FPS value was updated */
11525 redraw_mask |= REDRAW_FPS;
11528 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11529 if (GetDrawDeactivationMask() == REDRAW_NONE)
11530 redraw_mask |= REDRAW_FPS;
11534 static void GameActions_CheckSaveEngineSnapshot()
11536 if (!game.snapshot.save_snapshot)
11539 // clear flag for saving snapshot _before_ saving snapshot
11540 game.snapshot.save_snapshot = FALSE;
11542 SaveEngineSnapshotToList();
11549 GameActions_CheckSaveEngineSnapshot();
11552 void GameActions_EM_Main()
11554 byte effective_action[MAX_PLAYERS];
11555 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11558 for (i = 0; i < MAX_PLAYERS; i++)
11559 effective_action[i] = stored_player[i].effective_action;
11561 GameActions_EM(effective_action, warp_mode);
11564 void GameActions_SP_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_SP(effective_action, warp_mode);
11575 for (i = 0; i < MAX_PLAYERS; i++)
11577 if (stored_player[i].force_dropping)
11578 stored_player[i].action |= KEY_BUTTON_DROP;
11580 stored_player[i].force_dropping = FALSE;
11584 void GameActions_MM_Main()
11586 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11588 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11591 void GameActions_RND_Main()
11596 void GameActions_RND()
11598 int magic_wall_x = 0, magic_wall_y = 0;
11599 int i, x, y, element, graphic, last_gfx_frame;
11601 InitPlayfieldScanModeVars();
11603 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11605 SCAN_PLAYFIELD(x, y)
11607 ChangeCount[x][y] = 0;
11608 ChangeEvent[x][y] = -1;
11612 if (game.set_centered_player)
11614 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11616 /* switching to "all players" only possible if all players fit to screen */
11617 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11619 game.centered_player_nr_next = game.centered_player_nr;
11620 game.set_centered_player = FALSE;
11623 /* do not switch focus to non-existing (or non-active) player */
11624 if (game.centered_player_nr_next >= 0 &&
11625 !stored_player[game.centered_player_nr_next].active)
11627 game.centered_player_nr_next = game.centered_player_nr;
11628 game.set_centered_player = FALSE;
11632 if (game.set_centered_player &&
11633 ScreenMovPos == 0) /* screen currently aligned at tile position */
11637 if (game.centered_player_nr_next == -1)
11639 setScreenCenteredToAllPlayers(&sx, &sy);
11643 sx = stored_player[game.centered_player_nr_next].jx;
11644 sy = stored_player[game.centered_player_nr_next].jy;
11647 game.centered_player_nr = game.centered_player_nr_next;
11648 game.set_centered_player = FALSE;
11650 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11651 DrawGameDoorValues();
11654 for (i = 0; i < MAX_PLAYERS; i++)
11656 int actual_player_action = stored_player[i].effective_action;
11659 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11660 - rnd_equinox_tetrachloride 048
11661 - rnd_equinox_tetrachloride_ii 096
11662 - rnd_emanuel_schmieg 002
11663 - doctor_sloan_ww 001, 020
11665 if (stored_player[i].MovPos == 0)
11666 CheckGravityMovement(&stored_player[i]);
11669 /* overwrite programmed action with tape action */
11670 if (stored_player[i].programmed_action)
11671 actual_player_action = stored_player[i].programmed_action;
11673 PlayerActions(&stored_player[i], actual_player_action);
11675 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11678 ScrollScreen(NULL, SCROLL_GO_ON);
11680 /* for backwards compatibility, the following code emulates a fixed bug that
11681 occured when pushing elements (causing elements that just made their last
11682 pushing step to already (if possible) make their first falling step in the
11683 same game frame, which is bad); this code is also needed to use the famous
11684 "spring push bug" which is used in older levels and might be wanted to be
11685 used also in newer levels, but in this case the buggy pushing code is only
11686 affecting the "spring" element and no other elements */
11688 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11690 for (i = 0; i < MAX_PLAYERS; i++)
11692 struct PlayerInfo *player = &stored_player[i];
11693 int x = player->jx;
11694 int y = player->jy;
11696 if (player->active && player->is_pushing && player->is_moving &&
11698 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11699 Feld[x][y] == EL_SPRING))
11701 ContinueMoving(x, y);
11703 /* continue moving after pushing (this is actually a bug) */
11704 if (!IS_MOVING(x, y))
11705 Stop[x][y] = FALSE;
11710 SCAN_PLAYFIELD(x, y)
11712 ChangeCount[x][y] = 0;
11713 ChangeEvent[x][y] = -1;
11715 /* this must be handled before main playfield loop */
11716 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11719 if (MovDelay[x][y] <= 0)
11723 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11726 if (MovDelay[x][y] <= 0)
11729 TEST_DrawLevelField(x, y);
11731 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11736 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11738 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11739 printf("GameActions(): This should never happen!\n");
11741 ChangePage[x][y] = -1;
11745 Stop[x][y] = FALSE;
11746 if (WasJustMoving[x][y] > 0)
11747 WasJustMoving[x][y]--;
11748 if (WasJustFalling[x][y] > 0)
11749 WasJustFalling[x][y]--;
11750 if (CheckCollision[x][y] > 0)
11751 CheckCollision[x][y]--;
11752 if (CheckImpact[x][y] > 0)
11753 CheckImpact[x][y]--;
11757 /* reset finished pushing action (not done in ContinueMoving() to allow
11758 continuous pushing animation for elements with zero push delay) */
11759 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11761 ResetGfxAnimation(x, y);
11762 TEST_DrawLevelField(x, y);
11766 if (IS_BLOCKED(x, y))
11770 Blocked2Moving(x, y, &oldx, &oldy);
11771 if (!IS_MOVING(oldx, oldy))
11773 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11774 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11775 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11776 printf("GameActions(): This should never happen!\n");
11782 SCAN_PLAYFIELD(x, y)
11784 element = Feld[x][y];
11785 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11786 last_gfx_frame = GfxFrame[x][y];
11788 ResetGfxFrame(x, y);
11790 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11791 DrawLevelGraphicAnimation(x, y, graphic);
11793 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11794 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11795 ResetRandomAnimationValue(x, y);
11797 SetRandomAnimationValue(x, y);
11799 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11801 if (IS_INACTIVE(element))
11803 if (IS_ANIMATED(graphic))
11804 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11809 /* this may take place after moving, so 'element' may have changed */
11810 if (IS_CHANGING(x, y) &&
11811 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11813 int page = element_info[element].event_page_nr[CE_DELAY];
11815 HandleElementChange(x, y, page);
11817 element = Feld[x][y];
11818 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11821 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11825 element = Feld[x][y];
11826 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11828 if (IS_ANIMATED(graphic) &&
11829 !IS_MOVING(x, y) &&
11831 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11833 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11834 TEST_DrawTwinkleOnField(x, y);
11836 else if (element == EL_ACID)
11839 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11841 else if ((element == EL_EXIT_OPEN ||
11842 element == EL_EM_EXIT_OPEN ||
11843 element == EL_SP_EXIT_OPEN ||
11844 element == EL_STEEL_EXIT_OPEN ||
11845 element == EL_EM_STEEL_EXIT_OPEN ||
11846 element == EL_SP_TERMINAL ||
11847 element == EL_SP_TERMINAL_ACTIVE ||
11848 element == EL_EXTRA_TIME ||
11849 element == EL_SHIELD_NORMAL ||
11850 element == EL_SHIELD_DEADLY) &&
11851 IS_ANIMATED(graphic))
11852 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11853 else if (IS_MOVING(x, y))
11854 ContinueMoving(x, y);
11855 else if (IS_ACTIVE_BOMB(element))
11856 CheckDynamite(x, y);
11857 else if (element == EL_AMOEBA_GROWING)
11858 AmoebeWaechst(x, y);
11859 else if (element == EL_AMOEBA_SHRINKING)
11860 AmoebaDisappearing(x, y);
11862 #if !USE_NEW_AMOEBA_CODE
11863 else if (IS_AMOEBALIVE(element))
11864 AmoebeAbleger(x, y);
11867 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11869 else if (element == EL_EXIT_CLOSED)
11871 else if (element == EL_EM_EXIT_CLOSED)
11873 else if (element == EL_STEEL_EXIT_CLOSED)
11874 CheckExitSteel(x, y);
11875 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11876 CheckExitSteelEM(x, y);
11877 else if (element == EL_SP_EXIT_CLOSED)
11879 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11880 element == EL_EXPANDABLE_STEELWALL_GROWING)
11881 MauerWaechst(x, y);
11882 else if (element == EL_EXPANDABLE_WALL ||
11883 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11884 element == EL_EXPANDABLE_WALL_VERTICAL ||
11885 element == EL_EXPANDABLE_WALL_ANY ||
11886 element == EL_BD_EXPANDABLE_WALL)
11887 MauerAbleger(x, y);
11888 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11889 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11890 element == EL_EXPANDABLE_STEELWALL_ANY)
11891 MauerAblegerStahl(x, y);
11892 else if (element == EL_FLAMES)
11893 CheckForDragon(x, y);
11894 else if (element == EL_EXPLOSION)
11895 ; /* drawing of correct explosion animation is handled separately */
11896 else if (element == EL_ELEMENT_SNAPPING ||
11897 element == EL_DIAGONAL_SHRINKING ||
11898 element == EL_DIAGONAL_GROWING)
11900 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11902 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11904 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11905 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11907 if (IS_BELT_ACTIVE(element))
11908 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11910 if (game.magic_wall_active)
11912 int jx = local_player->jx, jy = local_player->jy;
11914 /* play the element sound at the position nearest to the player */
11915 if ((element == EL_MAGIC_WALL_FULL ||
11916 element == EL_MAGIC_WALL_ACTIVE ||
11917 element == EL_MAGIC_WALL_EMPTYING ||
11918 element == EL_BD_MAGIC_WALL_FULL ||
11919 element == EL_BD_MAGIC_WALL_ACTIVE ||
11920 element == EL_BD_MAGIC_WALL_EMPTYING ||
11921 element == EL_DC_MAGIC_WALL_FULL ||
11922 element == EL_DC_MAGIC_WALL_ACTIVE ||
11923 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11924 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11932 #if USE_NEW_AMOEBA_CODE
11933 /* new experimental amoeba growth stuff */
11934 if (!(FrameCounter % 8))
11936 static unsigned int random = 1684108901;
11938 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11940 x = RND(lev_fieldx);
11941 y = RND(lev_fieldy);
11942 element = Feld[x][y];
11944 if (!IS_PLAYER(x,y) &&
11945 (element == EL_EMPTY ||
11946 CAN_GROW_INTO(element) ||
11947 element == EL_QUICKSAND_EMPTY ||
11948 element == EL_QUICKSAND_FAST_EMPTY ||
11949 element == EL_ACID_SPLASH_LEFT ||
11950 element == EL_ACID_SPLASH_RIGHT))
11952 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11953 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11954 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11955 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11956 Feld[x][y] = EL_AMOEBA_DROP;
11959 random = random * 129 + 1;
11964 game.explosions_delayed = FALSE;
11966 SCAN_PLAYFIELD(x, y)
11968 element = Feld[x][y];
11970 if (ExplodeField[x][y])
11971 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11972 else if (element == EL_EXPLOSION)
11973 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11975 ExplodeField[x][y] = EX_TYPE_NONE;
11978 game.explosions_delayed = TRUE;
11980 if (game.magic_wall_active)
11982 if (!(game.magic_wall_time_left % 4))
11984 int element = Feld[magic_wall_x][magic_wall_y];
11986 if (element == EL_BD_MAGIC_WALL_FULL ||
11987 element == EL_BD_MAGIC_WALL_ACTIVE ||
11988 element == EL_BD_MAGIC_WALL_EMPTYING)
11989 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11990 else if (element == EL_DC_MAGIC_WALL_FULL ||
11991 element == EL_DC_MAGIC_WALL_ACTIVE ||
11992 element == EL_DC_MAGIC_WALL_EMPTYING)
11993 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11995 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11998 if (game.magic_wall_time_left > 0)
12000 game.magic_wall_time_left--;
12002 if (!game.magic_wall_time_left)
12004 SCAN_PLAYFIELD(x, y)
12006 element = Feld[x][y];
12008 if (element == EL_MAGIC_WALL_ACTIVE ||
12009 element == EL_MAGIC_WALL_FULL)
12011 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12012 TEST_DrawLevelField(x, y);
12014 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12015 element == EL_BD_MAGIC_WALL_FULL)
12017 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12018 TEST_DrawLevelField(x, y);
12020 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12021 element == EL_DC_MAGIC_WALL_FULL)
12023 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12024 TEST_DrawLevelField(x, y);
12028 game.magic_wall_active = FALSE;
12033 if (game.light_time_left > 0)
12035 game.light_time_left--;
12037 if (game.light_time_left == 0)
12038 RedrawAllLightSwitchesAndInvisibleElements();
12041 if (game.timegate_time_left > 0)
12043 game.timegate_time_left--;
12045 if (game.timegate_time_left == 0)
12046 CloseAllOpenTimegates();
12049 if (game.lenses_time_left > 0)
12051 game.lenses_time_left--;
12053 if (game.lenses_time_left == 0)
12054 RedrawAllInvisibleElementsForLenses();
12057 if (game.magnify_time_left > 0)
12059 game.magnify_time_left--;
12061 if (game.magnify_time_left == 0)
12062 RedrawAllInvisibleElementsForMagnifier();
12065 for (i = 0; i < MAX_PLAYERS; i++)
12067 struct PlayerInfo *player = &stored_player[i];
12069 if (SHIELD_ON(player))
12071 if (player->shield_deadly_time_left)
12072 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12073 else if (player->shield_normal_time_left)
12074 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12078 #if USE_DELAYED_GFX_REDRAW
12079 SCAN_PLAYFIELD(x, y)
12081 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12083 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12084 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12086 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12087 DrawLevelField(x, y);
12089 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12090 DrawLevelFieldCrumbled(x, y);
12092 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12093 DrawLevelFieldCrumbledNeighbours(x, y);
12095 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12096 DrawTwinkleOnField(x, y);
12099 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12104 PlayAllPlayersSound();
12106 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12108 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12110 local_player->show_envelope = 0;
12113 /* use random number generator in every frame to make it less predictable */
12114 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12118 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12120 int min_x = x, min_y = y, max_x = x, max_y = y;
12123 for (i = 0; i < MAX_PLAYERS; i++)
12125 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12127 if (!stored_player[i].active || &stored_player[i] == player)
12130 min_x = MIN(min_x, jx);
12131 min_y = MIN(min_y, jy);
12132 max_x = MAX(max_x, jx);
12133 max_y = MAX(max_y, jy);
12136 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12139 static boolean AllPlayersInVisibleScreen()
12143 for (i = 0; i < MAX_PLAYERS; i++)
12145 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12147 if (!stored_player[i].active)
12150 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12157 void ScrollLevel(int dx, int dy)
12159 int scroll_offset = 2 * TILEX_VAR;
12162 BlitBitmap(drawto_field, drawto_field,
12163 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12164 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12165 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12166 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12167 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12168 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12172 x = (dx == 1 ? BX1 : BX2);
12173 for (y = BY1; y <= BY2; y++)
12174 DrawScreenField(x, y);
12179 y = (dy == 1 ? BY1 : BY2);
12180 for (x = BX1; x <= BX2; x++)
12181 DrawScreenField(x, y);
12184 redraw_mask |= REDRAW_FIELD;
12187 static boolean canFallDown(struct PlayerInfo *player)
12189 int jx = player->jx, jy = player->jy;
12191 return (IN_LEV_FIELD(jx, jy + 1) &&
12192 (IS_FREE(jx, jy + 1) ||
12193 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12194 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12195 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12198 static boolean canPassField(int x, int y, int move_dir)
12200 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12201 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12202 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12203 int nextx = x + dx;
12204 int nexty = y + dy;
12205 int element = Feld[x][y];
12207 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12208 !CAN_MOVE(element) &&
12209 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12210 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12211 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12214 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12216 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12217 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12218 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12222 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12223 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12224 (IS_DIGGABLE(Feld[newx][newy]) ||
12225 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12226 canPassField(newx, newy, move_dir)));
12229 static void CheckGravityMovement(struct PlayerInfo *player)
12231 if (player->gravity && !player->programmed_action)
12233 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12234 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12235 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12236 int jx = player->jx, jy = player->jy;
12237 boolean player_is_moving_to_valid_field =
12238 (!player_is_snapping &&
12239 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12240 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12241 boolean player_can_fall_down = canFallDown(player);
12243 if (player_can_fall_down &&
12244 !player_is_moving_to_valid_field)
12245 player->programmed_action = MV_DOWN;
12249 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12251 return CheckGravityMovement(player);
12253 if (player->gravity && !player->programmed_action)
12255 int jx = player->jx, jy = player->jy;
12256 boolean field_under_player_is_free =
12257 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12258 boolean player_is_standing_on_valid_field =
12259 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12260 (IS_WALKABLE(Feld[jx][jy]) &&
12261 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12263 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12264 player->programmed_action = MV_DOWN;
12269 MovePlayerOneStep()
12270 -----------------------------------------------------------------------------
12271 dx, dy: direction (non-diagonal) to try to move the player to
12272 real_dx, real_dy: direction as read from input device (can be diagonal)
12275 boolean MovePlayerOneStep(struct PlayerInfo *player,
12276 int dx, int dy, int real_dx, int real_dy)
12278 int jx = player->jx, jy = player->jy;
12279 int new_jx = jx + dx, new_jy = jy + dy;
12281 boolean player_can_move = !player->cannot_move;
12283 if (!player->active || (!dx && !dy))
12284 return MP_NO_ACTION;
12286 player->MovDir = (dx < 0 ? MV_LEFT :
12287 dx > 0 ? MV_RIGHT :
12289 dy > 0 ? MV_DOWN : MV_NONE);
12291 if (!IN_LEV_FIELD(new_jx, new_jy))
12292 return MP_NO_ACTION;
12294 if (!player_can_move)
12296 if (player->MovPos == 0)
12298 player->is_moving = FALSE;
12299 player->is_digging = FALSE;
12300 player->is_collecting = FALSE;
12301 player->is_snapping = FALSE;
12302 player->is_pushing = FALSE;
12306 if (!network.enabled && game.centered_player_nr == -1 &&
12307 !AllPlayersInSight(player, new_jx, new_jy))
12308 return MP_NO_ACTION;
12310 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12311 if (can_move != MP_MOVING)
12314 /* check if DigField() has caused relocation of the player */
12315 if (player->jx != jx || player->jy != jy)
12316 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12318 StorePlayer[jx][jy] = 0;
12319 player->last_jx = jx;
12320 player->last_jy = jy;
12321 player->jx = new_jx;
12322 player->jy = new_jy;
12323 StorePlayer[new_jx][new_jy] = player->element_nr;
12325 if (player->move_delay_value_next != -1)
12327 player->move_delay_value = player->move_delay_value_next;
12328 player->move_delay_value_next = -1;
12332 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12334 player->step_counter++;
12336 PlayerVisit[jx][jy] = FrameCounter;
12338 player->is_moving = TRUE;
12341 /* should better be called in MovePlayer(), but this breaks some tapes */
12342 ScrollPlayer(player, SCROLL_INIT);
12348 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12350 int jx = player->jx, jy = player->jy;
12351 int old_jx = jx, old_jy = jy;
12352 int moved = MP_NO_ACTION;
12354 if (!player->active)
12359 if (player->MovPos == 0)
12361 player->is_moving = FALSE;
12362 player->is_digging = FALSE;
12363 player->is_collecting = FALSE;
12364 player->is_snapping = FALSE;
12365 player->is_pushing = FALSE;
12371 if (player->move_delay > 0)
12374 player->move_delay = -1; /* set to "uninitialized" value */
12376 /* store if player is automatically moved to next field */
12377 player->is_auto_moving = (player->programmed_action != MV_NONE);
12379 /* remove the last programmed player action */
12380 player->programmed_action = 0;
12382 if (player->MovPos)
12384 /* should only happen if pre-1.2 tape recordings are played */
12385 /* this is only for backward compatibility */
12387 int original_move_delay_value = player->move_delay_value;
12390 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12394 /* scroll remaining steps with finest movement resolution */
12395 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12397 while (player->MovPos)
12399 ScrollPlayer(player, SCROLL_GO_ON);
12400 ScrollScreen(NULL, SCROLL_GO_ON);
12402 AdvanceFrameAndPlayerCounters(player->index_nr);
12405 BackToFront_WithFrameDelay(0);
12408 player->move_delay_value = original_move_delay_value;
12411 player->is_active = FALSE;
12413 if (player->last_move_dir & MV_HORIZONTAL)
12415 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12416 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12420 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12421 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12424 if (!moved && !player->is_active)
12426 player->is_moving = FALSE;
12427 player->is_digging = FALSE;
12428 player->is_collecting = FALSE;
12429 player->is_snapping = FALSE;
12430 player->is_pushing = FALSE;
12436 if (moved & MP_MOVING && !ScreenMovPos &&
12437 (player->index_nr == game.centered_player_nr ||
12438 game.centered_player_nr == -1))
12440 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12441 int offset = game.scroll_delay_value;
12443 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12445 /* actual player has left the screen -- scroll in that direction */
12446 if (jx != old_jx) /* player has moved horizontally */
12447 scroll_x += (jx - old_jx);
12448 else /* player has moved vertically */
12449 scroll_y += (jy - old_jy);
12453 if (jx != old_jx) /* player has moved horizontally */
12455 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12456 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12457 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12459 /* don't scroll over playfield boundaries */
12460 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12461 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12463 /* don't scroll more than one field at a time */
12464 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12466 /* don't scroll against the player's moving direction */
12467 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12468 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12469 scroll_x = old_scroll_x;
12471 else /* player has moved vertically */
12473 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12474 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12475 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12477 /* don't scroll over playfield boundaries */
12478 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12479 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12481 /* don't scroll more than one field at a time */
12482 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12484 /* don't scroll against the player's moving direction */
12485 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12486 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12487 scroll_y = old_scroll_y;
12491 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12493 if (!network.enabled && game.centered_player_nr == -1 &&
12494 !AllPlayersInVisibleScreen())
12496 scroll_x = old_scroll_x;
12497 scroll_y = old_scroll_y;
12501 ScrollScreen(player, SCROLL_INIT);
12502 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12507 player->StepFrame = 0;
12509 if (moved & MP_MOVING)
12511 if (old_jx != jx && old_jy == jy)
12512 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12513 else if (old_jx == jx && old_jy != jy)
12514 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12516 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12518 player->last_move_dir = player->MovDir;
12519 player->is_moving = TRUE;
12520 player->is_snapping = FALSE;
12521 player->is_switching = FALSE;
12522 player->is_dropping = FALSE;
12523 player->is_dropping_pressed = FALSE;
12524 player->drop_pressed_delay = 0;
12527 /* should better be called here than above, but this breaks some tapes */
12528 ScrollPlayer(player, SCROLL_INIT);
12533 CheckGravityMovementWhenNotMoving(player);
12535 player->is_moving = FALSE;
12537 /* at this point, the player is allowed to move, but cannot move right now
12538 (e.g. because of something blocking the way) -- ensure that the player
12539 is also allowed to move in the next frame (in old versions before 3.1.1,
12540 the player was forced to wait again for eight frames before next try) */
12542 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12543 player->move_delay = 0; /* allow direct movement in the next frame */
12546 if (player->move_delay == -1) /* not yet initialized by DigField() */
12547 player->move_delay = player->move_delay_value;
12549 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12551 TestIfPlayerTouchesBadThing(jx, jy);
12552 TestIfPlayerTouchesCustomElement(jx, jy);
12555 if (!player->active)
12556 RemovePlayer(player);
12561 void ScrollPlayer(struct PlayerInfo *player, int mode)
12563 int jx = player->jx, jy = player->jy;
12564 int last_jx = player->last_jx, last_jy = player->last_jy;
12565 int move_stepsize = TILEX / player->move_delay_value;
12567 if (!player->active)
12570 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12573 if (mode == SCROLL_INIT)
12575 player->actual_frame_counter = FrameCounter;
12576 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12578 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12579 Feld[last_jx][last_jy] == EL_EMPTY)
12581 int last_field_block_delay = 0; /* start with no blocking at all */
12582 int block_delay_adjustment = player->block_delay_adjustment;
12584 /* if player blocks last field, add delay for exactly one move */
12585 if (player->block_last_field)
12587 last_field_block_delay += player->move_delay_value;
12589 /* when blocking enabled, prevent moving up despite gravity */
12590 if (player->gravity && player->MovDir == MV_UP)
12591 block_delay_adjustment = -1;
12594 /* add block delay adjustment (also possible when not blocking) */
12595 last_field_block_delay += block_delay_adjustment;
12597 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12598 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12601 if (player->MovPos != 0) /* player has not yet reached destination */
12604 else if (!FrameReached(&player->actual_frame_counter, 1))
12607 if (player->MovPos != 0)
12609 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12610 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12612 /* before DrawPlayer() to draw correct player graphic for this case */
12613 if (player->MovPos == 0)
12614 CheckGravityMovement(player);
12617 if (player->MovPos == 0) /* player reached destination field */
12619 if (player->move_delay_reset_counter > 0)
12621 player->move_delay_reset_counter--;
12623 if (player->move_delay_reset_counter == 0)
12625 /* continue with normal speed after quickly moving through gate */
12626 HALVE_PLAYER_SPEED(player);
12628 /* be able to make the next move without delay */
12629 player->move_delay = 0;
12633 player->last_jx = jx;
12634 player->last_jy = jy;
12636 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12637 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12638 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12639 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12640 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12641 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12642 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12643 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12645 RemovePlayerWithCleanup(player);
12647 if ((local_player->friends_still_needed == 0 ||
12648 IS_SP_ELEMENT(Feld[jx][jy])) &&
12650 PlayerWins(local_player);
12653 /* this breaks one level: "machine", level 000 */
12655 int move_direction = player->MovDir;
12656 int enter_side = MV_DIR_OPPOSITE(move_direction);
12657 int leave_side = move_direction;
12658 int old_jx = last_jx;
12659 int old_jy = last_jy;
12660 int old_element = Feld[old_jx][old_jy];
12661 int new_element = Feld[jx][jy];
12663 if (IS_CUSTOM_ELEMENT(old_element))
12664 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12666 player->index_bit, leave_side);
12668 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12669 CE_PLAYER_LEAVES_X,
12670 player->index_bit, leave_side);
12672 if (IS_CUSTOM_ELEMENT(new_element))
12673 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12674 player->index_bit, enter_side);
12676 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12677 CE_PLAYER_ENTERS_X,
12678 player->index_bit, enter_side);
12680 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12681 CE_MOVE_OF_X, move_direction);
12684 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12686 TestIfPlayerTouchesBadThing(jx, jy);
12687 TestIfPlayerTouchesCustomElement(jx, jy);
12689 /* needed because pushed element has not yet reached its destination,
12690 so it would trigger a change event at its previous field location */
12691 if (!player->is_pushing)
12692 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12694 if (!player->active)
12695 RemovePlayer(player);
12698 if (!local_player->LevelSolved && level.use_step_counter)
12708 if (TimeLeft <= 10 && setup.time_limit)
12709 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12711 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12713 DisplayGameControlValues();
12715 if (!TimeLeft && setup.time_limit)
12716 for (i = 0; i < MAX_PLAYERS; i++)
12717 KillPlayer(&stored_player[i]);
12719 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12721 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12723 DisplayGameControlValues();
12727 if (tape.single_step && tape.recording && !tape.pausing &&
12728 !player->programmed_action)
12729 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12731 if (!player->programmed_action)
12732 CheckSaveEngineSnapshot(player);
12736 void ScrollScreen(struct PlayerInfo *player, int mode)
12738 static unsigned int screen_frame_counter = 0;
12740 if (mode == SCROLL_INIT)
12742 /* set scrolling step size according to actual player's moving speed */
12743 ScrollStepSize = TILEX / player->move_delay_value;
12745 screen_frame_counter = FrameCounter;
12746 ScreenMovDir = player->MovDir;
12747 ScreenMovPos = player->MovPos;
12748 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12751 else if (!FrameReached(&screen_frame_counter, 1))
12756 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12757 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12758 redraw_mask |= REDRAW_FIELD;
12761 ScreenMovDir = MV_NONE;
12764 void TestIfPlayerTouchesCustomElement(int x, int y)
12766 static int xy[4][2] =
12773 static int trigger_sides[4][2] =
12775 /* center side border side */
12776 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12777 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12778 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12779 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12781 static int touch_dir[4] =
12783 MV_LEFT | MV_RIGHT,
12788 int center_element = Feld[x][y]; /* should always be non-moving! */
12791 for (i = 0; i < NUM_DIRECTIONS; i++)
12793 int xx = x + xy[i][0];
12794 int yy = y + xy[i][1];
12795 int center_side = trigger_sides[i][0];
12796 int border_side = trigger_sides[i][1];
12797 int border_element;
12799 if (!IN_LEV_FIELD(xx, yy))
12802 if (IS_PLAYER(x, y)) /* player found at center element */
12804 struct PlayerInfo *player = PLAYERINFO(x, y);
12806 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12807 border_element = Feld[xx][yy]; /* may be moving! */
12808 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12809 border_element = Feld[xx][yy];
12810 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12811 border_element = MovingOrBlocked2Element(xx, yy);
12813 continue; /* center and border element do not touch */
12815 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12816 player->index_bit, border_side);
12817 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12818 CE_PLAYER_TOUCHES_X,
12819 player->index_bit, border_side);
12822 /* use player element that is initially defined in the level playfield,
12823 not the player element that corresponds to the runtime player number
12824 (example: a level that contains EL_PLAYER_3 as the only player would
12825 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12826 int player_element = PLAYERINFO(x, y)->initial_element;
12828 CheckElementChangeBySide(xx, yy, border_element, player_element,
12829 CE_TOUCHING_X, border_side);
12832 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12834 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12836 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12838 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12839 continue; /* center and border element do not touch */
12842 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12843 player->index_bit, center_side);
12844 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12845 CE_PLAYER_TOUCHES_X,
12846 player->index_bit, center_side);
12849 /* use player element that is initially defined in the level playfield,
12850 not the player element that corresponds to the runtime player number
12851 (example: a level that contains EL_PLAYER_3 as the only player would
12852 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12853 int player_element = PLAYERINFO(xx, yy)->initial_element;
12855 CheckElementChangeBySide(x, y, center_element, player_element,
12856 CE_TOUCHING_X, center_side);
12864 void TestIfElementTouchesCustomElement(int x, int y)
12866 static int xy[4][2] =
12873 static int trigger_sides[4][2] =
12875 /* center side border side */
12876 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12877 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12878 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12879 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12881 static int touch_dir[4] =
12883 MV_LEFT | MV_RIGHT,
12888 boolean change_center_element = FALSE;
12889 int center_element = Feld[x][y]; /* should always be non-moving! */
12890 int border_element_old[NUM_DIRECTIONS];
12893 for (i = 0; i < NUM_DIRECTIONS; i++)
12895 int xx = x + xy[i][0];
12896 int yy = y + xy[i][1];
12897 int border_element;
12899 border_element_old[i] = -1;
12901 if (!IN_LEV_FIELD(xx, yy))
12904 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12905 border_element = Feld[xx][yy]; /* may be moving! */
12906 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12907 border_element = Feld[xx][yy];
12908 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12909 border_element = MovingOrBlocked2Element(xx, yy);
12911 continue; /* center and border element do not touch */
12913 border_element_old[i] = border_element;
12916 for (i = 0; i < NUM_DIRECTIONS; i++)
12918 int xx = x + xy[i][0];
12919 int yy = y + xy[i][1];
12920 int center_side = trigger_sides[i][0];
12921 int border_element = border_element_old[i];
12923 if (border_element == -1)
12926 /* check for change of border element */
12927 CheckElementChangeBySide(xx, yy, border_element, center_element,
12928 CE_TOUCHING_X, center_side);
12930 /* (center element cannot be player, so we dont have to check this here) */
12933 for (i = 0; i < NUM_DIRECTIONS; i++)
12935 int xx = x + xy[i][0];
12936 int yy = y + xy[i][1];
12937 int border_side = trigger_sides[i][1];
12938 int border_element = border_element_old[i];
12940 if (border_element == -1)
12943 /* check for change of center element (but change it only once) */
12944 if (!change_center_element)
12945 change_center_element =
12946 CheckElementChangeBySide(x, y, center_element, border_element,
12947 CE_TOUCHING_X, border_side);
12949 if (IS_PLAYER(xx, yy))
12951 /* use player element that is initially defined in the level playfield,
12952 not the player element that corresponds to the runtime player number
12953 (example: a level that contains EL_PLAYER_3 as the only player would
12954 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12955 int player_element = PLAYERINFO(xx, yy)->initial_element;
12957 CheckElementChangeBySide(x, y, center_element, player_element,
12958 CE_TOUCHING_X, border_side);
12963 void TestIfElementHitsCustomElement(int x, int y, int direction)
12965 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12966 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12967 int hitx = x + dx, hity = y + dy;
12968 int hitting_element = Feld[x][y];
12969 int touched_element;
12971 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12974 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12975 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12977 if (IN_LEV_FIELD(hitx, hity))
12979 int opposite_direction = MV_DIR_OPPOSITE(direction);
12980 int hitting_side = direction;
12981 int touched_side = opposite_direction;
12982 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12983 MovDir[hitx][hity] != direction ||
12984 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12990 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12991 CE_HITTING_X, touched_side);
12993 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12994 CE_HIT_BY_X, hitting_side);
12996 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12997 CE_HIT_BY_SOMETHING, opposite_direction);
12999 if (IS_PLAYER(hitx, hity))
13001 /* use player element that is initially defined in the level playfield,
13002 not the player element that corresponds to the runtime player number
13003 (example: a level that contains EL_PLAYER_3 as the only player would
13004 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13005 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13007 CheckElementChangeBySide(x, y, hitting_element, player_element,
13008 CE_HITTING_X, touched_side);
13013 /* "hitting something" is also true when hitting the playfield border */
13014 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13015 CE_HITTING_SOMETHING, direction);
13018 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13020 int i, kill_x = -1, kill_y = -1;
13022 int bad_element = -1;
13023 static int test_xy[4][2] =
13030 static int test_dir[4] =
13038 for (i = 0; i < NUM_DIRECTIONS; i++)
13040 int test_x, test_y, test_move_dir, test_element;
13042 test_x = good_x + test_xy[i][0];
13043 test_y = good_y + test_xy[i][1];
13045 if (!IN_LEV_FIELD(test_x, test_y))
13049 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13051 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13053 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13054 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13056 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13057 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13061 bad_element = test_element;
13067 if (kill_x != -1 || kill_y != -1)
13069 if (IS_PLAYER(good_x, good_y))
13071 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13073 if (player->shield_deadly_time_left > 0 &&
13074 !IS_INDESTRUCTIBLE(bad_element))
13075 Bang(kill_x, kill_y);
13076 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13077 KillPlayer(player);
13080 Bang(good_x, good_y);
13084 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13086 int i, kill_x = -1, kill_y = -1;
13087 int bad_element = Feld[bad_x][bad_y];
13088 static int test_xy[4][2] =
13095 static int touch_dir[4] =
13097 MV_LEFT | MV_RIGHT,
13102 static int test_dir[4] =
13110 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13113 for (i = 0; i < NUM_DIRECTIONS; i++)
13115 int test_x, test_y, test_move_dir, test_element;
13117 test_x = bad_x + test_xy[i][0];
13118 test_y = bad_y + test_xy[i][1];
13120 if (!IN_LEV_FIELD(test_x, test_y))
13124 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13126 test_element = Feld[test_x][test_y];
13128 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13129 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13131 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13132 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13134 /* good thing is player or penguin that does not move away */
13135 if (IS_PLAYER(test_x, test_y))
13137 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13139 if (bad_element == EL_ROBOT && player->is_moving)
13140 continue; /* robot does not kill player if he is moving */
13142 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13144 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13145 continue; /* center and border element do not touch */
13153 else if (test_element == EL_PENGUIN)
13163 if (kill_x != -1 || kill_y != -1)
13165 if (IS_PLAYER(kill_x, kill_y))
13167 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13169 if (player->shield_deadly_time_left > 0 &&
13170 !IS_INDESTRUCTIBLE(bad_element))
13171 Bang(bad_x, bad_y);
13172 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13173 KillPlayer(player);
13176 Bang(kill_x, kill_y);
13180 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13182 int bad_element = Feld[bad_x][bad_y];
13183 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13184 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13185 int test_x = bad_x + dx, test_y = bad_y + dy;
13186 int test_move_dir, test_element;
13187 int kill_x = -1, kill_y = -1;
13189 if (!IN_LEV_FIELD(test_x, test_y))
13193 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13195 test_element = Feld[test_x][test_y];
13197 if (test_move_dir != bad_move_dir)
13199 /* good thing can be player or penguin that does not move away */
13200 if (IS_PLAYER(test_x, test_y))
13202 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13204 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13205 player as being hit when he is moving towards the bad thing, because
13206 the "get hit by" condition would be lost after the player stops) */
13207 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13208 return; /* player moves away from bad thing */
13213 else if (test_element == EL_PENGUIN)
13220 if (kill_x != -1 || kill_y != -1)
13222 if (IS_PLAYER(kill_x, kill_y))
13224 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13226 if (player->shield_deadly_time_left > 0 &&
13227 !IS_INDESTRUCTIBLE(bad_element))
13228 Bang(bad_x, bad_y);
13229 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13230 KillPlayer(player);
13233 Bang(kill_x, kill_y);
13237 void TestIfPlayerTouchesBadThing(int x, int y)
13239 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13242 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13244 TestIfGoodThingHitsBadThing(x, y, move_dir);
13247 void TestIfBadThingTouchesPlayer(int x, int y)
13249 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13252 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13254 TestIfBadThingHitsGoodThing(x, y, move_dir);
13257 void TestIfFriendTouchesBadThing(int x, int y)
13259 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13262 void TestIfBadThingTouchesFriend(int x, int y)
13264 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13267 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13269 int i, kill_x = bad_x, kill_y = bad_y;
13270 static int xy[4][2] =
13278 for (i = 0; i < NUM_DIRECTIONS; i++)
13282 x = bad_x + xy[i][0];
13283 y = bad_y + xy[i][1];
13284 if (!IN_LEV_FIELD(x, y))
13287 element = Feld[x][y];
13288 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13289 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13297 if (kill_x != bad_x || kill_y != bad_y)
13298 Bang(bad_x, bad_y);
13301 void KillPlayer(struct PlayerInfo *player)
13303 int jx = player->jx, jy = player->jy;
13305 if (!player->active)
13309 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13310 player->killed, player->active, player->reanimated);
13313 /* the following code was introduced to prevent an infinite loop when calling
13315 -> CheckTriggeredElementChangeExt()
13316 -> ExecuteCustomElementAction()
13318 -> (infinitely repeating the above sequence of function calls)
13319 which occurs when killing the player while having a CE with the setting
13320 "kill player X when explosion of <player X>"; the solution using a new
13321 field "player->killed" was chosen for backwards compatibility, although
13322 clever use of the fields "player->active" etc. would probably also work */
13324 if (player->killed)
13328 player->killed = TRUE;
13330 /* remove accessible field at the player's position */
13331 Feld[jx][jy] = EL_EMPTY;
13333 /* deactivate shield (else Bang()/Explode() would not work right) */
13334 player->shield_normal_time_left = 0;
13335 player->shield_deadly_time_left = 0;
13338 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13339 player->killed, player->active, player->reanimated);
13345 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13346 player->killed, player->active, player->reanimated);
13349 if (player->reanimated) /* killed player may have been reanimated */
13350 player->killed = player->reanimated = FALSE;
13352 BuryPlayer(player);
13355 static void KillPlayerUnlessEnemyProtected(int x, int y)
13357 if (!PLAYER_ENEMY_PROTECTED(x, y))
13358 KillPlayer(PLAYERINFO(x, y));
13361 static void KillPlayerUnlessExplosionProtected(int x, int y)
13363 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13364 KillPlayer(PLAYERINFO(x, y));
13367 void BuryPlayer(struct PlayerInfo *player)
13369 int jx = player->jx, jy = player->jy;
13371 if (!player->active)
13374 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13375 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13377 player->GameOver = TRUE;
13378 RemovePlayer(player);
13381 void RemovePlayer(struct PlayerInfo *player)
13383 int jx = player->jx, jy = player->jy;
13384 int i, found = FALSE;
13386 player->present = FALSE;
13387 player->active = FALSE;
13389 if (!ExplodeField[jx][jy])
13390 StorePlayer[jx][jy] = 0;
13392 if (player->is_moving)
13393 TEST_DrawLevelField(player->last_jx, player->last_jy);
13395 for (i = 0; i < MAX_PLAYERS; i++)
13396 if (stored_player[i].active)
13400 AllPlayersGone = TRUE;
13406 void RemovePlayerWithCleanup(struct PlayerInfo *player)
13408 DrawPlayer(player); /* needed here only to cleanup last field */
13409 RemovePlayer(player);
13412 static void setFieldForSnapping(int x, int y, int element, int direction)
13414 struct ElementInfo *ei = &element_info[element];
13415 int direction_bit = MV_DIR_TO_BIT(direction);
13416 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13417 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13418 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13420 Feld[x][y] = EL_ELEMENT_SNAPPING;
13421 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13423 ResetGfxAnimation(x, y);
13425 GfxElement[x][y] = element;
13426 GfxAction[x][y] = action;
13427 GfxDir[x][y] = direction;
13428 GfxFrame[x][y] = -1;
13432 =============================================================================
13433 checkDiagonalPushing()
13434 -----------------------------------------------------------------------------
13435 check if diagonal input device direction results in pushing of object
13436 (by checking if the alternative direction is walkable, diggable, ...)
13437 =============================================================================
13440 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13441 int x, int y, int real_dx, int real_dy)
13443 int jx, jy, dx, dy, xx, yy;
13445 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13448 /* diagonal direction: check alternative direction */
13453 xx = jx + (dx == 0 ? real_dx : 0);
13454 yy = jy + (dy == 0 ? real_dy : 0);
13456 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13460 =============================================================================
13462 -----------------------------------------------------------------------------
13463 x, y: field next to player (non-diagonal) to try to dig to
13464 real_dx, real_dy: direction as read from input device (can be diagonal)
13465 =============================================================================
13468 static int DigField(struct PlayerInfo *player,
13469 int oldx, int oldy, int x, int y,
13470 int real_dx, int real_dy, int mode)
13472 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13473 boolean player_was_pushing = player->is_pushing;
13474 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13475 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13476 int jx = oldx, jy = oldy;
13477 int dx = x - jx, dy = y - jy;
13478 int nextx = x + dx, nexty = y + dy;
13479 int move_direction = (dx == -1 ? MV_LEFT :
13480 dx == +1 ? MV_RIGHT :
13482 dy == +1 ? MV_DOWN : MV_NONE);
13483 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13484 int dig_side = MV_DIR_OPPOSITE(move_direction);
13485 int old_element = Feld[jx][jy];
13486 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13489 if (is_player) /* function can also be called by EL_PENGUIN */
13491 if (player->MovPos == 0)
13493 player->is_digging = FALSE;
13494 player->is_collecting = FALSE;
13497 if (player->MovPos == 0) /* last pushing move finished */
13498 player->is_pushing = FALSE;
13500 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13502 player->is_switching = FALSE;
13503 player->push_delay = -1;
13505 return MP_NO_ACTION;
13509 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13510 old_element = Back[jx][jy];
13512 /* in case of element dropped at player position, check background */
13513 else if (Back[jx][jy] != EL_EMPTY &&
13514 game.engine_version >= VERSION_IDENT(2,2,0,0))
13515 old_element = Back[jx][jy];
13517 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13518 return MP_NO_ACTION; /* field has no opening in this direction */
13520 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13521 return MP_NO_ACTION; /* field has no opening in this direction */
13523 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13527 Feld[jx][jy] = player->artwork_element;
13528 InitMovingField(jx, jy, MV_DOWN);
13529 Store[jx][jy] = EL_ACID;
13530 ContinueMoving(jx, jy);
13531 BuryPlayer(player);
13533 return MP_DONT_RUN_INTO;
13536 if (player_can_move && DONT_RUN_INTO(element))
13538 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13540 return MP_DONT_RUN_INTO;
13543 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13544 return MP_NO_ACTION;
13546 collect_count = element_info[element].collect_count_initial;
13548 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13549 return MP_NO_ACTION;
13551 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13552 player_can_move = player_can_move_or_snap;
13554 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13555 game.engine_version >= VERSION_IDENT(2,2,0,0))
13557 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13558 player->index_bit, dig_side);
13559 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13560 player->index_bit, dig_side);
13562 if (element == EL_DC_LANDMINE)
13565 if (Feld[x][y] != element) /* field changed by snapping */
13568 return MP_NO_ACTION;
13571 if (player->gravity && is_player && !player->is_auto_moving &&
13572 canFallDown(player) && move_direction != MV_DOWN &&
13573 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13574 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13576 if (player_can_move &&
13577 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13579 int sound_element = SND_ELEMENT(element);
13580 int sound_action = ACTION_WALKING;
13582 if (IS_RND_GATE(element))
13584 if (!player->key[RND_GATE_NR(element)])
13585 return MP_NO_ACTION;
13587 else if (IS_RND_GATE_GRAY(element))
13589 if (!player->key[RND_GATE_GRAY_NR(element)])
13590 return MP_NO_ACTION;
13592 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13594 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13595 return MP_NO_ACTION;
13597 else if (element == EL_EXIT_OPEN ||
13598 element == EL_EM_EXIT_OPEN ||
13599 element == EL_EM_EXIT_OPENING ||
13600 element == EL_STEEL_EXIT_OPEN ||
13601 element == EL_EM_STEEL_EXIT_OPEN ||
13602 element == EL_EM_STEEL_EXIT_OPENING ||
13603 element == EL_SP_EXIT_OPEN ||
13604 element == EL_SP_EXIT_OPENING)
13606 sound_action = ACTION_PASSING; /* player is passing exit */
13608 else if (element == EL_EMPTY)
13610 sound_action = ACTION_MOVING; /* nothing to walk on */
13613 /* play sound from background or player, whatever is available */
13614 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13615 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13617 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13619 else if (player_can_move &&
13620 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13622 if (!ACCESS_FROM(element, opposite_direction))
13623 return MP_NO_ACTION; /* field not accessible from this direction */
13625 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13626 return MP_NO_ACTION;
13628 if (IS_EM_GATE(element))
13630 if (!player->key[EM_GATE_NR(element)])
13631 return MP_NO_ACTION;
13633 else if (IS_EM_GATE_GRAY(element))
13635 if (!player->key[EM_GATE_GRAY_NR(element)])
13636 return MP_NO_ACTION;
13638 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13640 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13641 return MP_NO_ACTION;
13643 else if (IS_EMC_GATE(element))
13645 if (!player->key[EMC_GATE_NR(element)])
13646 return MP_NO_ACTION;
13648 else if (IS_EMC_GATE_GRAY(element))
13650 if (!player->key[EMC_GATE_GRAY_NR(element)])
13651 return MP_NO_ACTION;
13653 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13655 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13656 return MP_NO_ACTION;
13658 else if (element == EL_DC_GATE_WHITE ||
13659 element == EL_DC_GATE_WHITE_GRAY ||
13660 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13662 if (player->num_white_keys == 0)
13663 return MP_NO_ACTION;
13665 player->num_white_keys--;
13667 else if (IS_SP_PORT(element))
13669 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13670 element == EL_SP_GRAVITY_PORT_RIGHT ||
13671 element == EL_SP_GRAVITY_PORT_UP ||
13672 element == EL_SP_GRAVITY_PORT_DOWN)
13673 player->gravity = !player->gravity;
13674 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13675 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13676 element == EL_SP_GRAVITY_ON_PORT_UP ||
13677 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13678 player->gravity = TRUE;
13679 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13680 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13681 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13682 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13683 player->gravity = FALSE;
13686 /* automatically move to the next field with double speed */
13687 player->programmed_action = move_direction;
13689 if (player->move_delay_reset_counter == 0)
13691 player->move_delay_reset_counter = 2; /* two double speed steps */
13693 DOUBLE_PLAYER_SPEED(player);
13696 PlayLevelSoundAction(x, y, ACTION_PASSING);
13698 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13702 if (mode != DF_SNAP)
13704 GfxElement[x][y] = GFX_ELEMENT(element);
13705 player->is_digging = TRUE;
13708 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13710 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13711 player->index_bit, dig_side);
13713 if (mode == DF_SNAP)
13715 if (level.block_snap_field)
13716 setFieldForSnapping(x, y, element, move_direction);
13718 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13720 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13721 player->index_bit, dig_side);
13724 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13728 if (is_player && mode != DF_SNAP)
13730 GfxElement[x][y] = element;
13731 player->is_collecting = TRUE;
13734 if (element == EL_SPEED_PILL)
13736 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13738 else if (element == EL_EXTRA_TIME && level.time > 0)
13740 TimeLeft += level.extra_time;
13742 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13744 DisplayGameControlValues();
13746 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13748 player->shield_normal_time_left += level.shield_normal_time;
13749 if (element == EL_SHIELD_DEADLY)
13750 player->shield_deadly_time_left += level.shield_deadly_time;
13752 else if (element == EL_DYNAMITE ||
13753 element == EL_EM_DYNAMITE ||
13754 element == EL_SP_DISK_RED)
13756 if (player->inventory_size < MAX_INVENTORY_SIZE)
13757 player->inventory_element[player->inventory_size++] = element;
13759 DrawGameDoorValues();
13761 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13763 player->dynabomb_count++;
13764 player->dynabombs_left++;
13766 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13768 player->dynabomb_size++;
13770 else if (element == EL_DYNABOMB_INCREASE_POWER)
13772 player->dynabomb_xl = TRUE;
13774 else if (IS_KEY(element))
13776 player->key[KEY_NR(element)] = TRUE;
13778 DrawGameDoorValues();
13780 else if (element == EL_DC_KEY_WHITE)
13782 player->num_white_keys++;
13784 /* display white keys? */
13785 /* DrawGameDoorValues(); */
13787 else if (IS_ENVELOPE(element))
13789 player->show_envelope = element;
13791 else if (element == EL_EMC_LENSES)
13793 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13795 RedrawAllInvisibleElementsForLenses();
13797 else if (element == EL_EMC_MAGNIFIER)
13799 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13801 RedrawAllInvisibleElementsForMagnifier();
13803 else if (IS_DROPPABLE(element) ||
13804 IS_THROWABLE(element)) /* can be collected and dropped */
13808 if (collect_count == 0)
13809 player->inventory_infinite_element = element;
13811 for (i = 0; i < collect_count; i++)
13812 if (player->inventory_size < MAX_INVENTORY_SIZE)
13813 player->inventory_element[player->inventory_size++] = element;
13815 DrawGameDoorValues();
13817 else if (collect_count > 0)
13819 local_player->gems_still_needed -= collect_count;
13820 if (local_player->gems_still_needed < 0)
13821 local_player->gems_still_needed = 0;
13823 game.snapshot.collected_item = TRUE;
13825 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13827 DisplayGameControlValues();
13830 RaiseScoreElement(element);
13831 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13834 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13835 player->index_bit, dig_side);
13837 if (mode == DF_SNAP)
13839 if (level.block_snap_field)
13840 setFieldForSnapping(x, y, element, move_direction);
13842 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13844 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13845 player->index_bit, dig_side);
13848 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13850 if (mode == DF_SNAP && element != EL_BD_ROCK)
13851 return MP_NO_ACTION;
13853 if (CAN_FALL(element) && dy)
13854 return MP_NO_ACTION;
13856 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13857 !(element == EL_SPRING && level.use_spring_bug))
13858 return MP_NO_ACTION;
13860 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13861 ((move_direction & MV_VERTICAL &&
13862 ((element_info[element].move_pattern & MV_LEFT &&
13863 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13864 (element_info[element].move_pattern & MV_RIGHT &&
13865 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13866 (move_direction & MV_HORIZONTAL &&
13867 ((element_info[element].move_pattern & MV_UP &&
13868 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13869 (element_info[element].move_pattern & MV_DOWN &&
13870 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13871 return MP_NO_ACTION;
13873 /* do not push elements already moving away faster than player */
13874 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13875 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13876 return MP_NO_ACTION;
13878 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13880 if (player->push_delay_value == -1 || !player_was_pushing)
13881 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13883 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13885 if (player->push_delay_value == -1)
13886 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13888 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13890 if (!player->is_pushing)
13891 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13894 player->is_pushing = TRUE;
13895 player->is_active = TRUE;
13897 if (!(IN_LEV_FIELD(nextx, nexty) &&
13898 (IS_FREE(nextx, nexty) ||
13899 (IS_SB_ELEMENT(element) &&
13900 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13901 (IS_CUSTOM_ELEMENT(element) &&
13902 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13903 return MP_NO_ACTION;
13905 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13906 return MP_NO_ACTION;
13908 if (player->push_delay == -1) /* new pushing; restart delay */
13909 player->push_delay = 0;
13911 if (player->push_delay < player->push_delay_value &&
13912 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13913 element != EL_SPRING && element != EL_BALLOON)
13915 /* make sure that there is no move delay before next try to push */
13916 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13917 player->move_delay = 0;
13919 return MP_NO_ACTION;
13922 if (IS_CUSTOM_ELEMENT(element) &&
13923 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13925 if (!DigFieldByCE(nextx, nexty, element))
13926 return MP_NO_ACTION;
13929 if (IS_SB_ELEMENT(element))
13931 if (element == EL_SOKOBAN_FIELD_FULL)
13933 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13934 local_player->sokobanfields_still_needed++;
13937 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13939 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13940 local_player->sokobanfields_still_needed--;
13943 Feld[x][y] = EL_SOKOBAN_OBJECT;
13945 if (Back[x][y] == Back[nextx][nexty])
13946 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13947 else if (Back[x][y] != 0)
13948 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13951 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13954 if (local_player->sokobanfields_still_needed == 0 &&
13955 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13957 PlayerWins(player);
13959 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13963 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13965 InitMovingField(x, y, move_direction);
13966 GfxAction[x][y] = ACTION_PUSHING;
13968 if (mode == DF_SNAP)
13969 ContinueMoving(x, y);
13971 MovPos[x][y] = (dx != 0 ? dx : dy);
13973 Pushed[x][y] = TRUE;
13974 Pushed[nextx][nexty] = TRUE;
13976 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13977 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13979 player->push_delay_value = -1; /* get new value later */
13981 /* check for element change _after_ element has been pushed */
13982 if (game.use_change_when_pushing_bug)
13984 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13985 player->index_bit, dig_side);
13986 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13987 player->index_bit, dig_side);
13990 else if (IS_SWITCHABLE(element))
13992 if (PLAYER_SWITCHING(player, x, y))
13994 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13995 player->index_bit, dig_side);
14000 player->is_switching = TRUE;
14001 player->switch_x = x;
14002 player->switch_y = y;
14004 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14006 if (element == EL_ROBOT_WHEEL)
14008 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14012 game.robot_wheel_active = TRUE;
14014 TEST_DrawLevelField(x, y);
14016 else if (element == EL_SP_TERMINAL)
14020 SCAN_PLAYFIELD(xx, yy)
14022 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14026 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14028 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14030 ResetGfxAnimation(xx, yy);
14031 TEST_DrawLevelField(xx, yy);
14035 else if (IS_BELT_SWITCH(element))
14037 ToggleBeltSwitch(x, y);
14039 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14040 element == EL_SWITCHGATE_SWITCH_DOWN ||
14041 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14042 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14044 ToggleSwitchgateSwitch(x, y);
14046 else if (element == EL_LIGHT_SWITCH ||
14047 element == EL_LIGHT_SWITCH_ACTIVE)
14049 ToggleLightSwitch(x, y);
14051 else if (element == EL_TIMEGATE_SWITCH ||
14052 element == EL_DC_TIMEGATE_SWITCH)
14054 ActivateTimegateSwitch(x, y);
14056 else if (element == EL_BALLOON_SWITCH_LEFT ||
14057 element == EL_BALLOON_SWITCH_RIGHT ||
14058 element == EL_BALLOON_SWITCH_UP ||
14059 element == EL_BALLOON_SWITCH_DOWN ||
14060 element == EL_BALLOON_SWITCH_NONE ||
14061 element == EL_BALLOON_SWITCH_ANY)
14063 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14064 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14065 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14066 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14067 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14070 else if (element == EL_LAMP)
14072 Feld[x][y] = EL_LAMP_ACTIVE;
14073 local_player->lights_still_needed--;
14075 ResetGfxAnimation(x, y);
14076 TEST_DrawLevelField(x, y);
14078 else if (element == EL_TIME_ORB_FULL)
14080 Feld[x][y] = EL_TIME_ORB_EMPTY;
14082 if (level.time > 0 || level.use_time_orb_bug)
14084 TimeLeft += level.time_orb_time;
14085 game.no_time_limit = FALSE;
14087 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14089 DisplayGameControlValues();
14092 ResetGfxAnimation(x, y);
14093 TEST_DrawLevelField(x, y);
14095 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14096 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14100 game.ball_state = !game.ball_state;
14102 SCAN_PLAYFIELD(xx, yy)
14104 int e = Feld[xx][yy];
14106 if (game.ball_state)
14108 if (e == EL_EMC_MAGIC_BALL)
14109 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14110 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14111 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14115 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14116 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14117 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14118 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14123 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14124 player->index_bit, dig_side);
14126 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14127 player->index_bit, dig_side);
14129 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14130 player->index_bit, dig_side);
14136 if (!PLAYER_SWITCHING(player, x, y))
14138 player->is_switching = TRUE;
14139 player->switch_x = x;
14140 player->switch_y = y;
14142 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14143 player->index_bit, dig_side);
14144 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14145 player->index_bit, dig_side);
14147 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14148 player->index_bit, dig_side);
14149 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14150 player->index_bit, dig_side);
14153 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14154 player->index_bit, dig_side);
14155 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14156 player->index_bit, dig_side);
14158 return MP_NO_ACTION;
14161 player->push_delay = -1;
14163 if (is_player) /* function can also be called by EL_PENGUIN */
14165 if (Feld[x][y] != element) /* really digged/collected something */
14167 player->is_collecting = !player->is_digging;
14168 player->is_active = TRUE;
14175 static boolean DigFieldByCE(int x, int y, int digging_element)
14177 int element = Feld[x][y];
14179 if (!IS_FREE(x, y))
14181 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14182 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14185 /* no element can dig solid indestructible elements */
14186 if (IS_INDESTRUCTIBLE(element) &&
14187 !IS_DIGGABLE(element) &&
14188 !IS_COLLECTIBLE(element))
14191 if (AmoebaNr[x][y] &&
14192 (element == EL_AMOEBA_FULL ||
14193 element == EL_BD_AMOEBA ||
14194 element == EL_AMOEBA_GROWING))
14196 AmoebaCnt[AmoebaNr[x][y]]--;
14197 AmoebaCnt2[AmoebaNr[x][y]]--;
14200 if (IS_MOVING(x, y))
14201 RemoveMovingField(x, y);
14205 TEST_DrawLevelField(x, y);
14208 /* if digged element was about to explode, prevent the explosion */
14209 ExplodeField[x][y] = EX_TYPE_NONE;
14211 PlayLevelSoundAction(x, y, action);
14214 Store[x][y] = EL_EMPTY;
14216 /* this makes it possible to leave the removed element again */
14217 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14218 Store[x][y] = element;
14223 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14225 int jx = player->jx, jy = player->jy;
14226 int x = jx + dx, y = jy + dy;
14227 int snap_direction = (dx == -1 ? MV_LEFT :
14228 dx == +1 ? MV_RIGHT :
14230 dy == +1 ? MV_DOWN : MV_NONE);
14231 boolean can_continue_snapping = (level.continuous_snapping &&
14232 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14234 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14237 if (!player->active || !IN_LEV_FIELD(x, y))
14245 if (player->MovPos == 0)
14246 player->is_pushing = FALSE;
14248 player->is_snapping = FALSE;
14250 if (player->MovPos == 0)
14252 player->is_moving = FALSE;
14253 player->is_digging = FALSE;
14254 player->is_collecting = FALSE;
14260 /* prevent snapping with already pressed snap key when not allowed */
14261 if (player->is_snapping && !can_continue_snapping)
14264 player->MovDir = snap_direction;
14266 if (player->MovPos == 0)
14268 player->is_moving = FALSE;
14269 player->is_digging = FALSE;
14270 player->is_collecting = FALSE;
14273 player->is_dropping = FALSE;
14274 player->is_dropping_pressed = FALSE;
14275 player->drop_pressed_delay = 0;
14277 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14280 player->is_snapping = TRUE;
14281 player->is_active = TRUE;
14283 if (player->MovPos == 0)
14285 player->is_moving = FALSE;
14286 player->is_digging = FALSE;
14287 player->is_collecting = FALSE;
14290 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14291 TEST_DrawLevelField(player->last_jx, player->last_jy);
14293 TEST_DrawLevelField(x, y);
14298 static boolean DropElement(struct PlayerInfo *player)
14300 int old_element, new_element;
14301 int dropx = player->jx, dropy = player->jy;
14302 int drop_direction = player->MovDir;
14303 int drop_side = drop_direction;
14304 int drop_element = get_next_dropped_element(player);
14306 /* do not drop an element on top of another element; when holding drop key
14307 pressed without moving, dropped element must move away before the next
14308 element can be dropped (this is especially important if the next element
14309 is dynamite, which can be placed on background for historical reasons) */
14310 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14313 if (IS_THROWABLE(drop_element))
14315 dropx += GET_DX_FROM_DIR(drop_direction);
14316 dropy += GET_DY_FROM_DIR(drop_direction);
14318 if (!IN_LEV_FIELD(dropx, dropy))
14322 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14323 new_element = drop_element; /* default: no change when dropping */
14325 /* check if player is active, not moving and ready to drop */
14326 if (!player->active || player->MovPos || player->drop_delay > 0)
14329 /* check if player has anything that can be dropped */
14330 if (new_element == EL_UNDEFINED)
14333 /* only set if player has anything that can be dropped */
14334 player->is_dropping_pressed = TRUE;
14336 /* check if drop key was pressed long enough for EM style dynamite */
14337 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14340 /* check if anything can be dropped at the current position */
14341 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14344 /* collected custom elements can only be dropped on empty fields */
14345 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14348 if (old_element != EL_EMPTY)
14349 Back[dropx][dropy] = old_element; /* store old element on this field */
14351 ResetGfxAnimation(dropx, dropy);
14352 ResetRandomAnimationValue(dropx, dropy);
14354 if (player->inventory_size > 0 ||
14355 player->inventory_infinite_element != EL_UNDEFINED)
14357 if (player->inventory_size > 0)
14359 player->inventory_size--;
14361 DrawGameDoorValues();
14363 if (new_element == EL_DYNAMITE)
14364 new_element = EL_DYNAMITE_ACTIVE;
14365 else if (new_element == EL_EM_DYNAMITE)
14366 new_element = EL_EM_DYNAMITE_ACTIVE;
14367 else if (new_element == EL_SP_DISK_RED)
14368 new_element = EL_SP_DISK_RED_ACTIVE;
14371 Feld[dropx][dropy] = new_element;
14373 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14374 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14375 el2img(Feld[dropx][dropy]), 0);
14377 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14379 /* needed if previous element just changed to "empty" in the last frame */
14380 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14382 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14383 player->index_bit, drop_side);
14384 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14386 player->index_bit, drop_side);
14388 TestIfElementTouchesCustomElement(dropx, dropy);
14390 else /* player is dropping a dyna bomb */
14392 player->dynabombs_left--;
14394 Feld[dropx][dropy] = new_element;
14396 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14397 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14398 el2img(Feld[dropx][dropy]), 0);
14400 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14403 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14404 InitField_WithBug1(dropx, dropy, FALSE);
14406 new_element = Feld[dropx][dropy]; /* element might have changed */
14408 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14409 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14411 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14412 MovDir[dropx][dropy] = drop_direction;
14414 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14416 /* do not cause impact style collision by dropping elements that can fall */
14417 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14420 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14421 player->is_dropping = TRUE;
14423 player->drop_pressed_delay = 0;
14424 player->is_dropping_pressed = FALSE;
14426 player->drop_x = dropx;
14427 player->drop_y = dropy;
14432 /* ------------------------------------------------------------------------- */
14433 /* game sound playing functions */
14434 /* ------------------------------------------------------------------------- */
14436 static int *loop_sound_frame = NULL;
14437 static int *loop_sound_volume = NULL;
14439 void InitPlayLevelSound()
14441 int num_sounds = getSoundListSize();
14443 checked_free(loop_sound_frame);
14444 checked_free(loop_sound_volume);
14446 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14447 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14450 static void PlayLevelSound(int x, int y, int nr)
14452 int sx = SCREENX(x), sy = SCREENY(y);
14453 int volume, stereo_position;
14454 int max_distance = 8;
14455 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14457 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14458 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14461 if (!IN_LEV_FIELD(x, y) ||
14462 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14463 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14466 volume = SOUND_MAX_VOLUME;
14468 if (!IN_SCR_FIELD(sx, sy))
14470 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14471 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14473 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14476 stereo_position = (SOUND_MAX_LEFT +
14477 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14478 (SCR_FIELDX + 2 * max_distance));
14480 if (IS_LOOP_SOUND(nr))
14482 /* This assures that quieter loop sounds do not overwrite louder ones,
14483 while restarting sound volume comparison with each new game frame. */
14485 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14488 loop_sound_volume[nr] = volume;
14489 loop_sound_frame[nr] = FrameCounter;
14492 PlaySoundExt(nr, volume, stereo_position, type);
14495 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14497 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14498 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14499 y < LEVELY(BY1) ? LEVELY(BY1) :
14500 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14504 static void PlayLevelSoundAction(int x, int y, int action)
14506 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14509 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14511 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14513 if (sound_effect != SND_UNDEFINED)
14514 PlayLevelSound(x, y, sound_effect);
14517 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14520 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14522 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14523 PlayLevelSound(x, y, sound_effect);
14526 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14528 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14530 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14531 PlayLevelSound(x, y, sound_effect);
14534 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14536 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14538 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14539 StopSound(sound_effect);
14542 static int getLevelMusicNr()
14544 if (levelset.music[level_nr] != MUS_UNDEFINED)
14545 return levelset.music[level_nr]; /* from config file */
14547 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14550 static void FadeLevelSounds()
14555 static void FadeLevelMusic()
14557 int music_nr = getLevelMusicNr();
14558 char *curr_music = getCurrentlyPlayingMusicFilename();
14559 char *next_music = getMusicInfoEntryFilename(music_nr);
14561 if (!strEqual(curr_music, next_music))
14565 void FadeLevelSoundsAndMusic()
14571 static void PlayLevelMusic()
14573 int music_nr = getLevelMusicNr();
14574 char *curr_music = getCurrentlyPlayingMusicFilename();
14575 char *next_music = getMusicInfoEntryFilename(music_nr);
14577 if (!strEqual(curr_music, next_music))
14578 PlayMusic(music_nr);
14581 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14583 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14584 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14585 int x = xx - 1 - offset;
14586 int y = yy - 1 - offset;
14591 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14595 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14599 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14603 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14607 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14611 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14615 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14618 case SAMPLE_android_clone:
14619 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14622 case SAMPLE_android_move:
14623 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14626 case SAMPLE_spring:
14627 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14631 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14635 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14638 case SAMPLE_eater_eat:
14639 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14643 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14646 case SAMPLE_collect:
14647 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14650 case SAMPLE_diamond:
14651 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14654 case SAMPLE_squash:
14655 /* !!! CHECK THIS !!! */
14657 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14659 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14663 case SAMPLE_wonderfall:
14664 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14668 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14672 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14676 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14680 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14684 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14688 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14691 case SAMPLE_wonder:
14692 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14696 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14699 case SAMPLE_exit_open:
14700 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14703 case SAMPLE_exit_leave:
14704 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14707 case SAMPLE_dynamite:
14708 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14712 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14716 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14720 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14724 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14728 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14732 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14736 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14741 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14743 int element = map_element_SP_to_RND(element_sp);
14744 int action = map_action_SP_to_RND(action_sp);
14745 int offset = (setup.sp_show_border_elements ? 0 : 1);
14746 int x = xx - offset;
14747 int y = yy - offset;
14749 PlayLevelSoundElementAction(x, y, element, action);
14752 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14754 int element = map_element_MM_to_RND(element_mm);
14755 int action = map_action_MM_to_RND(action_mm);
14757 int x = xx - offset;
14758 int y = yy - offset;
14760 if (!IS_MM_ELEMENT(element))
14761 element = EL_MM_DEFAULT;
14763 PlayLevelSoundElementAction(x, y, element, action);
14766 void PlaySound_MM(int sound_mm)
14768 int sound = map_sound_MM_to_RND(sound_mm);
14770 if (sound == SND_UNDEFINED)
14776 void PlaySoundLoop_MM(int sound_mm)
14778 int sound = map_sound_MM_to_RND(sound_mm);
14780 if (sound == SND_UNDEFINED)
14783 PlaySoundLoop(sound);
14786 void StopSound_MM(int sound_mm)
14788 int sound = map_sound_MM_to_RND(sound_mm);
14790 if (sound == SND_UNDEFINED)
14796 void RaiseScore(int value)
14798 local_player->score += value;
14800 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14802 DisplayGameControlValues();
14805 void RaiseScoreElement(int element)
14810 case EL_BD_DIAMOND:
14811 case EL_EMERALD_YELLOW:
14812 case EL_EMERALD_RED:
14813 case EL_EMERALD_PURPLE:
14814 case EL_SP_INFOTRON:
14815 RaiseScore(level.score[SC_EMERALD]);
14818 RaiseScore(level.score[SC_DIAMOND]);
14821 RaiseScore(level.score[SC_CRYSTAL]);
14824 RaiseScore(level.score[SC_PEARL]);
14827 case EL_BD_BUTTERFLY:
14828 case EL_SP_ELECTRON:
14829 RaiseScore(level.score[SC_BUG]);
14832 case EL_BD_FIREFLY:
14833 case EL_SP_SNIKSNAK:
14834 RaiseScore(level.score[SC_SPACESHIP]);
14837 case EL_DARK_YAMYAM:
14838 RaiseScore(level.score[SC_YAMYAM]);
14841 RaiseScore(level.score[SC_ROBOT]);
14844 RaiseScore(level.score[SC_PACMAN]);
14847 RaiseScore(level.score[SC_NUT]);
14850 case EL_EM_DYNAMITE:
14851 case EL_SP_DISK_RED:
14852 case EL_DYNABOMB_INCREASE_NUMBER:
14853 case EL_DYNABOMB_INCREASE_SIZE:
14854 case EL_DYNABOMB_INCREASE_POWER:
14855 RaiseScore(level.score[SC_DYNAMITE]);
14857 case EL_SHIELD_NORMAL:
14858 case EL_SHIELD_DEADLY:
14859 RaiseScore(level.score[SC_SHIELD]);
14861 case EL_EXTRA_TIME:
14862 RaiseScore(level.extra_time_score);
14876 case EL_DC_KEY_WHITE:
14877 RaiseScore(level.score[SC_KEY]);
14880 RaiseScore(element_info[element].collect_score);
14885 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14887 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14889 /* closing door required in case of envelope style request dialogs */
14891 CloseDoor(DOOR_CLOSE_1);
14893 if (network.enabled)
14894 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14898 FadeSkipNextFadeIn();
14900 SetGameStatus(GAME_MODE_MAIN);
14905 else /* continue playing the game */
14907 if (tape.playing && tape.deactivate_display)
14908 TapeDeactivateDisplayOff(TRUE);
14910 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14912 if (tape.playing && tape.deactivate_display)
14913 TapeDeactivateDisplayOn();
14917 void RequestQuitGame(boolean ask_if_really_quit)
14919 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14920 boolean skip_request = AllPlayersGone || quick_quit;
14922 RequestQuitGameExt(skip_request, quick_quit,
14923 "Do you really want to quit the game?");
14926 void RequestRestartGame(char *message)
14928 game.restart_game_message = NULL;
14930 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14932 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14936 SetGameStatus(GAME_MODE_MAIN);
14943 /* ------------------------------------------------------------------------- */
14944 /* random generator functions */
14945 /* ------------------------------------------------------------------------- */
14947 unsigned int InitEngineRandom_RND(int seed)
14949 game.num_random_calls = 0;
14951 return InitEngineRandom(seed);
14954 unsigned int RND(int max)
14958 game.num_random_calls++;
14960 return GetEngineRandom(max);
14967 /* ------------------------------------------------------------------------- */
14968 /* game engine snapshot handling functions */
14969 /* ------------------------------------------------------------------------- */
14971 struct EngineSnapshotInfo
14973 /* runtime values for custom element collect score */
14974 int collect_score[NUM_CUSTOM_ELEMENTS];
14976 /* runtime values for group element choice position */
14977 int choice_pos[NUM_GROUP_ELEMENTS];
14979 /* runtime values for belt position animations */
14980 int belt_graphic[4][NUM_BELT_PARTS];
14981 int belt_anim_mode[4][NUM_BELT_PARTS];
14984 static struct EngineSnapshotInfo engine_snapshot_rnd;
14985 static char *snapshot_level_identifier = NULL;
14986 static int snapshot_level_nr = -1;
14988 static void SaveEngineSnapshotValues_RND()
14990 static int belt_base_active_element[4] =
14992 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14993 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14994 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14995 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14999 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15001 int element = EL_CUSTOM_START + i;
15003 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15006 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15008 int element = EL_GROUP_START + i;
15010 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15013 for (i = 0; i < 4; i++)
15015 for (j = 0; j < NUM_BELT_PARTS; j++)
15017 int element = belt_base_active_element[i] + j;
15018 int graphic = el2img(element);
15019 int anim_mode = graphic_info[graphic].anim_mode;
15021 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15022 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15027 static void LoadEngineSnapshotValues_RND()
15029 unsigned int num_random_calls = game.num_random_calls;
15032 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15034 int element = EL_CUSTOM_START + i;
15036 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15039 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15041 int element = EL_GROUP_START + i;
15043 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15046 for (i = 0; i < 4; i++)
15048 for (j = 0; j < NUM_BELT_PARTS; j++)
15050 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15051 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15053 graphic_info[graphic].anim_mode = anim_mode;
15057 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15059 InitRND(tape.random_seed);
15060 for (i = 0; i < num_random_calls; i++)
15064 if (game.num_random_calls != num_random_calls)
15066 Error(ERR_INFO, "number of random calls out of sync");
15067 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15068 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15069 Error(ERR_EXIT, "this should not happen -- please debug");
15073 void FreeEngineSnapshotSingle()
15075 FreeSnapshotSingle();
15077 setString(&snapshot_level_identifier, NULL);
15078 snapshot_level_nr = -1;
15081 void FreeEngineSnapshotList()
15083 FreeSnapshotList();
15086 ListNode *SaveEngineSnapshotBuffers()
15088 ListNode *buffers = NULL;
15090 /* copy some special values to a structure better suited for the snapshot */
15092 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15093 SaveEngineSnapshotValues_RND();
15094 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15095 SaveEngineSnapshotValues_EM();
15096 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15097 SaveEngineSnapshotValues_SP(&buffers);
15098 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15099 SaveEngineSnapshotValues_MM(&buffers);
15101 /* save values stored in special snapshot structure */
15103 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15104 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15105 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15106 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15107 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15108 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15109 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15110 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15112 /* save further RND engine values */
15114 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15115 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15116 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15118 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15119 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15120 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15121 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15123 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15124 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15125 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15126 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15127 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15129 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15130 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15131 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15133 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15135 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15137 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15138 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15140 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15141 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15142 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15143 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15144 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15145 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15146 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15147 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15148 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15149 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15150 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15151 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15152 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15153 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15154 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15155 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15156 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15159 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15164 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15170 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15171 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15173 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15175 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15176 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15179 ListNode *node = engine_snapshot_list_rnd;
15182 while (node != NULL)
15184 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15189 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15195 void SaveEngineSnapshotSingle()
15197 ListNode *buffers = SaveEngineSnapshotBuffers();
15199 /* finally save all snapshot buffers to single snapshot */
15200 SaveSnapshotSingle(buffers);
15202 /* save level identification information */
15203 setString(&snapshot_level_identifier, leveldir_current->identifier);
15204 snapshot_level_nr = level_nr;
15207 boolean CheckSaveEngineSnapshotToList()
15209 boolean save_snapshot =
15210 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15211 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15212 game.snapshot.changed_action) ||
15213 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15214 game.snapshot.collected_item));
15216 game.snapshot.changed_action = FALSE;
15217 game.snapshot.collected_item = FALSE;
15218 game.snapshot.save_snapshot = save_snapshot;
15220 return save_snapshot;
15223 void SaveEngineSnapshotToList()
15225 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15229 ListNode *buffers = SaveEngineSnapshotBuffers();
15231 /* finally save all snapshot buffers to snapshot list */
15232 SaveSnapshotToList(buffers);
15235 void SaveEngineSnapshotToListInitial()
15237 FreeEngineSnapshotList();
15239 SaveEngineSnapshotToList();
15242 void LoadEngineSnapshotValues()
15244 /* restore special values from snapshot structure */
15246 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15247 LoadEngineSnapshotValues_RND();
15248 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15249 LoadEngineSnapshotValues_EM();
15250 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15251 LoadEngineSnapshotValues_SP();
15252 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15253 LoadEngineSnapshotValues_MM();
15256 void LoadEngineSnapshotSingle()
15258 LoadSnapshotSingle();
15260 LoadEngineSnapshotValues();
15263 void LoadEngineSnapshot_Undo(int steps)
15265 LoadSnapshotFromList_Older(steps);
15267 LoadEngineSnapshotValues();
15270 void LoadEngineSnapshot_Redo(int steps)
15272 LoadSnapshotFromList_Newer(steps);
15274 LoadEngineSnapshotValues();
15277 boolean CheckEngineSnapshotSingle()
15279 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15280 snapshot_level_nr == level_nr);
15283 boolean CheckEngineSnapshotList()
15285 return CheckSnapshotList();
15289 /* ---------- new game button stuff ---------------------------------------- */
15296 boolean *setup_value;
15297 boolean allowed_on_tape;
15299 } gamebutton_info[NUM_GAME_BUTTONS] =
15302 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15303 GAME_CTRL_ID_STOP, NULL,
15307 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15308 GAME_CTRL_ID_PAUSE, NULL,
15312 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15313 GAME_CTRL_ID_PLAY, NULL,
15317 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15318 GAME_CTRL_ID_UNDO, NULL,
15322 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15323 GAME_CTRL_ID_REDO, NULL,
15327 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15328 GAME_CTRL_ID_SAVE, NULL,
15332 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15333 GAME_CTRL_ID_PAUSE2, NULL,
15337 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15338 GAME_CTRL_ID_LOAD, NULL,
15342 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15343 GAME_CTRL_ID_PANEL_STOP, NULL,
15347 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15348 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15349 FALSE, "pause game"
15352 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15353 GAME_CTRL_ID_PANEL_PLAY, NULL,
15357 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15358 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15359 TRUE, "background music on/off"
15362 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15363 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15364 TRUE, "sound loops on/off"
15367 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15368 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15369 TRUE, "normal sounds on/off"
15372 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15373 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15374 FALSE, "background music on/off"
15377 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15378 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15379 FALSE, "sound loops on/off"
15382 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15383 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15384 FALSE, "normal sounds on/off"
15388 void CreateGameButtons()
15392 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15394 int graphic = gamebutton_info[i].graphic;
15395 struct GraphicInfo *gfx = &graphic_info[graphic];
15396 struct XY *pos = gamebutton_info[i].pos;
15397 struct GadgetInfo *gi;
15400 unsigned int event_mask;
15401 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15402 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15403 int base_x = (on_tape ? VX : DX);
15404 int base_y = (on_tape ? VY : DY);
15405 int gd_x = gfx->src_x;
15406 int gd_y = gfx->src_y;
15407 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15408 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15409 int gd_xa = gfx->src_x + gfx->active_xoffset;
15410 int gd_ya = gfx->src_y + gfx->active_yoffset;
15411 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15412 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15415 if (gfx->bitmap == NULL)
15417 game_gadget[id] = NULL;
15422 if (id == GAME_CTRL_ID_STOP ||
15423 id == GAME_CTRL_ID_PANEL_STOP ||
15424 id == GAME_CTRL_ID_PLAY ||
15425 id == GAME_CTRL_ID_PANEL_PLAY ||
15426 id == GAME_CTRL_ID_SAVE ||
15427 id == GAME_CTRL_ID_LOAD)
15429 button_type = GD_TYPE_NORMAL_BUTTON;
15431 event_mask = GD_EVENT_RELEASED;
15433 else if (id == GAME_CTRL_ID_UNDO ||
15434 id == GAME_CTRL_ID_REDO)
15436 button_type = GD_TYPE_NORMAL_BUTTON;
15438 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15442 button_type = GD_TYPE_CHECK_BUTTON;
15443 checked = (gamebutton_info[i].setup_value != NULL ?
15444 *gamebutton_info[i].setup_value : FALSE);
15445 event_mask = GD_EVENT_PRESSED;
15448 gi = CreateGadget(GDI_CUSTOM_ID, id,
15449 GDI_IMAGE_ID, graphic,
15450 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15451 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15452 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15453 GDI_WIDTH, gfx->width,
15454 GDI_HEIGHT, gfx->height,
15455 GDI_TYPE, button_type,
15456 GDI_STATE, GD_BUTTON_UNPRESSED,
15457 GDI_CHECKED, checked,
15458 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15459 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15460 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15461 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15462 GDI_DIRECT_DRAW, FALSE,
15463 GDI_EVENT_MASK, event_mask,
15464 GDI_CALLBACK_ACTION, HandleGameButtons,
15468 Error(ERR_EXIT, "cannot create gadget");
15470 game_gadget[id] = gi;
15474 void FreeGameButtons()
15478 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15479 FreeGadget(game_gadget[i]);
15482 static void UnmapGameButtonsAtSamePosition(int id)
15486 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15488 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15489 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15490 UnmapGadget(game_gadget[i]);
15493 static void UnmapGameButtonsAtSamePosition_All()
15495 if (setup.show_snapshot_buttons)
15497 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15498 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15499 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15503 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15504 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15505 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15507 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15508 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15509 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15513 static void MapGameButtonsAtSamePosition(int id)
15517 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15519 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15520 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15521 MapGadget(game_gadget[i]);
15523 UnmapGameButtonsAtSamePosition_All();
15526 void MapUndoRedoButtons()
15528 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15529 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15531 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15532 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15534 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15537 void UnmapUndoRedoButtons()
15539 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15540 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15542 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15543 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15545 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15548 void MapGameButtonsExt(boolean on_tape)
15552 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15553 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15554 i != GAME_CTRL_ID_UNDO &&
15555 i != GAME_CTRL_ID_REDO)
15556 MapGadget(game_gadget[i]);
15558 UnmapGameButtonsAtSamePosition_All();
15560 RedrawGameButtons();
15563 void UnmapGameButtonsExt(boolean on_tape)
15567 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15568 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15569 UnmapGadget(game_gadget[i]);
15572 void RedrawGameButtonsExt(boolean on_tape)
15576 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15577 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15578 RedrawGadget(game_gadget[i]);
15580 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15581 redraw_mask &= ~REDRAW_ALL;
15584 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15589 gi->checked = state;
15592 void RedrawSoundButtonGadget(int id)
15594 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15595 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15596 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15597 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15598 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15599 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15602 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15603 RedrawGadget(game_gadget[id2]);
15606 void MapGameButtons()
15608 MapGameButtonsExt(FALSE);
15611 void UnmapGameButtons()
15613 UnmapGameButtonsExt(FALSE);
15616 void RedrawGameButtons()
15618 RedrawGameButtonsExt(FALSE);
15621 void MapGameButtonsOnTape()
15623 MapGameButtonsExt(TRUE);
15626 void UnmapGameButtonsOnTape()
15628 UnmapGameButtonsExt(TRUE);
15631 void RedrawGameButtonsOnTape()
15633 RedrawGameButtonsExt(TRUE);
15636 void GameUndoRedoExt()
15638 ClearPlayerAction();
15640 tape.pausing = TRUE;
15643 UpdateAndDisplayGameControlValues();
15645 DrawCompleteVideoDisplay();
15646 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15647 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15648 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15653 void GameUndo(int steps)
15655 if (!CheckEngineSnapshotList())
15658 LoadEngineSnapshot_Undo(steps);
15663 void GameRedo(int steps)
15665 if (!CheckEngineSnapshotList())
15668 LoadEngineSnapshot_Redo(steps);
15673 static void HandleGameButtonsExt(int id, int button)
15675 static boolean game_undo_executed = FALSE;
15676 int steps = BUTTON_STEPSIZE(button);
15677 boolean handle_game_buttons =
15678 (game_status == GAME_MODE_PLAYING ||
15679 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15681 if (!handle_game_buttons)
15686 case GAME_CTRL_ID_STOP:
15687 case GAME_CTRL_ID_PANEL_STOP:
15688 if (game_status == GAME_MODE_MAIN)
15694 RequestQuitGame(TRUE);
15698 case GAME_CTRL_ID_PAUSE:
15699 case GAME_CTRL_ID_PAUSE2:
15700 case GAME_CTRL_ID_PANEL_PAUSE:
15701 if (network.enabled && game_status == GAME_MODE_PLAYING)
15704 SendToServer_ContinuePlaying();
15706 SendToServer_PausePlaying();
15709 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15711 game_undo_executed = FALSE;
15715 case GAME_CTRL_ID_PLAY:
15716 case GAME_CTRL_ID_PANEL_PLAY:
15717 if (game_status == GAME_MODE_MAIN)
15719 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15721 else if (tape.pausing)
15723 if (network.enabled)
15724 SendToServer_ContinuePlaying();
15726 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15730 case GAME_CTRL_ID_UNDO:
15731 // Important: When using "save snapshot when collecting an item" mode,
15732 // load last (current) snapshot for first "undo" after pressing "pause"
15733 // (else the last-but-one snapshot would be loaded, because the snapshot
15734 // pointer already points to the last snapshot when pressing "pause",
15735 // which is fine for "every step/move" mode, but not for "every collect")
15736 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15737 !game_undo_executed)
15740 game_undo_executed = TRUE;
15745 case GAME_CTRL_ID_REDO:
15749 case GAME_CTRL_ID_SAVE:
15753 case GAME_CTRL_ID_LOAD:
15757 case SOUND_CTRL_ID_MUSIC:
15758 case SOUND_CTRL_ID_PANEL_MUSIC:
15759 if (setup.sound_music)
15761 setup.sound_music = FALSE;
15765 else if (audio.music_available)
15767 setup.sound = setup.sound_music = TRUE;
15769 SetAudioMode(setup.sound);
15771 if (game_status == GAME_MODE_PLAYING)
15775 RedrawSoundButtonGadget(id);
15779 case SOUND_CTRL_ID_LOOPS:
15780 case SOUND_CTRL_ID_PANEL_LOOPS:
15781 if (setup.sound_loops)
15782 setup.sound_loops = FALSE;
15783 else if (audio.loops_available)
15785 setup.sound = setup.sound_loops = TRUE;
15787 SetAudioMode(setup.sound);
15790 RedrawSoundButtonGadget(id);
15794 case SOUND_CTRL_ID_SIMPLE:
15795 case SOUND_CTRL_ID_PANEL_SIMPLE:
15796 if (setup.sound_simple)
15797 setup.sound_simple = FALSE;
15798 else if (audio.sound_available)
15800 setup.sound = setup.sound_simple = TRUE;
15802 SetAudioMode(setup.sound);
15805 RedrawSoundButtonGadget(id);
15814 static void HandleGameButtons(struct GadgetInfo *gi)
15816 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15819 void HandleSoundButtonKeys(Key key)
15821 if (key == setup.shortcut.sound_simple)
15822 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15823 else if (key == setup.shortcut.sound_loops)
15824 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15825 else if (key == setup.shortcut.sound_music)
15826 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);