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(void);
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 *);
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1125 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1127 if (recursion_loop_detected) \
1130 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1132 recursion_loop_detected = TRUE; \
1133 recursion_loop_element = (e); \
1136 recursion_loop_depth++; \
1139 #define RECURSION_LOOP_DETECTION_END() \
1141 recursion_loop_depth--; \
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1148 static int map_player_action[MAX_PLAYERS];
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after */
1153 /* a specified time, eventually calling a function when changing */
1154 /* ------------------------------------------------------------------------- */
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1174 struct ChangingElementInfo
1179 void (*pre_change_function)(int x, int y);
1180 void (*change_function)(int x, int y);
1181 void (*post_change_function)(int x, int y);
1184 static struct ChangingElementInfo change_delay_list[] =
1219 EL_STEEL_EXIT_OPENING,
1227 EL_STEEL_EXIT_CLOSING,
1228 EL_STEEL_EXIT_CLOSED,
1251 EL_EM_STEEL_EXIT_OPENING,
1252 EL_EM_STEEL_EXIT_OPEN,
1259 EL_EM_STEEL_EXIT_CLOSING,
1283 EL_SWITCHGATE_OPENING,
1291 EL_SWITCHGATE_CLOSING,
1292 EL_SWITCHGATE_CLOSED,
1299 EL_TIMEGATE_OPENING,
1307 EL_TIMEGATE_CLOSING,
1316 EL_ACID_SPLASH_LEFT,
1324 EL_ACID_SPLASH_RIGHT,
1333 EL_SP_BUGGY_BASE_ACTIVATING,
1340 EL_SP_BUGGY_BASE_ACTIVATING,
1341 EL_SP_BUGGY_BASE_ACTIVE,
1348 EL_SP_BUGGY_BASE_ACTIVE,
1372 EL_ROBOT_WHEEL_ACTIVE,
1380 EL_TIMEGATE_SWITCH_ACTIVE,
1388 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389 EL_DC_TIMEGATE_SWITCH,
1396 EL_EMC_MAGIC_BALL_ACTIVE,
1397 EL_EMC_MAGIC_BALL_ACTIVE,
1404 EL_EMC_SPRING_BUMPER_ACTIVE,
1405 EL_EMC_SPRING_BUMPER,
1412 EL_DIAGONAL_SHRINKING,
1420 EL_DIAGONAL_GROWING,
1441 int push_delay_fixed, push_delay_random;
1445 { EL_SPRING, 0, 0 },
1446 { EL_BALLOON, 0, 0 },
1448 { EL_SOKOBAN_OBJECT, 2, 0 },
1449 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1450 { EL_SATELLITE, 2, 0 },
1451 { EL_SP_DISK_YELLOW, 2, 0 },
1453 { EL_UNDEFINED, 0, 0 },
1461 move_stepsize_list[] =
1463 { EL_AMOEBA_DROP, 2 },
1464 { EL_AMOEBA_DROPPING, 2 },
1465 { EL_QUICKSAND_FILLING, 1 },
1466 { EL_QUICKSAND_EMPTYING, 1 },
1467 { EL_QUICKSAND_FAST_FILLING, 2 },
1468 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469 { EL_MAGIC_WALL_FILLING, 2 },
1470 { EL_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_BD_MAGIC_WALL_FILLING, 2 },
1472 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1473 { EL_DC_MAGIC_WALL_FILLING, 2 },
1474 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1476 { EL_UNDEFINED, 0 },
1484 collect_count_list[] =
1487 { EL_BD_DIAMOND, 1 },
1488 { EL_EMERALD_YELLOW, 1 },
1489 { EL_EMERALD_RED, 1 },
1490 { EL_EMERALD_PURPLE, 1 },
1492 { EL_SP_INFOTRON, 1 },
1496 { EL_UNDEFINED, 0 },
1504 access_direction_list[] =
1506 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1508 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1509 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1510 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1511 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1512 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1513 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1514 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1515 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1516 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1518 { EL_SP_PORT_LEFT, MV_RIGHT },
1519 { EL_SP_PORT_RIGHT, MV_LEFT },
1520 { EL_SP_PORT_UP, MV_DOWN },
1521 { EL_SP_PORT_DOWN, MV_UP },
1522 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1523 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1524 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1526 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1527 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1528 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1529 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1530 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1531 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1532 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1533 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1534 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1535 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1536 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1538 { EL_UNDEFINED, MV_NONE }
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1546 IS_JUST_CHANGING(x, y))
1548 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1556 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1557 (y) >= 0 && (y) <= lev_fieldy - 1; \
1558 (y) += playfield_scan_delta_y) \
1559 for ((x) = playfield_scan_start_x; \
1560 (x) >= 0 && (x) <= lev_fieldx - 1; \
1561 (x) += playfield_scan_delta_x)
1564 void DEBUG_SetMaximumDynamite()
1568 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570 local_player->inventory_element[local_player->inventory_size++] =
1575 static void InitPlayfieldScanModeVars()
1577 if (game.use_reverse_scan_direction)
1579 playfield_scan_start_x = lev_fieldx - 1;
1580 playfield_scan_start_y = lev_fieldy - 1;
1582 playfield_scan_delta_x = -1;
1583 playfield_scan_delta_y = -1;
1587 playfield_scan_start_x = 0;
1588 playfield_scan_start_y = 0;
1590 playfield_scan_delta_x = 1;
1591 playfield_scan_delta_y = 1;
1595 static void InitPlayfieldScanMode(int mode)
1597 game.use_reverse_scan_direction =
1598 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600 InitPlayfieldScanModeVars();
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1606 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608 /* make sure that stepsize value is always a power of 2 */
1609 move_stepsize = (1 << log_2(move_stepsize));
1611 return TILEX / move_stepsize;
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1617 int player_nr = player->index_nr;
1618 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621 /* do no immediately change move delay -- the player might just be moving */
1622 player->move_delay_value_next = move_delay;
1624 /* information if player can move must be set separately */
1625 player->cannot_move = cannot_move;
1629 player->move_delay = game.initial_move_delay[player_nr];
1630 player->move_delay_value = game.initial_move_delay_value[player_nr];
1632 player->move_delay_value_next = -1;
1634 player->move_delay_reset_counter = 0;
1638 void GetPlayerConfig()
1640 GameFrameDelay = setup.game_frame_delay;
1642 if (!audio.sound_available)
1643 setup.sound_simple = FALSE;
1645 if (!audio.loops_available)
1646 setup.sound_loops = FALSE;
1648 if (!audio.music_available)
1649 setup.sound_music = FALSE;
1651 if (!video.fullscreen_available)
1652 setup.fullscreen = FALSE;
1654 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656 SetAudioMode(setup.sound);
1659 int GetElementFromGroupElement(int element)
1661 if (IS_GROUP_ELEMENT(element))
1663 struct ElementGroupInfo *group = element_info[element].group;
1664 int last_anim_random_frame = gfx.anim_random_frame;
1667 if (group->choice_mode == ANIM_RANDOM)
1668 gfx.anim_random_frame = RND(group->num_elements_resolved);
1670 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671 group->choice_mode, 0,
1674 if (group->choice_mode == ANIM_RANDOM)
1675 gfx.anim_random_frame = last_anim_random_frame;
1677 group->choice_pos++;
1679 element = group->element_resolved[element_pos];
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 if (element == EL_SP_MURPHY)
1691 if (stored_player[0].present)
1693 Feld[x][y] = EL_SP_MURPHY_CLONE;
1699 stored_player[0].initial_element = element;
1700 stored_player[0].use_murphy = TRUE;
1702 if (!level.use_artwork_element[0])
1703 stored_player[0].artwork_element = EL_SP_MURPHY;
1706 Feld[x][y] = EL_PLAYER_1;
1712 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713 int jx = player->jx, jy = player->jy;
1715 player->present = TRUE;
1717 player->block_last_field = (element == EL_SP_MURPHY ?
1718 level.sp_block_last_field :
1719 level.block_last_field);
1721 /* ---------- initialize player's last field block delay --------------- */
1723 /* always start with reliable default value (no adjustment needed) */
1724 player->block_delay_adjustment = 0;
1726 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727 if (player->block_last_field && element == EL_SP_MURPHY)
1728 player->block_delay_adjustment = 1;
1730 /* special case 2: in game engines before 3.1.1, blocking was different */
1731 if (game.use_block_last_field_bug)
1732 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734 if (!network.enabled || player->connected_network)
1736 player->active = TRUE;
1738 /* remove potentially duplicate players */
1739 if (StorePlayer[jx][jy] == Feld[x][y])
1740 StorePlayer[jx][jy] = 0;
1742 StorePlayer[x][y] = Feld[x][y];
1744 #if DEBUG_INIT_PLAYER
1747 printf("- player element %d activated", player->element_nr);
1748 printf(" (local player is %d and currently %s)\n",
1749 local_player->element_nr,
1750 local_player->active ? "active" : "not active");
1755 Feld[x][y] = EL_EMPTY;
1757 player->jx = player->last_jx = x;
1758 player->jy = player->last_jy = y;
1763 int player_nr = GET_PLAYER_NR(element);
1764 struct PlayerInfo *player = &stored_player[player_nr];
1766 if (player->active && player->killed)
1767 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1771 static void InitField(int x, int y, boolean init_game)
1773 int element = Feld[x][y];
1782 InitPlayerField(x, y, element, init_game);
1785 case EL_SOKOBAN_FIELD_PLAYER:
1786 element = Feld[x][y] = EL_PLAYER_1;
1787 InitField(x, y, init_game);
1789 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790 InitField(x, y, init_game);
1793 case EL_SOKOBAN_FIELD_EMPTY:
1794 local_player->sokobanfields_still_needed++;
1798 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1816 case EL_SPACESHIP_RIGHT:
1817 case EL_SPACESHIP_UP:
1818 case EL_SPACESHIP_LEFT:
1819 case EL_SPACESHIP_DOWN:
1820 case EL_BD_BUTTERFLY:
1821 case EL_BD_BUTTERFLY_RIGHT:
1822 case EL_BD_BUTTERFLY_UP:
1823 case EL_BD_BUTTERFLY_LEFT:
1824 case EL_BD_BUTTERFLY_DOWN:
1826 case EL_BD_FIREFLY_RIGHT:
1827 case EL_BD_FIREFLY_UP:
1828 case EL_BD_FIREFLY_LEFT:
1829 case EL_BD_FIREFLY_DOWN:
1830 case EL_PACMAN_RIGHT:
1832 case EL_PACMAN_LEFT:
1833 case EL_PACMAN_DOWN:
1835 case EL_YAMYAM_LEFT:
1836 case EL_YAMYAM_RIGHT:
1838 case EL_YAMYAM_DOWN:
1839 case EL_DARK_YAMYAM:
1842 case EL_SP_SNIKSNAK:
1843 case EL_SP_ELECTRON:
1852 case EL_AMOEBA_FULL:
1857 case EL_AMOEBA_DROP:
1858 if (y == lev_fieldy - 1)
1860 Feld[x][y] = EL_AMOEBA_GROWING;
1861 Store[x][y] = EL_AMOEBA_WET;
1865 case EL_DYNAMITE_ACTIVE:
1866 case EL_SP_DISK_RED_ACTIVE:
1867 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871 MovDelay[x][y] = 96;
1874 case EL_EM_DYNAMITE_ACTIVE:
1875 MovDelay[x][y] = 32;
1879 local_player->lights_still_needed++;
1883 local_player->friends_still_needed++;
1888 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1891 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1905 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1911 game.belt_dir[belt_nr] = belt_dir;
1912 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914 else /* more than one switch -- set it like the first switch */
1916 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1921 case EL_LIGHT_SWITCH_ACTIVE:
1923 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1926 case EL_INVISIBLE_STEELWALL:
1927 case EL_INVISIBLE_WALL:
1928 case EL_INVISIBLE_SAND:
1929 if (game.light_time_left > 0 ||
1930 game.lenses_time_left > 0)
1931 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1934 case EL_EMC_MAGIC_BALL:
1935 if (game.ball_state)
1936 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1939 case EL_EMC_MAGIC_BALL_SWITCH:
1940 if (game.ball_state)
1941 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1944 case EL_TRIGGER_PLAYER:
1945 case EL_TRIGGER_ELEMENT:
1946 case EL_TRIGGER_CE_VALUE:
1947 case EL_TRIGGER_CE_SCORE:
1949 case EL_ANY_ELEMENT:
1950 case EL_CURRENT_CE_VALUE:
1951 case EL_CURRENT_CE_SCORE:
1968 /* reference elements should not be used on the playfield */
1969 Feld[x][y] = EL_EMPTY;
1973 if (IS_CUSTOM_ELEMENT(element))
1975 if (CAN_MOVE(element))
1978 if (!element_info[element].use_last_ce_value || init_game)
1979 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981 else if (IS_GROUP_ELEMENT(element))
1983 Feld[x][y] = GetElementFromGroupElement(element);
1985 InitField(x, y, init_game);
1992 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 InitField(x, y, init_game);
1999 /* not needed to call InitMovDir() -- already done by InitField()! */
2000 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001 CAN_MOVE(Feld[x][y]))
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 int old_element = Feld[x][y];
2009 InitField(x, y, init_game);
2011 /* not needed to call InitMovDir() -- already done by InitField()! */
2012 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013 CAN_MOVE(old_element) &&
2014 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2017 /* this case is in fact a combination of not less than three bugs:
2018 first, it calls InitMovDir() for elements that can move, although this is
2019 already done by InitField(); then, it checks the element that was at this
2020 field _before_ the call to InitField() (which can change it); lastly, it
2021 was not called for "mole with direction" elements, which were treated as
2022 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2026 static int get_key_element_from_nr(int key_nr)
2028 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030 EL_EM_KEY_1 : EL_KEY_1);
2032 return key_base_element + key_nr;
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2037 return (player->inventory_size > 0 ?
2038 player->inventory_element[player->inventory_size - 1] :
2039 player->inventory_infinite_element != EL_UNDEFINED ?
2040 player->inventory_infinite_element :
2041 player->dynabombs_left > 0 ?
2042 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 /* pos >= 0: get element from bottom of the stack;
2049 pos < 0: get element from top of the stack */
2053 int min_inventory_size = -pos;
2054 int inventory_pos = player->inventory_size - min_inventory_size;
2055 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057 return (player->inventory_size >= min_inventory_size ?
2058 player->inventory_element[inventory_pos] :
2059 player->inventory_infinite_element != EL_UNDEFINED ?
2060 player->inventory_infinite_element :
2061 player->dynabombs_left >= min_dynabombs_left ?
2062 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2067 int min_dynabombs_left = pos + 1;
2068 int min_inventory_size = pos + 1 - player->dynabombs_left;
2069 int inventory_pos = pos - player->dynabombs_left;
2071 return (player->inventory_infinite_element != EL_UNDEFINED ?
2072 player->inventory_infinite_element :
2073 player->dynabombs_left >= min_dynabombs_left ?
2074 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075 player->inventory_size >= min_inventory_size ?
2076 player->inventory_element[inventory_pos] :
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2087 if (gpo1->sort_priority != gpo2->sort_priority)
2088 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090 compare_result = gpo1->nr - gpo2->nr;
2092 return compare_result;
2095 int getPlayerInventorySize(int player_nr)
2097 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098 return level.native_em_level->ply[player_nr]->dynamite;
2099 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100 return level.native_sp_level->game_sp->red_disk_count;
2102 return stored_player[player_nr].inventory_size;
2105 void InitGameControlValues()
2109 for (i = 0; game_panel_controls[i].nr != -1; i++)
2111 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113 struct TextPosInfo *pos = gpc->pos;
2115 int type = gpc->type;
2119 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120 Error(ERR_EXIT, "this should not happen -- please debug");
2123 /* force update of game controls after initialization */
2124 gpc->value = gpc->last_value = -1;
2125 gpc->frame = gpc->last_frame = -1;
2126 gpc->gfx_frame = -1;
2128 /* determine panel value width for later calculation of alignment */
2129 if (type == TYPE_INTEGER || type == TYPE_STRING)
2131 pos->width = pos->size * getFontWidth(pos->font);
2132 pos->height = getFontHeight(pos->font);
2134 else if (type == TYPE_ELEMENT)
2136 pos->width = pos->size;
2137 pos->height = pos->size;
2140 /* fill structure for game panel draw order */
2142 gpo->sort_priority = pos->sort_priority;
2145 /* sort game panel controls according to sort_priority and control number */
2146 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2150 void UpdatePlayfieldElementCount()
2152 boolean use_element_count = FALSE;
2155 /* first check if it is needed at all to calculate playfield element count */
2156 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158 use_element_count = TRUE;
2160 if (!use_element_count)
2163 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164 element_info[i].element_count = 0;
2166 SCAN_PLAYFIELD(x, y)
2168 element_info[Feld[x][y]].element_count++;
2171 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173 if (IS_IN_GROUP(j, i))
2174 element_info[EL_GROUP_START + i].element_count +=
2175 element_info[j].element_count;
2178 void UpdateGameControlValues()
2181 int time = (local_player->LevelSolved ?
2182 local_player->LevelSolved_CountingTime :
2183 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184 level.native_em_level->lev->time :
2185 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186 level.native_sp_level->game_sp->time_played :
2187 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188 game_mm.energy_left :
2189 game.no_time_limit ? TimePlayed : TimeLeft);
2190 int score = (local_player->LevelSolved ?
2191 local_player->LevelSolved_CountingScore :
2192 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193 level.native_em_level->lev->score :
2194 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195 level.native_sp_level->game_sp->score :
2196 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198 local_player->score);
2199 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200 level.native_em_level->lev->required :
2201 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202 level.native_sp_level->game_sp->infotrons_still_needed :
2203 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204 game_mm.kettles_still_needed :
2205 local_player->gems_still_needed);
2206 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207 level.native_em_level->lev->required > 0 :
2208 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211 game_mm.kettles_still_needed > 0 ||
2212 game_mm.lights_still_needed > 0 :
2213 local_player->gems_still_needed > 0 ||
2214 local_player->sokobanfields_still_needed > 0 ||
2215 local_player->lights_still_needed > 0);
2216 int health = (local_player->LevelSolved ?
2217 local_player->LevelSolved_CountingHealth :
2218 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219 MM_HEALTH(game_mm.laser_overload_value) :
2220 local_player->health);
2222 UpdatePlayfieldElementCount();
2224 /* update game panel control values */
2226 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2229 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230 for (i = 0; i < MAX_NUM_KEYS; i++)
2231 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2235 if (game.centered_player_nr == -1)
2237 for (i = 0; i < MAX_PLAYERS; i++)
2239 /* only one player in Supaplex game engine */
2240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243 for (k = 0; k < MAX_NUM_KEYS; k++)
2245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2247 if (level.native_em_level->ply[i]->keys & (1 << k))
2248 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249 get_key_element_from_nr(k);
2251 else if (stored_player[i].key[k])
2252 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253 get_key_element_from_nr(k);
2256 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257 getPlayerInventorySize(i);
2259 if (stored_player[i].num_white_keys > 0)
2260 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264 stored_player[i].num_white_keys;
2269 int player_nr = game.centered_player_nr;
2271 for (k = 0; k < MAX_NUM_KEYS; k++)
2273 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2275 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277 get_key_element_from_nr(k);
2279 else if (stored_player[player_nr].key[k])
2280 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281 get_key_element_from_nr(k);
2284 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 getPlayerInventorySize(player_nr);
2287 if (stored_player[player_nr].num_white_keys > 0)
2288 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2290 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291 stored_player[player_nr].num_white_keys;
2294 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2296 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297 get_inventory_element_from_pos(local_player, i);
2298 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299 get_inventory_element_from_pos(local_player, -i - 1);
2302 game_panel_controls[GAME_PANEL_SCORE].value = score;
2303 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2305 game_panel_controls[GAME_PANEL_TIME].value = time;
2307 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2311 if (level.time == 0)
2312 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2314 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2316 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2319 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2321 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2324 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325 local_player->shield_normal_time_left;
2326 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2329 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330 local_player->shield_deadly_time_left;
2332 game_panel_controls[GAME_PANEL_EXIT].value =
2333 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2335 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339 EL_EMC_MAGIC_BALL_SWITCH);
2341 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344 game.light_time_left;
2346 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349 game.timegate_time_left;
2351 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2354 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357 game.lenses_time_left;
2359 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362 game.magnify_time_left;
2364 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2366 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2368 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2369 EL_BALLOON_SWITCH_NONE);
2371 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372 local_player->dynabomb_count;
2373 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374 local_player->dynabomb_size;
2375 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2378 game_panel_controls[GAME_PANEL_PENGUINS].value =
2379 local_player->friends_still_needed;
2381 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382 local_player->sokobanfields_still_needed;
2383 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384 local_player->sokobanfields_still_needed;
2386 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2389 for (i = 0; i < NUM_BELTS; i++)
2391 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401 game.magic_wall_time_left;
2403 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404 local_player->gravity;
2406 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2409 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412 game.panel.element[i].id : EL_UNDEFINED);
2414 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417 element_info[game.panel.element_count[i].id].element_count : 0);
2419 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422 element_info[game.panel.ce_score[i].id].collect_score : 0);
2424 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427 element_info[game.panel.ce_score_element[i].id].collect_score :
2430 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2434 /* update game panel control frames */
2436 for (i = 0; game_panel_controls[i].nr != -1; i++)
2438 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2440 if (gpc->type == TYPE_ELEMENT)
2442 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2444 int last_anim_random_frame = gfx.anim_random_frame;
2445 int element = gpc->value;
2446 int graphic = el2panelimg(element);
2448 if (gpc->value != gpc->last_value)
2451 gpc->gfx_random = INIT_GFX_RANDOM();
2457 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459 gpc->gfx_random = INIT_GFX_RANDOM();
2462 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463 gfx.anim_random_frame = gpc->gfx_random;
2465 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466 gpc->gfx_frame = element_info[element].collect_score;
2468 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472 gfx.anim_random_frame = last_anim_random_frame;
2475 else if (gpc->type == TYPE_GRAPHIC)
2477 if (gpc->graphic != IMG_UNDEFINED)
2479 int last_anim_random_frame = gfx.anim_random_frame;
2480 int graphic = gpc->graphic;
2482 if (gpc->value != gpc->last_value)
2485 gpc->gfx_random = INIT_GFX_RANDOM();
2491 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493 gpc->gfx_random = INIT_GFX_RANDOM();
2496 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497 gfx.anim_random_frame = gpc->gfx_random;
2499 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2501 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502 gfx.anim_random_frame = last_anim_random_frame;
2508 void DisplayGameControlValues()
2510 boolean redraw_panel = FALSE;
2513 for (i = 0; game_panel_controls[i].nr != -1; i++)
2515 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2517 if (PANEL_DEACTIVATED(gpc->pos))
2520 if (gpc->value == gpc->last_value &&
2521 gpc->frame == gpc->last_frame)
2524 redraw_panel = TRUE;
2530 /* copy default game door content to main double buffer */
2532 /* !!! CHECK AGAIN !!! */
2533 SetPanelBackground();
2534 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2537 /* redraw game control buttons */
2538 RedrawGameButtons();
2540 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2542 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2544 int nr = game_panel_order[i].nr;
2545 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546 struct TextPosInfo *pos = gpc->pos;
2547 int type = gpc->type;
2548 int value = gpc->value;
2549 int frame = gpc->frame;
2550 int size = pos->size;
2551 int font = pos->font;
2552 boolean draw_masked = pos->draw_masked;
2553 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555 if (PANEL_DEACTIVATED(pos))
2558 gpc->last_value = value;
2559 gpc->last_frame = frame;
2561 if (type == TYPE_INTEGER)
2563 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564 nr == GAME_PANEL_TIME)
2566 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2568 if (use_dynamic_size) /* use dynamic number of digits */
2570 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572 int size2 = size1 + 1;
2573 int font1 = pos->font;
2574 int font2 = pos->font_alt;
2576 size = (value < value_change ? size1 : size2);
2577 font = (value < value_change ? font1 : font2);
2581 /* correct text size if "digits" is zero or less */
2583 size = strlen(int2str(value, size));
2585 /* dynamically correct text alignment */
2586 pos->width = size * getFontWidth(font);
2588 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589 int2str(value, size), font, mask_mode);
2591 else if (type == TYPE_ELEMENT)
2593 int element, graphic;
2597 int dst_x = PANEL_XPOS(pos);
2598 int dst_y = PANEL_YPOS(pos);
2600 if (value != EL_UNDEFINED && value != EL_EMPTY)
2603 graphic = el2panelimg(value);
2605 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2607 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613 width = graphic_info[graphic].width * size / TILESIZE;
2614 height = graphic_info[graphic].height * size / TILESIZE;
2617 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2624 else if (type == TYPE_GRAPHIC)
2626 int graphic = gpc->graphic;
2627 int graphic_active = gpc->graphic_active;
2631 int dst_x = PANEL_XPOS(pos);
2632 int dst_y = PANEL_YPOS(pos);
2633 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2636 if (graphic != IMG_UNDEFINED && !skip)
2638 if (pos->style == STYLE_REVERSE)
2639 value = 100 - value;
2641 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2643 if (pos->direction & MV_HORIZONTAL)
2645 width = graphic_info[graphic_active].width * value / 100;
2646 height = graphic_info[graphic_active].height;
2648 if (pos->direction == MV_LEFT)
2650 src_x += graphic_info[graphic_active].width - width;
2651 dst_x += graphic_info[graphic_active].width - width;
2656 width = graphic_info[graphic_active].width;
2657 height = graphic_info[graphic_active].height * value / 100;
2659 if (pos->direction == MV_UP)
2661 src_y += graphic_info[graphic_active].height - height;
2662 dst_y += graphic_info[graphic_active].height - height;
2667 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2675 if (pos->direction & MV_HORIZONTAL)
2677 if (pos->direction == MV_RIGHT)
2684 dst_x = PANEL_XPOS(pos);
2687 width = graphic_info[graphic].width - width;
2691 if (pos->direction == MV_DOWN)
2698 dst_y = PANEL_YPOS(pos);
2701 height = graphic_info[graphic].height - height;
2705 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2712 else if (type == TYPE_STRING)
2714 boolean active = (value != 0);
2715 char *state_normal = "off";
2716 char *state_active = "on";
2717 char *state = (active ? state_active : state_normal);
2718 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2720 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2721 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2723 if (nr == GAME_PANEL_GRAVITY_STATE)
2725 int font1 = pos->font; /* (used for normal state) */
2726 int font2 = pos->font_alt; /* (used for active state) */
2728 font = (active ? font2 : font1);
2737 /* don't truncate output if "chars" is zero or less */
2740 /* dynamically correct text alignment */
2741 pos->width = size * getFontWidth(font);
2744 s_cut = getStringCopyN(s, size);
2746 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747 s_cut, font, mask_mode);
2753 redraw_mask |= REDRAW_DOOR_1;
2756 SetGameStatus(GAME_MODE_PLAYING);
2759 void UpdateAndDisplayGameControlValues()
2761 if (tape.deactivate_display)
2764 UpdateGameControlValues();
2765 DisplayGameControlValues();
2768 void UpdateGameDoorValues()
2770 UpdateGameControlValues();
2773 void DrawGameDoorValues()
2775 DisplayGameControlValues();
2780 =============================================================================
2782 -----------------------------------------------------------------------------
2783 initialize game engine due to level / tape version number
2784 =============================================================================
2787 static void InitGameEngine()
2789 int i, j, k, l, x, y;
2791 /* set game engine from tape file when re-playing, else from level file */
2792 game.engine_version = (tape.playing ? tape.engine_version :
2793 level.game_version);
2795 /* set single or multi-player game mode (needed for re-playing tapes) */
2796 game.team_mode = setup.team_mode;
2800 int num_players = 0;
2802 for (i = 0; i < MAX_PLAYERS; i++)
2803 if (tape.player_participates[i])
2806 /* multi-player tapes contain input data for more than one player */
2807 game.team_mode = (num_players > 1);
2810 /* ---------------------------------------------------------------------- */
2811 /* set flags for bugs and changes according to active game engine version */
2812 /* ---------------------------------------------------------------------- */
2815 Summary of bugfix/change:
2816 Fixed handling for custom elements that change when pushed by the player.
2818 Fixed/changed in version:
2822 Before 3.1.0, custom elements that "change when pushing" changed directly
2823 after the player started pushing them (until then handled in "DigField()").
2824 Since 3.1.0, these custom elements are not changed until the "pushing"
2825 move of the element is finished (now handled in "ContinueMoving()").
2827 Affected levels/tapes:
2828 The first condition is generally needed for all levels/tapes before version
2829 3.1.0, which might use the old behaviour before it was changed; known tapes
2830 that are affected are some tapes from the level set "Walpurgis Gardens" by
2832 The second condition is an exception from the above case and is needed for
2833 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834 above (including some development versions of 3.1.0), but before it was
2835 known that this change would break tapes like the above and was fixed in
2836 3.1.1, so that the changed behaviour was active although the engine version
2837 while recording maybe was before 3.1.0. There is at least one tape that is
2838 affected by this exception, which is the tape for the one-level set "Bug
2839 Machine" by Juergen Bonhagen.
2842 game.use_change_when_pushing_bug =
2843 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2845 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846 tape.game_version < VERSION_IDENT(3,1,1,0)));
2849 Summary of bugfix/change:
2850 Fixed handling for blocking the field the player leaves when moving.
2852 Fixed/changed in version:
2856 Before 3.1.1, when "block last field when moving" was enabled, the field
2857 the player is leaving when moving was blocked for the time of the move,
2858 and was directly unblocked afterwards. This resulted in the last field
2859 being blocked for exactly one less than the number of frames of one player
2860 move. Additionally, even when blocking was disabled, the last field was
2861 blocked for exactly one frame.
2862 Since 3.1.1, due to changes in player movement handling, the last field
2863 is not blocked at all when blocking is disabled. When blocking is enabled,
2864 the last field is blocked for exactly the number of frames of one player
2865 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866 last field is blocked for exactly one more than the number of frames of
2869 Affected levels/tapes:
2870 (!!! yet to be determined -- probably many !!!)
2873 game.use_block_last_field_bug =
2874 (game.engine_version < VERSION_IDENT(3,1,1,0));
2876 game_em.use_single_button =
2877 (game.engine_version > VERSION_IDENT(4,0,0,2));
2879 game_em.use_snap_key_bug =
2880 (game.engine_version < VERSION_IDENT(4,0,1,0));
2882 /* ---------------------------------------------------------------------- */
2884 /* set maximal allowed number of custom element changes per game frame */
2885 game.max_num_changes_per_frame = 1;
2887 /* default scan direction: scan playfield from top/left to bottom/right */
2888 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2890 /* dynamically adjust element properties according to game engine version */
2891 InitElementPropertiesEngine(game.engine_version);
2894 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895 printf(" tape version == %06d [%s] [file: %06d]\n",
2896 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2898 printf(" => game.engine_version == %06d\n", game.engine_version);
2901 /* ---------- initialize player's initial move delay --------------------- */
2903 /* dynamically adjust player properties according to level information */
2904 for (i = 0; i < MAX_PLAYERS; i++)
2905 game.initial_move_delay_value[i] =
2906 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2908 /* dynamically adjust player properties according to game engine version */
2909 for (i = 0; i < MAX_PLAYERS; i++)
2910 game.initial_move_delay[i] =
2911 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912 game.initial_move_delay_value[i] : 0);
2914 /* ---------- initialize player's initial push delay --------------------- */
2916 /* dynamically adjust player properties according to game engine version */
2917 game.initial_push_delay_value =
2918 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2920 /* ---------- initialize changing elements ------------------------------- */
2922 /* initialize changing elements information */
2923 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2925 struct ElementInfo *ei = &element_info[i];
2927 /* this pointer might have been changed in the level editor */
2928 ei->change = &ei->change_page[0];
2930 if (!IS_CUSTOM_ELEMENT(i))
2932 ei->change->target_element = EL_EMPTY_SPACE;
2933 ei->change->delay_fixed = 0;
2934 ei->change->delay_random = 0;
2935 ei->change->delay_frames = 1;
2938 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2940 ei->has_change_event[j] = FALSE;
2942 ei->event_page_nr[j] = 0;
2943 ei->event_page[j] = &ei->change_page[0];
2947 /* add changing elements from pre-defined list */
2948 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2950 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951 struct ElementInfo *ei = &element_info[ch_delay->element];
2953 ei->change->target_element = ch_delay->target_element;
2954 ei->change->delay_fixed = ch_delay->change_delay;
2956 ei->change->pre_change_function = ch_delay->pre_change_function;
2957 ei->change->change_function = ch_delay->change_function;
2958 ei->change->post_change_function = ch_delay->post_change_function;
2960 ei->change->can_change = TRUE;
2961 ei->change->can_change_or_has_action = TRUE;
2963 ei->has_change_event[CE_DELAY] = TRUE;
2965 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969 /* ---------- initialize internal run-time variables --------------------- */
2971 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2973 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2975 for (j = 0; j < ei->num_change_pages; j++)
2977 ei->change_page[j].can_change_or_has_action =
2978 (ei->change_page[j].can_change |
2979 ei->change_page[j].has_action);
2983 /* add change events from custom element configuration */
2984 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2986 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2988 for (j = 0; j < ei->num_change_pages; j++)
2990 if (!ei->change_page[j].can_change_or_has_action)
2993 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2995 /* only add event page for the first page found with this event */
2996 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2998 ei->has_change_event[k] = TRUE;
3000 ei->event_page_nr[k] = j;
3001 ei->event_page[k] = &ei->change_page[j];
3007 /* ---------- initialize reference elements in change conditions --------- */
3009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3011 int element = EL_CUSTOM_START + i;
3012 struct ElementInfo *ei = &element_info[element];
3014 for (j = 0; j < ei->num_change_pages; j++)
3016 int trigger_element = ei->change_page[j].initial_trigger_element;
3018 if (trigger_element >= EL_PREV_CE_8 &&
3019 trigger_element <= EL_NEXT_CE_8)
3020 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3022 ei->change_page[j].trigger_element = trigger_element;
3026 /* ---------- initialize run-time trigger player and element ------------- */
3028 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3030 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3032 for (j = 0; j < ei->num_change_pages; j++)
3034 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038 ei->change_page[j].actual_trigger_ce_value = 0;
3039 ei->change_page[j].actual_trigger_ce_score = 0;
3043 /* ---------- initialize trigger events ---------------------------------- */
3045 /* initialize trigger events information */
3046 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048 trigger_events[i][j] = FALSE;
3050 /* add trigger events from element change event properties */
3051 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3053 struct ElementInfo *ei = &element_info[i];
3055 for (j = 0; j < ei->num_change_pages; j++)
3057 if (!ei->change_page[j].can_change_or_has_action)
3060 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3062 int trigger_element = ei->change_page[j].trigger_element;
3064 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3066 if (ei->change_page[j].has_event[k])
3068 if (IS_GROUP_ELEMENT(trigger_element))
3070 struct ElementGroupInfo *group =
3071 element_info[trigger_element].group;
3073 for (l = 0; l < group->num_elements_resolved; l++)
3074 trigger_events[group->element_resolved[l]][k] = TRUE;
3076 else if (trigger_element == EL_ANY_ELEMENT)
3077 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078 trigger_events[l][k] = TRUE;
3080 trigger_events[trigger_element][k] = TRUE;
3087 /* ---------- initialize push delay -------------------------------------- */
3089 /* initialize push delay values to default */
3090 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3092 if (!IS_CUSTOM_ELEMENT(i))
3094 /* set default push delay values (corrected since version 3.0.7-1) */
3095 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3097 element_info[i].push_delay_fixed = 2;
3098 element_info[i].push_delay_random = 8;
3102 element_info[i].push_delay_fixed = 8;
3103 element_info[i].push_delay_random = 8;
3108 /* set push delay value for certain elements from pre-defined list */
3109 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3111 int e = push_delay_list[i].element;
3113 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3114 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117 /* set push delay value for Supaplex elements for newer engine versions */
3118 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122 if (IS_SP_ELEMENT(i))
3124 /* set SP push delay to just enough to push under a falling zonk */
3125 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3127 element_info[i].push_delay_fixed = delay;
3128 element_info[i].push_delay_random = 0;
3133 /* ---------- initialize move stepsize ----------------------------------- */
3135 /* initialize move stepsize values to default */
3136 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137 if (!IS_CUSTOM_ELEMENT(i))
3138 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3140 /* set move stepsize value for certain elements from pre-defined list */
3141 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3143 int e = move_stepsize_list[i].element;
3145 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148 /* ---------- initialize collect score ----------------------------------- */
3150 /* initialize collect score values for custom elements from initial value */
3151 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152 if (IS_CUSTOM_ELEMENT(i))
3153 element_info[i].collect_score = element_info[i].collect_score_initial;
3155 /* ---------- initialize collect count ----------------------------------- */
3157 /* initialize collect count values for non-custom elements */
3158 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159 if (!IS_CUSTOM_ELEMENT(i))
3160 element_info[i].collect_count_initial = 0;
3162 /* add collect count values for all elements from pre-defined list */
3163 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164 element_info[collect_count_list[i].element].collect_count_initial =
3165 collect_count_list[i].count;
3167 /* ---------- initialize access direction -------------------------------- */
3169 /* initialize access direction values to default (access from every side) */
3170 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171 if (!IS_CUSTOM_ELEMENT(i))
3172 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3174 /* set access direction value for certain elements from pre-defined list */
3175 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176 element_info[access_direction_list[i].element].access_direction =
3177 access_direction_list[i].direction;
3179 /* ---------- initialize explosion content ------------------------------- */
3180 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3182 if (IS_CUSTOM_ELEMENT(i))
3185 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3187 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3189 element_info[i].content.e[x][y] =
3190 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192 i == EL_PLAYER_3 ? EL_EMERALD :
3193 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194 i == EL_MOLE ? EL_EMERALD_RED :
3195 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200 i == EL_WALL_EMERALD ? EL_EMERALD :
3201 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206 i == EL_WALL_PEARL ? EL_PEARL :
3207 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3212 /* ---------- initialize recursion detection ------------------------------ */
3213 recursion_loop_depth = 0;
3214 recursion_loop_detected = FALSE;
3215 recursion_loop_element = EL_UNDEFINED;
3217 /* ---------- initialize graphics engine ---------------------------------- */
3218 game.scroll_delay_value =
3219 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220 setup.scroll_delay ? setup.scroll_delay_value : 0);
3221 game.scroll_delay_value =
3222 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3224 /* ---------- initialize game engine snapshots ---------------------------- */
3225 for (i = 0; i < MAX_PLAYERS; i++)
3226 game.snapshot.last_action[i] = 0;
3227 game.snapshot.changed_action = FALSE;
3228 game.snapshot.collected_item = FALSE;
3229 game.snapshot.mode =
3230 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231 SNAPSHOT_MODE_EVERY_STEP :
3232 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233 SNAPSHOT_MODE_EVERY_MOVE :
3234 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236 game.snapshot.save_snapshot = FALSE;
3238 /* ---------- initialize level time for Supaplex engine ------------------- */
3239 /* Supaplex levels with time limit currently unsupported -- should be added */
3240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3244 int get_num_special_action(int element, int action_first, int action_last)
3246 int num_special_action = 0;
3249 for (i = action_first; i <= action_last; i++)
3251 boolean found = FALSE;
3253 for (j = 0; j < NUM_DIRECTIONS; j++)
3254 if (el_act_dir2img(element, i, j) !=
3255 el_act_dir2img(element, ACTION_DEFAULT, j))
3259 num_special_action++;
3264 return num_special_action;
3269 =============================================================================
3271 -----------------------------------------------------------------------------
3272 initialize and start new game
3273 =============================================================================
3278 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280 int fade_mask = REDRAW_FIELD;
3282 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3283 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3284 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3285 int initial_move_dir = MV_DOWN;
3288 // required here to update video display before fading (FIX THIS)
3289 DrawMaskedBorder(REDRAW_DOOR_2);
3291 if (!game.restart_level)
3292 CloseDoor(DOOR_CLOSE_1);
3294 SetGameStatus(GAME_MODE_PLAYING);
3296 if (level_editor_test_game)
3297 FadeSkipNextFadeIn();
3299 FadeSetEnterScreen();
3301 if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302 fade_mask = REDRAW_ALL;
3304 FadeLevelSoundsAndMusic();
3306 ExpireSoundLoops(TRUE);
3308 if (!level_editor_test_game)
3311 /* needed if different viewport properties defined for playing */
3312 ChangeViewportPropertiesIfNeeded();
3316 DrawCompleteVideoDisplay();
3318 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3321 InitGameControlValues();
3323 /* don't play tapes over network */
3324 network_playing = (network.enabled && !tape.playing);
3326 for (i = 0; i < MAX_PLAYERS; i++)
3328 struct PlayerInfo *player = &stored_player[i];
3330 player->index_nr = i;
3331 player->index_bit = (1 << i);
3332 player->element_nr = EL_PLAYER_1 + i;
3334 player->present = FALSE;
3335 player->active = FALSE;
3336 player->mapped = FALSE;
3338 player->killed = FALSE;
3339 player->reanimated = FALSE;
3342 player->effective_action = 0;
3343 player->programmed_action = 0;
3345 player->mouse_action.lx = 0;
3346 player->mouse_action.ly = 0;
3347 player->mouse_action.button = 0;
3348 player->mouse_action.button_hint = 0;
3350 player->effective_mouse_action.lx = 0;
3351 player->effective_mouse_action.ly = 0;
3352 player->effective_mouse_action.button = 0;
3353 player->effective_mouse_action.button_hint = 0;
3356 player->score_final = 0;
3358 player->health = MAX_HEALTH;
3359 player->health_final = MAX_HEALTH;
3361 player->gems_still_needed = level.gems_needed;
3362 player->sokobanfields_still_needed = 0;
3363 player->lights_still_needed = 0;
3364 player->friends_still_needed = 0;
3366 for (j = 0; j < MAX_NUM_KEYS; j++)
3367 player->key[j] = FALSE;
3369 player->num_white_keys = 0;
3371 player->dynabomb_count = 0;
3372 player->dynabomb_size = 1;
3373 player->dynabombs_left = 0;
3374 player->dynabomb_xl = FALSE;
3376 player->MovDir = initial_move_dir;
3379 player->GfxDir = initial_move_dir;
3380 player->GfxAction = ACTION_DEFAULT;
3382 player->StepFrame = 0;
3384 player->initial_element = player->element_nr;
3385 player->artwork_element =
3386 (level.use_artwork_element[i] ? level.artwork_element[i] :
3387 player->element_nr);
3388 player->use_murphy = FALSE;
3390 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3391 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3393 player->gravity = level.initial_player_gravity[i];
3395 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3397 player->actual_frame_counter = 0;
3399 player->step_counter = 0;
3401 player->last_move_dir = initial_move_dir;
3403 player->is_active = FALSE;
3405 player->is_waiting = FALSE;
3406 player->is_moving = FALSE;
3407 player->is_auto_moving = FALSE;
3408 player->is_digging = FALSE;
3409 player->is_snapping = FALSE;
3410 player->is_collecting = FALSE;
3411 player->is_pushing = FALSE;
3412 player->is_switching = FALSE;
3413 player->is_dropping = FALSE;
3414 player->is_dropping_pressed = FALSE;
3416 player->is_bored = FALSE;
3417 player->is_sleeping = FALSE;
3419 player->was_waiting = TRUE;
3420 player->was_moving = FALSE;
3421 player->was_snapping = FALSE;
3422 player->was_dropping = FALSE;
3424 player->force_dropping = FALSE;
3426 player->frame_counter_bored = -1;
3427 player->frame_counter_sleeping = -1;
3429 player->anim_delay_counter = 0;
3430 player->post_delay_counter = 0;
3432 player->dir_waiting = initial_move_dir;
3433 player->action_waiting = ACTION_DEFAULT;
3434 player->last_action_waiting = ACTION_DEFAULT;
3435 player->special_action_bored = ACTION_DEFAULT;
3436 player->special_action_sleeping = ACTION_DEFAULT;
3438 player->switch_x = -1;
3439 player->switch_y = -1;
3441 player->drop_x = -1;
3442 player->drop_y = -1;
3444 player->show_envelope = 0;
3446 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3448 player->push_delay = -1; /* initialized when pushing starts */
3449 player->push_delay_value = game.initial_push_delay_value;
3451 player->drop_delay = 0;
3452 player->drop_pressed_delay = 0;
3454 player->last_jx = -1;
3455 player->last_jy = -1;
3459 player->shield_normal_time_left = 0;
3460 player->shield_deadly_time_left = 0;
3462 player->inventory_infinite_element = EL_UNDEFINED;
3463 player->inventory_size = 0;
3465 if (level.use_initial_inventory[i])
3467 for (j = 0; j < level.initial_inventory_size[i]; j++)
3469 int element = level.initial_inventory_content[i][j];
3470 int collect_count = element_info[element].collect_count_initial;
3473 if (!IS_CUSTOM_ELEMENT(element))
3476 if (collect_count == 0)
3477 player->inventory_infinite_element = element;
3479 for (k = 0; k < collect_count; k++)
3480 if (player->inventory_size < MAX_INVENTORY_SIZE)
3481 player->inventory_element[player->inventory_size++] = element;
3485 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3486 SnapField(player, 0, 0);
3488 player->LevelSolved = FALSE;
3489 player->GameOver = FALSE;
3491 player->LevelSolved_GameWon = FALSE;
3492 player->LevelSolved_GameEnd = FALSE;
3493 player->LevelSolved_PanelOff = FALSE;
3494 player->LevelSolved_SaveTape = FALSE;
3495 player->LevelSolved_SaveScore = FALSE;
3497 player->LevelSolved_CountingTime = 0;
3498 player->LevelSolved_CountingScore = 0;
3499 player->LevelSolved_CountingHealth = 0;
3501 map_player_action[i] = i;
3504 network_player_action_received = FALSE;
3506 /* initial null action */
3507 if (network_playing)
3508 SendToServer_MovePlayer(MV_NONE);
3516 TimeLeft = level.time;
3519 ScreenMovDir = MV_NONE;
3523 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3525 AllPlayersGone = FALSE;
3527 game.no_time_limit = (level.time == 0);
3529 game.yamyam_content_nr = 0;
3530 game.robot_wheel_active = FALSE;
3531 game.magic_wall_active = FALSE;
3532 game.magic_wall_time_left = 0;
3533 game.light_time_left = 0;
3534 game.timegate_time_left = 0;
3535 game.switchgate_pos = 0;
3536 game.wind_direction = level.wind_direction_initial;
3538 game.lenses_time_left = 0;
3539 game.magnify_time_left = 0;
3541 game.ball_state = level.ball_state_initial;
3542 game.ball_content_nr = 0;
3544 game.envelope_active = FALSE;
3546 for (i = 0; i < NUM_BELTS; i++)
3548 game.belt_dir[i] = MV_NONE;
3549 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3552 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3553 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3555 #if DEBUG_INIT_PLAYER
3558 printf("Player status at level initialization:\n");
3560 for (i = 0; i < MAX_PLAYERS; i++)
3562 struct PlayerInfo *player = &stored_player[i];
3564 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3568 player->connected_locally,
3569 player->connected_network,
3572 if (local_player == player)
3573 printf(" (local player)");
3580 SCAN_PLAYFIELD(x, y)
3582 Feld[x][y] = level.field[x][y];
3583 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3584 ChangeDelay[x][y] = 0;
3585 ChangePage[x][y] = -1;
3586 CustomValue[x][y] = 0; /* initialized in InitField() */
3587 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3589 WasJustMoving[x][y] = 0;
3590 WasJustFalling[x][y] = 0;
3591 CheckCollision[x][y] = 0;
3592 CheckImpact[x][y] = 0;
3594 Pushed[x][y] = FALSE;
3596 ChangeCount[x][y] = 0;
3597 ChangeEvent[x][y] = -1;
3599 ExplodePhase[x][y] = 0;
3600 ExplodeDelay[x][y] = 0;
3601 ExplodeField[x][y] = EX_TYPE_NONE;
3603 RunnerVisit[x][y] = 0;
3604 PlayerVisit[x][y] = 0;
3607 GfxRandom[x][y] = INIT_GFX_RANDOM();
3608 GfxElement[x][y] = EL_UNDEFINED;
3609 GfxAction[x][y] = ACTION_DEFAULT;
3610 GfxDir[x][y] = MV_NONE;
3611 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3614 SCAN_PLAYFIELD(x, y)
3616 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3618 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3620 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3623 InitField(x, y, TRUE);
3625 ResetGfxAnimation(x, y);
3630 for (i = 0; i < MAX_PLAYERS; i++)
3632 struct PlayerInfo *player = &stored_player[i];
3634 /* set number of special actions for bored and sleeping animation */
3635 player->num_special_action_bored =
3636 get_num_special_action(player->artwork_element,
3637 ACTION_BORING_1, ACTION_BORING_LAST);
3638 player->num_special_action_sleeping =
3639 get_num_special_action(player->artwork_element,
3640 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3643 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3644 emulate_sb ? EMU_SOKOBAN :
3645 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3647 /* initialize type of slippery elements */
3648 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3650 if (!IS_CUSTOM_ELEMENT(i))
3652 /* default: elements slip down either to the left or right randomly */
3653 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3655 /* SP style elements prefer to slip down on the left side */
3656 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3657 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3659 /* BD style elements prefer to slip down on the left side */
3660 if (game.emulation == EMU_BOULDERDASH)
3661 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3665 /* initialize explosion and ignition delay */
3666 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3668 if (!IS_CUSTOM_ELEMENT(i))
3671 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3672 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3673 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3674 int last_phase = (num_phase + 1) * delay;
3675 int half_phase = (num_phase / 2) * delay;
3677 element_info[i].explosion_delay = last_phase - 1;
3678 element_info[i].ignition_delay = half_phase;
3680 if (i == EL_BLACK_ORB)
3681 element_info[i].ignition_delay = 1;
3685 /* correct non-moving belts to start moving left */
3686 for (i = 0; i < NUM_BELTS; i++)
3687 if (game.belt_dir[i] == MV_NONE)
3688 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3690 #if USE_NEW_PLAYER_ASSIGNMENTS
3691 for (i = 0; i < MAX_PLAYERS; i++)
3693 stored_player[i].connected = FALSE;
3695 /* in network game mode, the local player might not be the first player */
3696 if (stored_player[i].connected_locally)
3697 local_player = &stored_player[i];
3700 if (!network.enabled)
3701 local_player->connected = TRUE;
3705 for (i = 0; i < MAX_PLAYERS; i++)
3706 stored_player[i].connected = tape.player_participates[i];
3708 else if (network.enabled)
3710 /* add team mode players connected over the network (needed for correct
3711 assignment of player figures from level to locally playing players) */
3713 for (i = 0; i < MAX_PLAYERS; i++)
3714 if (stored_player[i].connected_network)
3715 stored_player[i].connected = TRUE;
3717 else if (game.team_mode)
3719 /* try to guess locally connected team mode players (needed for correct
3720 assignment of player figures from level to locally playing players) */
3722 for (i = 0; i < MAX_PLAYERS; i++)
3723 if (setup.input[i].use_joystick ||
3724 setup.input[i].key.left != KSYM_UNDEFINED)
3725 stored_player[i].connected = TRUE;
3728 #if DEBUG_INIT_PLAYER
3731 printf("Player status after level initialization:\n");
3733 for (i = 0; i < MAX_PLAYERS; i++)
3735 struct PlayerInfo *player = &stored_player[i];
3737 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3741 player->connected_locally,
3742 player->connected_network,
3745 if (local_player == player)
3746 printf(" (local player)");
3753 #if DEBUG_INIT_PLAYER
3755 printf("Reassigning players ...\n");
3758 /* check if any connected player was not found in playfield */
3759 for (i = 0; i < MAX_PLAYERS; i++)
3761 struct PlayerInfo *player = &stored_player[i];
3763 if (player->connected && !player->present)
3765 struct PlayerInfo *field_player = NULL;
3767 #if DEBUG_INIT_PLAYER
3769 printf("- looking for field player for player %d ...\n", i + 1);
3772 /* assign first free player found that is present in the playfield */
3774 /* first try: look for unmapped playfield player that is not connected */
3775 for (j = 0; j < MAX_PLAYERS; j++)
3776 if (field_player == NULL &&
3777 stored_player[j].present &&
3778 !stored_player[j].mapped &&
3779 !stored_player[j].connected)
3780 field_player = &stored_player[j];
3782 /* second try: look for *any* unmapped playfield player */
3783 for (j = 0; j < MAX_PLAYERS; j++)
3784 if (field_player == NULL &&
3785 stored_player[j].present &&
3786 !stored_player[j].mapped)
3787 field_player = &stored_player[j];
3789 if (field_player != NULL)
3791 int jx = field_player->jx, jy = field_player->jy;
3793 #if DEBUG_INIT_PLAYER
3795 printf("- found player %d\n", field_player->index_nr + 1);
3798 player->present = FALSE;
3799 player->active = FALSE;
3801 field_player->present = TRUE;
3802 field_player->active = TRUE;
3805 player->initial_element = field_player->initial_element;
3806 player->artwork_element = field_player->artwork_element;
3808 player->block_last_field = field_player->block_last_field;
3809 player->block_delay_adjustment = field_player->block_delay_adjustment;
3812 StorePlayer[jx][jy] = field_player->element_nr;
3814 field_player->jx = field_player->last_jx = jx;
3815 field_player->jy = field_player->last_jy = jy;
3817 if (local_player == player)
3818 local_player = field_player;
3820 map_player_action[field_player->index_nr] = i;
3822 field_player->mapped = TRUE;
3824 #if DEBUG_INIT_PLAYER
3826 printf("- map_player_action[%d] == %d\n",
3827 field_player->index_nr + 1, i + 1);
3832 if (player->connected && player->present)
3833 player->mapped = TRUE;
3836 #if DEBUG_INIT_PLAYER
3839 printf("Player status after player assignment (first stage):\n");
3841 for (i = 0; i < MAX_PLAYERS; i++)
3843 struct PlayerInfo *player = &stored_player[i];
3845 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3849 player->connected_locally,
3850 player->connected_network,
3853 if (local_player == player)
3854 printf(" (local player)");
3863 /* check if any connected player was not found in playfield */
3864 for (i = 0; i < MAX_PLAYERS; i++)
3866 struct PlayerInfo *player = &stored_player[i];
3868 if (player->connected && !player->present)
3870 for (j = 0; j < MAX_PLAYERS; j++)
3872 struct PlayerInfo *field_player = &stored_player[j];
3873 int jx = field_player->jx, jy = field_player->jy;
3875 /* assign first free player found that is present in the playfield */
3876 if (field_player->present && !field_player->connected)
3878 player->present = TRUE;
3879 player->active = TRUE;
3881 field_player->present = FALSE;
3882 field_player->active = FALSE;
3884 player->initial_element = field_player->initial_element;
3885 player->artwork_element = field_player->artwork_element;
3887 player->block_last_field = field_player->block_last_field;
3888 player->block_delay_adjustment = field_player->block_delay_adjustment;
3890 StorePlayer[jx][jy] = player->element_nr;
3892 player->jx = player->last_jx = jx;
3893 player->jy = player->last_jy = jy;
3903 printf("::: local_player->present == %d\n", local_player->present);
3906 /* set focus to local player for network games, else to all players */
3907 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908 game.centered_player_nr_next = game.centered_player_nr;
3909 game.set_centered_player = FALSE;
3911 if (network_playing && tape.recording)
3913 /* store client dependent player focus when recording network games */
3914 tape.centered_player_nr_next = game.centered_player_nr_next;
3915 tape.set_centered_player = TRUE;
3920 /* when playing a tape, eliminate all players who do not participate */
3922 #if USE_NEW_PLAYER_ASSIGNMENTS
3924 if (!game.team_mode)
3926 for (i = 0; i < MAX_PLAYERS; i++)
3928 if (stored_player[i].active &&
3929 !tape.player_participates[map_player_action[i]])
3931 struct PlayerInfo *player = &stored_player[i];
3932 int jx = player->jx, jy = player->jy;
3934 #if DEBUG_INIT_PLAYER
3936 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3939 player->active = FALSE;
3940 StorePlayer[jx][jy] = 0;
3941 Feld[jx][jy] = EL_EMPTY;
3948 for (i = 0; i < MAX_PLAYERS; i++)
3950 if (stored_player[i].active &&
3951 !tape.player_participates[i])
3953 struct PlayerInfo *player = &stored_player[i];
3954 int jx = player->jx, jy = player->jy;
3956 player->active = FALSE;
3957 StorePlayer[jx][jy] = 0;
3958 Feld[jx][jy] = EL_EMPTY;
3963 else if (!network.enabled && !game.team_mode) /* && !tape.playing */
3965 /* when in single player mode, eliminate all but the local player */
3967 for (i = 0; i < MAX_PLAYERS; i++)
3969 struct PlayerInfo *player = &stored_player[i];
3971 if (player->active && player != local_player)
3973 int jx = player->jx, jy = player->jy;
3975 player->active = FALSE;
3976 player->present = FALSE;
3978 StorePlayer[jx][jy] = 0;
3979 Feld[jx][jy] = EL_EMPTY;
3984 /* when recording the game, store which players take part in the game */
3987 #if USE_NEW_PLAYER_ASSIGNMENTS
3988 for (i = 0; i < MAX_PLAYERS; i++)
3989 if (stored_player[i].connected)
3990 tape.player_participates[i] = TRUE;
3992 for (i = 0; i < MAX_PLAYERS; i++)
3993 if (stored_player[i].active)
3994 tape.player_participates[i] = TRUE;
3998 #if DEBUG_INIT_PLAYER
4001 printf("Player status after player assignment (final stage):\n");
4003 for (i = 0; i < MAX_PLAYERS; i++)
4005 struct PlayerInfo *player = &stored_player[i];
4007 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4011 player->connected_locally,
4012 player->connected_network,
4015 if (local_player == player)
4016 printf(" (local player)");
4023 if (BorderElement == EL_EMPTY)
4026 SBX_Right = lev_fieldx - SCR_FIELDX;
4028 SBY_Lower = lev_fieldy - SCR_FIELDY;
4033 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4035 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4038 if (full_lev_fieldx <= SCR_FIELDX)
4039 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4040 if (full_lev_fieldy <= SCR_FIELDY)
4041 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4043 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4045 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4048 /* if local player not found, look for custom element that might create
4049 the player (make some assumptions about the right custom element) */
4050 if (!local_player->present)
4052 int start_x = 0, start_y = 0;
4053 int found_rating = 0;
4054 int found_element = EL_UNDEFINED;
4055 int player_nr = local_player->index_nr;
4057 SCAN_PLAYFIELD(x, y)
4059 int element = Feld[x][y];
4064 if (level.use_start_element[player_nr] &&
4065 level.start_element[player_nr] == element &&
4072 found_element = element;
4075 if (!IS_CUSTOM_ELEMENT(element))
4078 if (CAN_CHANGE(element))
4080 for (i = 0; i < element_info[element].num_change_pages; i++)
4082 /* check for player created from custom element as single target */
4083 content = element_info[element].change_page[i].target_element;
4084 is_player = ELEM_IS_PLAYER(content);
4086 if (is_player && (found_rating < 3 ||
4087 (found_rating == 3 && element < found_element)))
4093 found_element = element;
4098 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4100 /* check for player created from custom element as explosion content */
4101 content = element_info[element].content.e[xx][yy];
4102 is_player = ELEM_IS_PLAYER(content);
4104 if (is_player && (found_rating < 2 ||
4105 (found_rating == 2 && element < found_element)))
4107 start_x = x + xx - 1;
4108 start_y = y + yy - 1;
4111 found_element = element;
4114 if (!CAN_CHANGE(element))
4117 for (i = 0; i < element_info[element].num_change_pages; i++)
4119 /* check for player created from custom element as extended target */
4121 element_info[element].change_page[i].target_content.e[xx][yy];
4123 is_player = ELEM_IS_PLAYER(content);
4125 if (is_player && (found_rating < 1 ||
4126 (found_rating == 1 && element < found_element)))
4128 start_x = x + xx - 1;
4129 start_y = y + yy - 1;
4132 found_element = element;
4138 scroll_x = SCROLL_POSITION_X(start_x);
4139 scroll_y = SCROLL_POSITION_Y(start_y);
4143 scroll_x = SCROLL_POSITION_X(local_player->jx);
4144 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4147 /* !!! FIX THIS (START) !!! */
4148 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4150 InitGameEngine_EM();
4152 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4154 InitGameEngine_SP();
4156 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4158 InitGameEngine_MM();
4162 DrawLevel(REDRAW_FIELD);
4165 /* after drawing the level, correct some elements */
4166 if (game.timegate_time_left == 0)
4167 CloseAllOpenTimegates();
4170 /* blit playfield from scroll buffer to normal back buffer for fading in */
4171 BlitScreenToBitmap(backbuffer);
4172 /* !!! FIX THIS (END) !!! */
4174 DrawMaskedBorder(fade_mask);
4179 // full screen redraw is required at this point in the following cases:
4180 // - special editor door undrawn when game was started from level editor
4181 // - drawing area (playfield) was changed and has to be removed completely
4182 redraw_mask = REDRAW_ALL;
4186 if (!game.restart_level)
4188 /* copy default game door content to main double buffer */
4190 /* !!! CHECK AGAIN !!! */
4191 SetPanelBackground();
4192 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4193 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4196 SetPanelBackground();
4197 SetDrawBackgroundMask(REDRAW_DOOR_1);
4199 UpdateAndDisplayGameControlValues();
4201 if (!game.restart_level)
4207 CreateGameButtons();
4212 /* copy actual game door content to door double buffer for OpenDoor() */
4213 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4215 OpenDoor(DOOR_OPEN_ALL);
4217 KeyboardAutoRepeatOffUnlessAutoplay();
4219 #if DEBUG_INIT_PLAYER
4222 printf("Player status (final):\n");
4224 for (i = 0; i < MAX_PLAYERS; i++)
4226 struct PlayerInfo *player = &stored_player[i];
4228 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4232 player->connected_locally,
4233 player->connected_network,
4236 if (local_player == player)
4237 printf(" (local player)");
4250 if (!game.restart_level && !tape.playing)
4252 LevelStats_incPlayed(level_nr);
4254 SaveLevelSetup_SeriesInfo();
4257 game.restart_level = FALSE;
4258 game.restart_game_message = NULL;
4260 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4261 InitGameActions_MM();
4263 SaveEngineSnapshotToListInitial();
4265 if (!game.restart_level)
4267 PlaySound(SND_GAME_STARTING);
4269 if (setup.sound_music)
4274 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4275 int actual_player_x, int actual_player_y)
4277 /* this is used for non-R'n'D game engines to update certain engine values */
4279 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4281 actual_player_x = correctLevelPosX_EM(actual_player_x);
4282 actual_player_y = correctLevelPosY_EM(actual_player_y);
4285 /* needed to determine if sounds are played within the visible screen area */
4286 scroll_x = actual_scroll_x;
4287 scroll_y = actual_scroll_y;
4289 /* needed to get player position for "follow finger" playing input method */
4290 local_player->jx = actual_player_x;
4291 local_player->jy = actual_player_y;
4294 void InitMovDir(int x, int y)
4296 int i, element = Feld[x][y];
4297 static int xy[4][2] =
4304 static int direction[3][4] =
4306 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4307 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4308 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4317 Feld[x][y] = EL_BUG;
4318 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4321 case EL_SPACESHIP_RIGHT:
4322 case EL_SPACESHIP_UP:
4323 case EL_SPACESHIP_LEFT:
4324 case EL_SPACESHIP_DOWN:
4325 Feld[x][y] = EL_SPACESHIP;
4326 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4329 case EL_BD_BUTTERFLY_RIGHT:
4330 case EL_BD_BUTTERFLY_UP:
4331 case EL_BD_BUTTERFLY_LEFT:
4332 case EL_BD_BUTTERFLY_DOWN:
4333 Feld[x][y] = EL_BD_BUTTERFLY;
4334 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4337 case EL_BD_FIREFLY_RIGHT:
4338 case EL_BD_FIREFLY_UP:
4339 case EL_BD_FIREFLY_LEFT:
4340 case EL_BD_FIREFLY_DOWN:
4341 Feld[x][y] = EL_BD_FIREFLY;
4342 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4345 case EL_PACMAN_RIGHT:
4347 case EL_PACMAN_LEFT:
4348 case EL_PACMAN_DOWN:
4349 Feld[x][y] = EL_PACMAN;
4350 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4353 case EL_YAMYAM_LEFT:
4354 case EL_YAMYAM_RIGHT:
4356 case EL_YAMYAM_DOWN:
4357 Feld[x][y] = EL_YAMYAM;
4358 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4361 case EL_SP_SNIKSNAK:
4362 MovDir[x][y] = MV_UP;
4365 case EL_SP_ELECTRON:
4366 MovDir[x][y] = MV_LEFT;
4373 Feld[x][y] = EL_MOLE;
4374 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4378 if (IS_CUSTOM_ELEMENT(element))
4380 struct ElementInfo *ei = &element_info[element];
4381 int move_direction_initial = ei->move_direction_initial;
4382 int move_pattern = ei->move_pattern;
4384 if (move_direction_initial == MV_START_PREVIOUS)
4386 if (MovDir[x][y] != MV_NONE)
4389 move_direction_initial = MV_START_AUTOMATIC;
4392 if (move_direction_initial == MV_START_RANDOM)
4393 MovDir[x][y] = 1 << RND(4);
4394 else if (move_direction_initial & MV_ANY_DIRECTION)
4395 MovDir[x][y] = move_direction_initial;
4396 else if (move_pattern == MV_ALL_DIRECTIONS ||
4397 move_pattern == MV_TURNING_LEFT ||
4398 move_pattern == MV_TURNING_RIGHT ||
4399 move_pattern == MV_TURNING_LEFT_RIGHT ||
4400 move_pattern == MV_TURNING_RIGHT_LEFT ||
4401 move_pattern == MV_TURNING_RANDOM)
4402 MovDir[x][y] = 1 << RND(4);
4403 else if (move_pattern == MV_HORIZONTAL)
4404 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4405 else if (move_pattern == MV_VERTICAL)
4406 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4407 else if (move_pattern & MV_ANY_DIRECTION)
4408 MovDir[x][y] = element_info[element].move_pattern;
4409 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4410 move_pattern == MV_ALONG_RIGHT_SIDE)
4412 /* use random direction as default start direction */
4413 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4414 MovDir[x][y] = 1 << RND(4);
4416 for (i = 0; i < NUM_DIRECTIONS; i++)
4418 int x1 = x + xy[i][0];
4419 int y1 = y + xy[i][1];
4421 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4423 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4424 MovDir[x][y] = direction[0][i];
4426 MovDir[x][y] = direction[1][i];
4435 MovDir[x][y] = 1 << RND(4);
4437 if (element != EL_BUG &&
4438 element != EL_SPACESHIP &&
4439 element != EL_BD_BUTTERFLY &&
4440 element != EL_BD_FIREFLY)
4443 for (i = 0; i < NUM_DIRECTIONS; i++)
4445 int x1 = x + xy[i][0];
4446 int y1 = y + xy[i][1];
4448 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4450 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4452 MovDir[x][y] = direction[0][i];
4455 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4456 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4458 MovDir[x][y] = direction[1][i];
4467 GfxDir[x][y] = MovDir[x][y];
4470 void InitAmoebaNr(int x, int y)
4473 int group_nr = AmoebeNachbarNr(x, y);
4477 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4479 if (AmoebaCnt[i] == 0)
4487 AmoebaNr[x][y] = group_nr;
4488 AmoebaCnt[group_nr]++;
4489 AmoebaCnt2[group_nr]++;
4492 static void PlayerWins(struct PlayerInfo *player)
4494 player->LevelSolved = TRUE;
4495 player->GameOver = TRUE;
4497 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4498 level.native_em_level->lev->score :
4499 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4502 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4503 MM_HEALTH(game_mm.laser_overload_value) :
4506 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4508 player->LevelSolved_CountingScore = player->score_final;
4509 player->LevelSolved_CountingHealth = player->health_final;
4514 static int time_count_steps;
4515 static int time, time_final;
4516 static int score, score_final;
4517 static int health, health_final;
4518 static int game_over_delay_1 = 0;
4519 static int game_over_delay_2 = 0;
4520 static int game_over_delay_3 = 0;
4521 int game_over_delay_value_1 = 50;
4522 int game_over_delay_value_2 = 25;
4523 int game_over_delay_value_3 = 50;
4525 if (!local_player->LevelSolved_GameWon)
4529 /* do not start end game actions before the player stops moving (to exit) */
4530 if (local_player->MovPos)
4533 local_player->LevelSolved_GameWon = TRUE;
4534 local_player->LevelSolved_SaveTape = tape.recording;
4535 local_player->LevelSolved_SaveScore = !tape.playing;
4539 LevelStats_incSolved(level_nr);
4541 SaveLevelSetup_SeriesInfo();
4544 if (tape.auto_play) /* tape might already be stopped here */
4545 tape.auto_play_level_solved = TRUE;
4549 game_over_delay_1 = 0;
4550 game_over_delay_2 = 0;
4551 game_over_delay_3 = game_over_delay_value_3;
4553 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4554 score = score_final = local_player->score_final;
4555 health = health_final = local_player->health_final;
4557 if (level.score[SC_TIME_BONUS] > 0)
4562 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4564 else if (game.no_time_limit && TimePlayed < 999)
4567 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4570 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4572 game_over_delay_1 = game_over_delay_value_1;
4574 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4577 score_final += health * level.score[SC_TIME_BONUS];
4579 game_over_delay_2 = game_over_delay_value_2;
4582 local_player->score_final = score_final;
4583 local_player->health_final = health_final;
4586 if (level_editor_test_game)
4589 score = score_final;
4591 local_player->LevelSolved_CountingTime = time;
4592 local_player->LevelSolved_CountingScore = score;
4594 game_panel_controls[GAME_PANEL_TIME].value = time;
4595 game_panel_controls[GAME_PANEL_SCORE].value = score;
4597 DisplayGameControlValues();
4600 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4602 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4604 /* close exit door after last player */
4605 if ((AllPlayersGone &&
4606 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4607 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4608 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4609 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4610 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4612 int element = Feld[ExitX][ExitY];
4614 Feld[ExitX][ExitY] =
4615 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4616 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4617 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4618 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4619 EL_EM_STEEL_EXIT_CLOSING);
4621 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4624 /* player disappears */
4625 DrawLevelField(ExitX, ExitY);
4628 for (i = 0; i < MAX_PLAYERS; i++)
4630 struct PlayerInfo *player = &stored_player[i];
4632 if (player->present)
4634 RemovePlayer(player);
4636 /* player disappears */
4637 DrawLevelField(player->jx, player->jy);
4642 PlaySound(SND_GAME_WINNING);
4645 if (game_over_delay_1 > 0)
4647 game_over_delay_1--;
4652 if (time != time_final)
4654 int time_to_go = ABS(time_final - time);
4655 int time_count_dir = (time < time_final ? +1 : -1);
4657 if (time_to_go < time_count_steps)
4658 time_count_steps = 1;
4660 time += time_count_steps * time_count_dir;
4661 score += time_count_steps * level.score[SC_TIME_BONUS];
4663 local_player->LevelSolved_CountingTime = time;
4664 local_player->LevelSolved_CountingScore = score;
4666 game_panel_controls[GAME_PANEL_TIME].value = time;
4667 game_panel_controls[GAME_PANEL_SCORE].value = score;
4669 DisplayGameControlValues();
4671 if (time == time_final)
4672 StopSound(SND_GAME_LEVELTIME_BONUS);
4673 else if (setup.sound_loops)
4674 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4676 PlaySound(SND_GAME_LEVELTIME_BONUS);
4681 if (game_over_delay_2 > 0)
4683 game_over_delay_2--;
4688 if (health != health_final)
4690 int health_count_dir = (health < health_final ? +1 : -1);
4692 health += health_count_dir;
4693 score += level.score[SC_TIME_BONUS];
4695 local_player->LevelSolved_CountingHealth = health;
4696 local_player->LevelSolved_CountingScore = score;
4698 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4699 game_panel_controls[GAME_PANEL_SCORE].value = score;
4701 DisplayGameControlValues();
4703 if (health == health_final)
4704 StopSound(SND_GAME_LEVELTIME_BONUS);
4705 else if (setup.sound_loops)
4706 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4708 PlaySound(SND_GAME_LEVELTIME_BONUS);
4713 local_player->LevelSolved_PanelOff = TRUE;
4715 if (game_over_delay_3 > 0)
4717 game_over_delay_3--;
4728 boolean raise_level = FALSE;
4730 local_player->LevelSolved_GameEnd = TRUE;
4732 if (local_player->LevelSolved_SaveTape)
4734 /* make sure that request dialog to save tape does not open door again */
4735 if (!global.use_envelope_request)
4736 CloseDoor(DOOR_CLOSE_1);
4738 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4741 /* if no tape is to be saved, close both doors simultaneously */
4742 CloseDoor(DOOR_CLOSE_ALL);
4744 if (level_editor_test_game)
4746 SetGameStatus(GAME_MODE_MAIN);
4753 if (!local_player->LevelSolved_SaveScore)
4755 SetGameStatus(GAME_MODE_MAIN);
4762 if (level_nr == leveldir_current->handicap_level)
4764 leveldir_current->handicap_level++;
4766 SaveLevelSetup_SeriesInfo();
4769 if (setup.increment_levels &&
4770 level_nr < leveldir_current->last_level)
4771 raise_level = TRUE; /* advance to next level */
4773 if ((hi_pos = NewHiScore()) >= 0)
4775 SetGameStatus(GAME_MODE_SCORES);
4777 DrawHallOfFame(hi_pos);
4787 SetGameStatus(GAME_MODE_MAIN);
4803 boolean one_score_entry_per_name = !program.many_scores_per_name;
4805 LoadScore(level_nr);
4807 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4808 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4811 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4813 if (local_player->score_final > highscore[k].Score)
4815 /* player has made it to the hall of fame */
4817 if (k < MAX_SCORE_ENTRIES - 1)
4819 int m = MAX_SCORE_ENTRIES - 1;
4821 if (one_score_entry_per_name)
4823 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4824 if (strEqual(setup.player_name, highscore[l].Name))
4827 if (m == k) /* player's new highscore overwrites his old one */
4831 for (l = m; l > k; l--)
4833 strcpy(highscore[l].Name, highscore[l - 1].Name);
4834 highscore[l].Score = highscore[l - 1].Score;
4840 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4841 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4842 highscore[k].Score = local_player->score_final;
4847 else if (one_score_entry_per_name &&
4848 !strncmp(setup.player_name, highscore[k].Name,
4849 MAX_PLAYER_NAME_LEN))
4850 break; /* player already there with a higher score */
4854 SaveScore(level_nr);
4859 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4861 int element = Feld[x][y];
4862 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4863 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4864 int horiz_move = (dx != 0);
4865 int sign = (horiz_move ? dx : dy);
4866 int step = sign * element_info[element].move_stepsize;
4868 /* special values for move stepsize for spring and things on conveyor belt */
4871 if (CAN_FALL(element) &&
4872 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4873 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4874 else if (element == EL_SPRING)
4875 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4881 inline static int getElementMoveStepsize(int x, int y)
4883 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4886 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4888 if (player->GfxAction != action || player->GfxDir != dir)
4890 player->GfxAction = action;
4891 player->GfxDir = dir;
4893 player->StepFrame = 0;
4897 static void ResetGfxFrame(int x, int y)
4899 // profiling showed that "autotest" spends 10~20% of its time in this function
4900 if (DrawingDeactivatedField())
4903 int element = Feld[x][y];
4904 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4906 if (graphic_info[graphic].anim_global_sync)
4907 GfxFrame[x][y] = FrameCounter;
4908 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4909 GfxFrame[x][y] = CustomValue[x][y];
4910 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4911 GfxFrame[x][y] = element_info[element].collect_score;
4912 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4913 GfxFrame[x][y] = ChangeDelay[x][y];
4916 static void ResetGfxAnimation(int x, int y)
4918 GfxAction[x][y] = ACTION_DEFAULT;
4919 GfxDir[x][y] = MovDir[x][y];
4922 ResetGfxFrame(x, y);
4925 static void ResetRandomAnimationValue(int x, int y)
4927 GfxRandom[x][y] = INIT_GFX_RANDOM();
4930 void InitMovingField(int x, int y, int direction)
4932 int element = Feld[x][y];
4933 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4934 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4937 boolean is_moving_before, is_moving_after;
4939 /* check if element was/is moving or being moved before/after mode change */
4940 is_moving_before = (WasJustMoving[x][y] != 0);
4941 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4943 /* reset animation only for moving elements which change direction of moving
4944 or which just started or stopped moving
4945 (else CEs with property "can move" / "not moving" are reset each frame) */
4946 if (is_moving_before != is_moving_after ||
4947 direction != MovDir[x][y])
4948 ResetGfxAnimation(x, y);
4950 MovDir[x][y] = direction;
4951 GfxDir[x][y] = direction;
4953 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4954 direction == MV_DOWN && CAN_FALL(element) ?
4955 ACTION_FALLING : ACTION_MOVING);
4957 /* this is needed for CEs with property "can move" / "not moving" */
4959 if (is_moving_after)
4961 if (Feld[newx][newy] == EL_EMPTY)
4962 Feld[newx][newy] = EL_BLOCKED;
4964 MovDir[newx][newy] = MovDir[x][y];
4966 CustomValue[newx][newy] = CustomValue[x][y];
4968 GfxFrame[newx][newy] = GfxFrame[x][y];
4969 GfxRandom[newx][newy] = GfxRandom[x][y];
4970 GfxAction[newx][newy] = GfxAction[x][y];
4971 GfxDir[newx][newy] = GfxDir[x][y];
4975 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4977 int direction = MovDir[x][y];
4978 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4979 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4985 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4987 int oldx = x, oldy = y;
4988 int direction = MovDir[x][y];
4990 if (direction == MV_LEFT)
4992 else if (direction == MV_RIGHT)
4994 else if (direction == MV_UP)
4996 else if (direction == MV_DOWN)
4999 *comes_from_x = oldx;
5000 *comes_from_y = oldy;
5003 int MovingOrBlocked2Element(int x, int y)
5005 int element = Feld[x][y];
5007 if (element == EL_BLOCKED)
5011 Blocked2Moving(x, y, &oldx, &oldy);
5012 return Feld[oldx][oldy];
5018 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5020 /* like MovingOrBlocked2Element(), but if element is moving
5021 and (x,y) is the field the moving element is just leaving,
5022 return EL_BLOCKED instead of the element value */
5023 int element = Feld[x][y];
5025 if (IS_MOVING(x, y))
5027 if (element == EL_BLOCKED)
5031 Blocked2Moving(x, y, &oldx, &oldy);
5032 return Feld[oldx][oldy];
5041 static void RemoveField(int x, int y)
5043 Feld[x][y] = EL_EMPTY;
5049 CustomValue[x][y] = 0;
5052 ChangeDelay[x][y] = 0;
5053 ChangePage[x][y] = -1;
5054 Pushed[x][y] = FALSE;
5056 GfxElement[x][y] = EL_UNDEFINED;
5057 GfxAction[x][y] = ACTION_DEFAULT;
5058 GfxDir[x][y] = MV_NONE;
5061 void RemoveMovingField(int x, int y)
5063 int oldx = x, oldy = y, newx = x, newy = y;
5064 int element = Feld[x][y];
5065 int next_element = EL_UNDEFINED;
5067 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5070 if (IS_MOVING(x, y))
5072 Moving2Blocked(x, y, &newx, &newy);
5074 if (Feld[newx][newy] != EL_BLOCKED)
5076 /* element is moving, but target field is not free (blocked), but
5077 already occupied by something different (example: acid pool);
5078 in this case, only remove the moving field, but not the target */
5080 RemoveField(oldx, oldy);
5082 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5084 TEST_DrawLevelField(oldx, oldy);
5089 else if (element == EL_BLOCKED)
5091 Blocked2Moving(x, y, &oldx, &oldy);
5092 if (!IS_MOVING(oldx, oldy))
5096 if (element == EL_BLOCKED &&
5097 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5098 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5099 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5100 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5101 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5102 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5103 next_element = get_next_element(Feld[oldx][oldy]);
5105 RemoveField(oldx, oldy);
5106 RemoveField(newx, newy);
5108 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5110 if (next_element != EL_UNDEFINED)
5111 Feld[oldx][oldy] = next_element;
5113 TEST_DrawLevelField(oldx, oldy);
5114 TEST_DrawLevelField(newx, newy);
5117 void DrawDynamite(int x, int y)
5119 int sx = SCREENX(x), sy = SCREENY(y);
5120 int graphic = el2img(Feld[x][y]);
5123 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5126 if (IS_WALKABLE_INSIDE(Back[x][y]))
5130 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5131 else if (Store[x][y])
5132 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5134 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5136 if (Back[x][y] || Store[x][y])
5137 DrawGraphicThruMask(sx, sy, graphic, frame);
5139 DrawGraphic(sx, sy, graphic, frame);
5142 void CheckDynamite(int x, int y)
5144 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5148 if (MovDelay[x][y] != 0)
5151 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5157 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5162 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5164 boolean num_checked_players = 0;
5167 for (i = 0; i < MAX_PLAYERS; i++)
5169 if (stored_player[i].active)
5171 int sx = stored_player[i].jx;
5172 int sy = stored_player[i].jy;
5174 if (num_checked_players == 0)
5181 *sx1 = MIN(*sx1, sx);
5182 *sy1 = MIN(*sy1, sy);
5183 *sx2 = MAX(*sx2, sx);
5184 *sy2 = MAX(*sy2, sy);
5187 num_checked_players++;
5192 static boolean checkIfAllPlayersFitToScreen_RND()
5194 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5196 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5198 return (sx2 - sx1 < SCR_FIELDX &&
5199 sy2 - sy1 < SCR_FIELDY);
5202 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5204 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5206 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5208 *sx = (sx1 + sx2) / 2;
5209 *sy = (sy1 + sy2) / 2;
5212 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5213 boolean center_screen, boolean quick_relocation)
5215 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5216 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5217 boolean no_delay = (tape.warp_forward);
5218 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5219 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5220 int new_scroll_x, new_scroll_y;
5222 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5224 /* case 1: quick relocation inside visible screen (without scrolling) */
5231 if (!level.shifted_relocation || center_screen)
5233 /* relocation _with_ centering of screen */
5235 new_scroll_x = SCROLL_POSITION_X(x);
5236 new_scroll_y = SCROLL_POSITION_Y(y);
5240 /* relocation _without_ centering of screen */
5242 int center_scroll_x = SCROLL_POSITION_X(old_x);
5243 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5244 int offset_x = x + (scroll_x - center_scroll_x);
5245 int offset_y = y + (scroll_y - center_scroll_y);
5247 /* for new screen position, apply previous offset to center position */
5248 new_scroll_x = SCROLL_POSITION_X(offset_x);
5249 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5252 if (quick_relocation)
5254 /* case 2: quick relocation (redraw without visible scrolling) */
5256 scroll_x = new_scroll_x;
5257 scroll_y = new_scroll_y;
5264 /* case 3: visible relocation (with scrolling to new position) */
5266 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5268 SetVideoFrameDelay(wait_delay_value);
5270 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5273 int fx = FX, fy = FY;
5275 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5276 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5278 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5284 fx += dx * TILEX / 2;
5285 fy += dy * TILEY / 2;
5287 ScrollLevel(dx, dy);
5290 /* scroll in two steps of half tile size to make things smoother */
5291 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5293 /* scroll second step to align at full tile size */
5294 BlitScreenToBitmap(window);
5300 SetVideoFrameDelay(frame_delay_value_old);
5303 void RelocatePlayer(int jx, int jy, int el_player_raw)
5305 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5306 int player_nr = GET_PLAYER_NR(el_player);
5307 struct PlayerInfo *player = &stored_player[player_nr];
5308 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5309 boolean no_delay = (tape.warp_forward);
5310 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5311 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5312 int old_jx = player->jx;
5313 int old_jy = player->jy;
5314 int old_element = Feld[old_jx][old_jy];
5315 int element = Feld[jx][jy];
5316 boolean player_relocated = (old_jx != jx || old_jy != jy);
5318 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5319 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5320 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5321 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5322 int leave_side_horiz = move_dir_horiz;
5323 int leave_side_vert = move_dir_vert;
5324 int enter_side = enter_side_horiz | enter_side_vert;
5325 int leave_side = leave_side_horiz | leave_side_vert;
5327 if (player->GameOver) /* do not reanimate dead player */
5330 if (!player_relocated) /* no need to relocate the player */
5333 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5335 RemoveField(jx, jy); /* temporarily remove newly placed player */
5336 DrawLevelField(jx, jy);
5339 if (player->present)
5341 while (player->MovPos)
5343 ScrollPlayer(player, SCROLL_GO_ON);
5344 ScrollScreen(NULL, SCROLL_GO_ON);
5346 AdvanceFrameAndPlayerCounters(player->index_nr);
5350 BackToFront_WithFrameDelay(wait_delay_value);
5353 DrawPlayer(player); /* needed here only to cleanup last field */
5354 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5356 player->is_moving = FALSE;
5359 if (IS_CUSTOM_ELEMENT(old_element))
5360 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5362 player->index_bit, leave_side);
5364 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5366 player->index_bit, leave_side);
5368 Feld[jx][jy] = el_player;
5369 InitPlayerField(jx, jy, el_player, TRUE);
5371 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5372 possible that the relocation target field did not contain a player element,
5373 but a walkable element, to which the new player was relocated -- in this
5374 case, restore that (already initialized!) element on the player field */
5375 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5377 Feld[jx][jy] = element; /* restore previously existing element */
5380 /* only visually relocate centered player */
5381 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5382 FALSE, level.instant_relocation);
5384 TestIfPlayerTouchesBadThing(jx, jy);
5385 TestIfPlayerTouchesCustomElement(jx, jy);
5387 if (IS_CUSTOM_ELEMENT(element))
5388 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5389 player->index_bit, enter_side);
5391 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5392 player->index_bit, enter_side);
5394 if (player->is_switching)
5396 /* ensure that relocation while still switching an element does not cause
5397 a new element to be treated as also switched directly after relocation
5398 (this is important for teleporter switches that teleport the player to
5399 a place where another teleporter switch is in the same direction, which
5400 would then incorrectly be treated as immediately switched before the
5401 direction key that caused the switch was released) */
5403 player->switch_x += jx - old_jx;
5404 player->switch_y += jy - old_jy;
5408 void Explode(int ex, int ey, int phase, int mode)
5414 /* !!! eliminate this variable !!! */
5415 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5417 if (game.explosions_delayed)
5419 ExplodeField[ex][ey] = mode;
5423 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5425 int center_element = Feld[ex][ey];
5426 int artwork_element, explosion_element; /* set these values later */
5428 /* remove things displayed in background while burning dynamite */
5429 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5432 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5434 /* put moving element to center field (and let it explode there) */
5435 center_element = MovingOrBlocked2Element(ex, ey);
5436 RemoveMovingField(ex, ey);
5437 Feld[ex][ey] = center_element;
5440 /* now "center_element" is finally determined -- set related values now */
5441 artwork_element = center_element; /* for custom player artwork */
5442 explosion_element = center_element; /* for custom player artwork */
5444 if (IS_PLAYER(ex, ey))
5446 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5448 artwork_element = stored_player[player_nr].artwork_element;
5450 if (level.use_explosion_element[player_nr])
5452 explosion_element = level.explosion_element[player_nr];
5453 artwork_element = explosion_element;
5457 if (mode == EX_TYPE_NORMAL ||
5458 mode == EX_TYPE_CENTER ||
5459 mode == EX_TYPE_CROSS)
5460 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5462 last_phase = element_info[explosion_element].explosion_delay + 1;
5464 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5466 int xx = x - ex + 1;
5467 int yy = y - ey + 1;
5470 if (!IN_LEV_FIELD(x, y) ||
5471 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5472 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5475 element = Feld[x][y];
5477 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5479 element = MovingOrBlocked2Element(x, y);
5481 if (!IS_EXPLOSION_PROOF(element))
5482 RemoveMovingField(x, y);
5485 /* indestructible elements can only explode in center (but not flames) */
5486 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5487 mode == EX_TYPE_BORDER)) ||
5488 element == EL_FLAMES)
5491 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5492 behaviour, for example when touching a yamyam that explodes to rocks
5493 with active deadly shield, a rock is created under the player !!! */
5494 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5496 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5497 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5498 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5500 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5503 if (IS_ACTIVE_BOMB(element))
5505 /* re-activate things under the bomb like gate or penguin */
5506 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5513 /* save walkable background elements while explosion on same tile */
5514 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5515 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5516 Back[x][y] = element;
5518 /* ignite explodable elements reached by other explosion */
5519 if (element == EL_EXPLOSION)
5520 element = Store2[x][y];
5522 if (AmoebaNr[x][y] &&
5523 (element == EL_AMOEBA_FULL ||
5524 element == EL_BD_AMOEBA ||
5525 element == EL_AMOEBA_GROWING))
5527 AmoebaCnt[AmoebaNr[x][y]]--;
5528 AmoebaCnt2[AmoebaNr[x][y]]--;
5533 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5535 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5537 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5539 if (PLAYERINFO(ex, ey)->use_murphy)
5540 Store[x][y] = EL_EMPTY;
5543 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5544 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5545 else if (ELEM_IS_PLAYER(center_element))
5546 Store[x][y] = EL_EMPTY;
5547 else if (center_element == EL_YAMYAM)
5548 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5549 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5550 Store[x][y] = element_info[center_element].content.e[xx][yy];
5552 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5553 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5554 otherwise) -- FIX THIS !!! */
5555 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5556 Store[x][y] = element_info[element].content.e[1][1];
5558 else if (!CAN_EXPLODE(element))
5559 Store[x][y] = element_info[element].content.e[1][1];
5562 Store[x][y] = EL_EMPTY;
5564 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5565 center_element == EL_AMOEBA_TO_DIAMOND)
5566 Store2[x][y] = element;
5568 Feld[x][y] = EL_EXPLOSION;
5569 GfxElement[x][y] = artwork_element;
5571 ExplodePhase[x][y] = 1;
5572 ExplodeDelay[x][y] = last_phase;
5577 if (center_element == EL_YAMYAM)
5578 game.yamyam_content_nr =
5579 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5591 GfxFrame[x][y] = 0; /* restart explosion animation */
5593 last_phase = ExplodeDelay[x][y];
5595 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5597 /* this can happen if the player leaves an explosion just in time */
5598 if (GfxElement[x][y] == EL_UNDEFINED)
5599 GfxElement[x][y] = EL_EMPTY;
5601 border_element = Store2[x][y];
5602 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5603 border_element = StorePlayer[x][y];
5605 if (phase == element_info[border_element].ignition_delay ||
5606 phase == last_phase)
5608 boolean border_explosion = FALSE;
5610 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5611 !PLAYER_EXPLOSION_PROTECTED(x, y))
5613 KillPlayerUnlessExplosionProtected(x, y);
5614 border_explosion = TRUE;
5616 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5618 Feld[x][y] = Store2[x][y];
5621 border_explosion = TRUE;
5623 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5625 AmoebeUmwandeln(x, y);
5627 border_explosion = TRUE;
5630 /* if an element just explodes due to another explosion (chain-reaction),
5631 do not immediately end the new explosion when it was the last frame of
5632 the explosion (as it would be done in the following "if"-statement!) */
5633 if (border_explosion && phase == last_phase)
5637 if (phase == last_phase)
5641 element = Feld[x][y] = Store[x][y];
5642 Store[x][y] = Store2[x][y] = 0;
5643 GfxElement[x][y] = EL_UNDEFINED;
5645 /* player can escape from explosions and might therefore be still alive */
5646 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5647 element <= EL_PLAYER_IS_EXPLODING_4)
5649 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5650 int explosion_element = EL_PLAYER_1 + player_nr;
5651 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5652 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5654 if (level.use_explosion_element[player_nr])
5655 explosion_element = level.explosion_element[player_nr];
5657 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5658 element_info[explosion_element].content.e[xx][yy]);
5661 /* restore probably existing indestructible background element */
5662 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5663 element = Feld[x][y] = Back[x][y];
5666 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5667 GfxDir[x][y] = MV_NONE;
5668 ChangeDelay[x][y] = 0;
5669 ChangePage[x][y] = -1;
5671 CustomValue[x][y] = 0;
5673 InitField_WithBug2(x, y, FALSE);
5675 TEST_DrawLevelField(x, y);
5677 TestIfElementTouchesCustomElement(x, y);
5679 if (GFX_CRUMBLED(element))
5680 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5682 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5683 StorePlayer[x][y] = 0;
5685 if (ELEM_IS_PLAYER(element))
5686 RelocatePlayer(x, y, element);
5688 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5690 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5691 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5694 TEST_DrawLevelFieldCrumbled(x, y);
5696 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5698 DrawLevelElement(x, y, Back[x][y]);
5699 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5701 else if (IS_WALKABLE_UNDER(Back[x][y]))
5703 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5704 DrawLevelElementThruMask(x, y, Back[x][y]);
5706 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5707 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5711 void DynaExplode(int ex, int ey)
5714 int dynabomb_element = Feld[ex][ey];
5715 int dynabomb_size = 1;
5716 boolean dynabomb_xl = FALSE;
5717 struct PlayerInfo *player;
5718 static int xy[4][2] =
5726 if (IS_ACTIVE_BOMB(dynabomb_element))
5728 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5729 dynabomb_size = player->dynabomb_size;
5730 dynabomb_xl = player->dynabomb_xl;
5731 player->dynabombs_left++;
5734 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5736 for (i = 0; i < NUM_DIRECTIONS; i++)
5738 for (j = 1; j <= dynabomb_size; j++)
5740 int x = ex + j * xy[i][0];
5741 int y = ey + j * xy[i][1];
5744 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5747 element = Feld[x][y];
5749 /* do not restart explosions of fields with active bombs */
5750 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5753 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5755 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5756 !IS_DIGGABLE(element) && !dynabomb_xl)
5762 void Bang(int x, int y)
5764 int element = MovingOrBlocked2Element(x, y);
5765 int explosion_type = EX_TYPE_NORMAL;
5767 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5769 struct PlayerInfo *player = PLAYERINFO(x, y);
5771 element = Feld[x][y] = player->initial_element;
5773 if (level.use_explosion_element[player->index_nr])
5775 int explosion_element = level.explosion_element[player->index_nr];
5777 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5778 explosion_type = EX_TYPE_CROSS;
5779 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5780 explosion_type = EX_TYPE_CENTER;
5788 case EL_BD_BUTTERFLY:
5791 case EL_DARK_YAMYAM:
5795 RaiseScoreElement(element);
5798 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5799 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5800 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5801 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5802 case EL_DYNABOMB_INCREASE_NUMBER:
5803 case EL_DYNABOMB_INCREASE_SIZE:
5804 case EL_DYNABOMB_INCREASE_POWER:
5805 explosion_type = EX_TYPE_DYNA;
5808 case EL_DC_LANDMINE:
5809 explosion_type = EX_TYPE_CENTER;
5814 case EL_LAMP_ACTIVE:
5815 case EL_AMOEBA_TO_DIAMOND:
5816 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5817 explosion_type = EX_TYPE_CENTER;
5821 if (element_info[element].explosion_type == EXPLODES_CROSS)
5822 explosion_type = EX_TYPE_CROSS;
5823 else if (element_info[element].explosion_type == EXPLODES_1X1)
5824 explosion_type = EX_TYPE_CENTER;
5828 if (explosion_type == EX_TYPE_DYNA)
5831 Explode(x, y, EX_PHASE_START, explosion_type);
5833 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5836 void SplashAcid(int x, int y)
5838 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5839 (!IN_LEV_FIELD(x - 1, y - 2) ||
5840 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5841 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5843 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5844 (!IN_LEV_FIELD(x + 1, y - 2) ||
5845 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5846 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5848 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5851 static void InitBeltMovement()
5853 static int belt_base_element[4] =
5855 EL_CONVEYOR_BELT_1_LEFT,
5856 EL_CONVEYOR_BELT_2_LEFT,
5857 EL_CONVEYOR_BELT_3_LEFT,
5858 EL_CONVEYOR_BELT_4_LEFT
5860 static int belt_base_active_element[4] =
5862 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5863 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5864 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5865 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5870 /* set frame order for belt animation graphic according to belt direction */
5871 for (i = 0; i < NUM_BELTS; i++)
5875 for (j = 0; j < NUM_BELT_PARTS; j++)
5877 int element = belt_base_active_element[belt_nr] + j;
5878 int graphic_1 = el2img(element);
5879 int graphic_2 = el2panelimg(element);
5881 if (game.belt_dir[i] == MV_LEFT)
5883 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5884 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5888 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5889 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5894 SCAN_PLAYFIELD(x, y)
5896 int element = Feld[x][y];
5898 for (i = 0; i < NUM_BELTS; i++)
5900 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5902 int e_belt_nr = getBeltNrFromBeltElement(element);
5905 if (e_belt_nr == belt_nr)
5907 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5909 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5916 static void ToggleBeltSwitch(int x, int y)
5918 static int belt_base_element[4] =
5920 EL_CONVEYOR_BELT_1_LEFT,
5921 EL_CONVEYOR_BELT_2_LEFT,
5922 EL_CONVEYOR_BELT_3_LEFT,
5923 EL_CONVEYOR_BELT_4_LEFT
5925 static int belt_base_active_element[4] =
5927 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5928 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5929 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5930 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5932 static int belt_base_switch_element[4] =
5934 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5935 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5936 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5937 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5939 static int belt_move_dir[4] =
5947 int element = Feld[x][y];
5948 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5949 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5950 int belt_dir = belt_move_dir[belt_dir_nr];
5953 if (!IS_BELT_SWITCH(element))
5956 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5957 game.belt_dir[belt_nr] = belt_dir;
5959 if (belt_dir_nr == 3)
5962 /* set frame order for belt animation graphic according to belt direction */
5963 for (i = 0; i < NUM_BELT_PARTS; i++)
5965 int element = belt_base_active_element[belt_nr] + i;
5966 int graphic_1 = el2img(element);
5967 int graphic_2 = el2panelimg(element);
5969 if (belt_dir == MV_LEFT)
5971 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5972 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5976 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5977 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5981 SCAN_PLAYFIELD(xx, yy)
5983 int element = Feld[xx][yy];
5985 if (IS_BELT_SWITCH(element))
5987 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5989 if (e_belt_nr == belt_nr)
5991 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5992 TEST_DrawLevelField(xx, yy);
5995 else if (IS_BELT(element) && belt_dir != MV_NONE)
5997 int e_belt_nr = getBeltNrFromBeltElement(element);
5999 if (e_belt_nr == belt_nr)
6001 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6003 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6004 TEST_DrawLevelField(xx, yy);
6007 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6009 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6011 if (e_belt_nr == belt_nr)
6013 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6015 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6016 TEST_DrawLevelField(xx, yy);
6022 static void ToggleSwitchgateSwitch(int x, int y)
6026 game.switchgate_pos = !game.switchgate_pos;
6028 SCAN_PLAYFIELD(xx, yy)
6030 int element = Feld[xx][yy];
6032 if (element == EL_SWITCHGATE_SWITCH_UP)
6034 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6035 TEST_DrawLevelField(xx, yy);
6037 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6039 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6040 TEST_DrawLevelField(xx, yy);
6042 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6044 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6045 TEST_DrawLevelField(xx, yy);
6047 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6049 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6050 TEST_DrawLevelField(xx, yy);
6052 else if (element == EL_SWITCHGATE_OPEN ||
6053 element == EL_SWITCHGATE_OPENING)
6055 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6057 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6059 else if (element == EL_SWITCHGATE_CLOSED ||
6060 element == EL_SWITCHGATE_CLOSING)
6062 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6064 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6069 static int getInvisibleActiveFromInvisibleElement(int element)
6071 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6072 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6073 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6077 static int getInvisibleFromInvisibleActiveElement(int element)
6079 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6080 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6081 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6085 static void RedrawAllLightSwitchesAndInvisibleElements()
6089 SCAN_PLAYFIELD(x, y)
6091 int element = Feld[x][y];
6093 if (element == EL_LIGHT_SWITCH &&
6094 game.light_time_left > 0)
6096 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6097 TEST_DrawLevelField(x, y);
6099 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6100 game.light_time_left == 0)
6102 Feld[x][y] = EL_LIGHT_SWITCH;
6103 TEST_DrawLevelField(x, y);
6105 else if (element == EL_EMC_DRIPPER &&
6106 game.light_time_left > 0)
6108 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6109 TEST_DrawLevelField(x, y);
6111 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6112 game.light_time_left == 0)
6114 Feld[x][y] = EL_EMC_DRIPPER;
6115 TEST_DrawLevelField(x, y);
6117 else if (element == EL_INVISIBLE_STEELWALL ||
6118 element == EL_INVISIBLE_WALL ||
6119 element == EL_INVISIBLE_SAND)
6121 if (game.light_time_left > 0)
6122 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6124 TEST_DrawLevelField(x, y);
6126 /* uncrumble neighbour fields, if needed */
6127 if (element == EL_INVISIBLE_SAND)
6128 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6130 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6131 element == EL_INVISIBLE_WALL_ACTIVE ||
6132 element == EL_INVISIBLE_SAND_ACTIVE)
6134 if (game.light_time_left == 0)
6135 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6137 TEST_DrawLevelField(x, y);
6139 /* re-crumble neighbour fields, if needed */
6140 if (element == EL_INVISIBLE_SAND)
6141 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6146 static void RedrawAllInvisibleElementsForLenses()
6150 SCAN_PLAYFIELD(x, y)
6152 int element = Feld[x][y];
6154 if (element == EL_EMC_DRIPPER &&
6155 game.lenses_time_left > 0)
6157 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6158 TEST_DrawLevelField(x, y);
6160 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6161 game.lenses_time_left == 0)
6163 Feld[x][y] = EL_EMC_DRIPPER;
6164 TEST_DrawLevelField(x, y);
6166 else if (element == EL_INVISIBLE_STEELWALL ||
6167 element == EL_INVISIBLE_WALL ||
6168 element == EL_INVISIBLE_SAND)
6170 if (game.lenses_time_left > 0)
6171 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6173 TEST_DrawLevelField(x, y);
6175 /* uncrumble neighbour fields, if needed */
6176 if (element == EL_INVISIBLE_SAND)
6177 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6179 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6180 element == EL_INVISIBLE_WALL_ACTIVE ||
6181 element == EL_INVISIBLE_SAND_ACTIVE)
6183 if (game.lenses_time_left == 0)
6184 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6186 TEST_DrawLevelField(x, y);
6188 /* re-crumble neighbour fields, if needed */
6189 if (element == EL_INVISIBLE_SAND)
6190 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6195 static void RedrawAllInvisibleElementsForMagnifier()
6199 SCAN_PLAYFIELD(x, y)
6201 int element = Feld[x][y];
6203 if (element == EL_EMC_FAKE_GRASS &&
6204 game.magnify_time_left > 0)
6206 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6207 TEST_DrawLevelField(x, y);
6209 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6210 game.magnify_time_left == 0)
6212 Feld[x][y] = EL_EMC_FAKE_GRASS;
6213 TEST_DrawLevelField(x, y);
6215 else if (IS_GATE_GRAY(element) &&
6216 game.magnify_time_left > 0)
6218 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6219 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6220 IS_EM_GATE_GRAY(element) ?
6221 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6222 IS_EMC_GATE_GRAY(element) ?
6223 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6224 IS_DC_GATE_GRAY(element) ?
6225 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6227 TEST_DrawLevelField(x, y);
6229 else if (IS_GATE_GRAY_ACTIVE(element) &&
6230 game.magnify_time_left == 0)
6232 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6233 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6234 IS_EM_GATE_GRAY_ACTIVE(element) ?
6235 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6236 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6237 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6238 IS_DC_GATE_GRAY_ACTIVE(element) ?
6239 EL_DC_GATE_WHITE_GRAY :
6241 TEST_DrawLevelField(x, y);
6246 static void ToggleLightSwitch(int x, int y)
6248 int element = Feld[x][y];
6250 game.light_time_left =
6251 (element == EL_LIGHT_SWITCH ?
6252 level.time_light * FRAMES_PER_SECOND : 0);
6254 RedrawAllLightSwitchesAndInvisibleElements();
6257 static void ActivateTimegateSwitch(int x, int y)
6261 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6263 SCAN_PLAYFIELD(xx, yy)
6265 int element = Feld[xx][yy];
6267 if (element == EL_TIMEGATE_CLOSED ||
6268 element == EL_TIMEGATE_CLOSING)
6270 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6271 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6275 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6277 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6278 TEST_DrawLevelField(xx, yy);
6284 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6285 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6288 void Impact(int x, int y)
6290 boolean last_line = (y == lev_fieldy - 1);
6291 boolean object_hit = FALSE;
6292 boolean impact = (last_line || object_hit);
6293 int element = Feld[x][y];
6294 int smashed = EL_STEELWALL;
6296 if (!last_line) /* check if element below was hit */
6298 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6301 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6302 MovDir[x][y + 1] != MV_DOWN ||
6303 MovPos[x][y + 1] <= TILEY / 2));
6305 /* do not smash moving elements that left the smashed field in time */
6306 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6307 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6310 #if USE_QUICKSAND_IMPACT_BUGFIX
6311 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6313 RemoveMovingField(x, y + 1);
6314 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6315 Feld[x][y + 2] = EL_ROCK;
6316 TEST_DrawLevelField(x, y + 2);
6321 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6323 RemoveMovingField(x, y + 1);
6324 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6325 Feld[x][y + 2] = EL_ROCK;
6326 TEST_DrawLevelField(x, y + 2);
6333 smashed = MovingOrBlocked2Element(x, y + 1);
6335 impact = (last_line || object_hit);
6338 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6340 SplashAcid(x, y + 1);
6344 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6345 /* only reset graphic animation if graphic really changes after impact */
6347 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6349 ResetGfxAnimation(x, y);
6350 TEST_DrawLevelField(x, y);
6353 if (impact && CAN_EXPLODE_IMPACT(element))
6358 else if (impact && element == EL_PEARL &&
6359 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6361 ResetGfxAnimation(x, y);
6363 Feld[x][y] = EL_PEARL_BREAKING;
6364 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6367 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6369 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6374 if (impact && element == EL_AMOEBA_DROP)
6376 if (object_hit && IS_PLAYER(x, y + 1))
6377 KillPlayerUnlessEnemyProtected(x, y + 1);
6378 else if (object_hit && smashed == EL_PENGUIN)
6382 Feld[x][y] = EL_AMOEBA_GROWING;
6383 Store[x][y] = EL_AMOEBA_WET;
6385 ResetRandomAnimationValue(x, y);
6390 if (object_hit) /* check which object was hit */
6392 if ((CAN_PASS_MAGIC_WALL(element) &&
6393 (smashed == EL_MAGIC_WALL ||
6394 smashed == EL_BD_MAGIC_WALL)) ||
6395 (CAN_PASS_DC_MAGIC_WALL(element) &&
6396 smashed == EL_DC_MAGIC_WALL))
6399 int activated_magic_wall =
6400 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6401 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6402 EL_DC_MAGIC_WALL_ACTIVE);
6404 /* activate magic wall / mill */
6405 SCAN_PLAYFIELD(xx, yy)
6407 if (Feld[xx][yy] == smashed)
6408 Feld[xx][yy] = activated_magic_wall;
6411 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6412 game.magic_wall_active = TRUE;
6414 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6415 SND_MAGIC_WALL_ACTIVATING :
6416 smashed == EL_BD_MAGIC_WALL ?
6417 SND_BD_MAGIC_WALL_ACTIVATING :
6418 SND_DC_MAGIC_WALL_ACTIVATING));
6421 if (IS_PLAYER(x, y + 1))
6423 if (CAN_SMASH_PLAYER(element))
6425 KillPlayerUnlessEnemyProtected(x, y + 1);
6429 else if (smashed == EL_PENGUIN)
6431 if (CAN_SMASH_PLAYER(element))
6437 else if (element == EL_BD_DIAMOND)
6439 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6445 else if (((element == EL_SP_INFOTRON ||
6446 element == EL_SP_ZONK) &&
6447 (smashed == EL_SP_SNIKSNAK ||
6448 smashed == EL_SP_ELECTRON ||
6449 smashed == EL_SP_DISK_ORANGE)) ||
6450 (element == EL_SP_INFOTRON &&
6451 smashed == EL_SP_DISK_YELLOW))
6456 else if (CAN_SMASH_EVERYTHING(element))
6458 if (IS_CLASSIC_ENEMY(smashed) ||
6459 CAN_EXPLODE_SMASHED(smashed))
6464 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6466 if (smashed == EL_LAMP ||
6467 smashed == EL_LAMP_ACTIVE)
6472 else if (smashed == EL_NUT)
6474 Feld[x][y + 1] = EL_NUT_BREAKING;
6475 PlayLevelSound(x, y, SND_NUT_BREAKING);
6476 RaiseScoreElement(EL_NUT);
6479 else if (smashed == EL_PEARL)
6481 ResetGfxAnimation(x, y);
6483 Feld[x][y + 1] = EL_PEARL_BREAKING;
6484 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6487 else if (smashed == EL_DIAMOND)
6489 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6490 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6493 else if (IS_BELT_SWITCH(smashed))
6495 ToggleBeltSwitch(x, y + 1);
6497 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6498 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6499 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6500 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6502 ToggleSwitchgateSwitch(x, y + 1);
6504 else if (smashed == EL_LIGHT_SWITCH ||
6505 smashed == EL_LIGHT_SWITCH_ACTIVE)
6507 ToggleLightSwitch(x, y + 1);
6511 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6513 CheckElementChangeBySide(x, y + 1, smashed, element,
6514 CE_SWITCHED, CH_SIDE_TOP);
6515 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6521 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6526 /* play sound of magic wall / mill */
6528 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6529 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6530 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6532 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6533 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6534 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6535 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6536 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6537 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6542 /* play sound of object that hits the ground */
6543 if (last_line || object_hit)
6544 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6547 inline static void TurnRoundExt(int x, int y)
6559 { 0, 0 }, { 0, 0 }, { 0, 0 },
6564 int left, right, back;
6568 { MV_DOWN, MV_UP, MV_RIGHT },
6569 { MV_UP, MV_DOWN, MV_LEFT },
6571 { MV_LEFT, MV_RIGHT, MV_DOWN },
6575 { MV_RIGHT, MV_LEFT, MV_UP }
6578 int element = Feld[x][y];
6579 int move_pattern = element_info[element].move_pattern;
6581 int old_move_dir = MovDir[x][y];
6582 int left_dir = turn[old_move_dir].left;
6583 int right_dir = turn[old_move_dir].right;
6584 int back_dir = turn[old_move_dir].back;
6586 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6587 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6588 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6589 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6591 int left_x = x + left_dx, left_y = y + left_dy;
6592 int right_x = x + right_dx, right_y = y + right_dy;
6593 int move_x = x + move_dx, move_y = y + move_dy;
6597 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6599 TestIfBadThingTouchesOtherBadThing(x, y);
6601 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6602 MovDir[x][y] = right_dir;
6603 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6604 MovDir[x][y] = left_dir;
6606 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6608 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6611 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6613 TestIfBadThingTouchesOtherBadThing(x, y);
6615 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6616 MovDir[x][y] = left_dir;
6617 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6618 MovDir[x][y] = right_dir;
6620 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6622 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6625 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6627 TestIfBadThingTouchesOtherBadThing(x, y);
6629 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6630 MovDir[x][y] = left_dir;
6631 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6632 MovDir[x][y] = right_dir;
6634 if (MovDir[x][y] != old_move_dir)
6637 else if (element == EL_YAMYAM)
6639 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6640 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6642 if (can_turn_left && can_turn_right)
6643 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6644 else if (can_turn_left)
6645 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6646 else if (can_turn_right)
6647 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6649 MovDir[x][y] = back_dir;
6651 MovDelay[x][y] = 16 + 16 * RND(3);
6653 else if (element == EL_DARK_YAMYAM)
6655 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6657 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6660 if (can_turn_left && can_turn_right)
6661 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6662 else if (can_turn_left)
6663 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6664 else if (can_turn_right)
6665 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6667 MovDir[x][y] = back_dir;
6669 MovDelay[x][y] = 16 + 16 * RND(3);
6671 else if (element == EL_PACMAN)
6673 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6674 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6676 if (can_turn_left && can_turn_right)
6677 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6678 else if (can_turn_left)
6679 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6680 else if (can_turn_right)
6681 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6683 MovDir[x][y] = back_dir;
6685 MovDelay[x][y] = 6 + RND(40);
6687 else if (element == EL_PIG)
6689 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6690 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6691 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6692 boolean should_turn_left, should_turn_right, should_move_on;
6694 int rnd = RND(rnd_value);
6696 should_turn_left = (can_turn_left &&
6698 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6699 y + back_dy + left_dy)));
6700 should_turn_right = (can_turn_right &&
6702 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6703 y + back_dy + right_dy)));
6704 should_move_on = (can_move_on &&
6707 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6708 y + move_dy + left_dy) ||
6709 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6710 y + move_dy + right_dy)));
6712 if (should_turn_left || should_turn_right || should_move_on)
6714 if (should_turn_left && should_turn_right && should_move_on)
6715 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6716 rnd < 2 * rnd_value / 3 ? right_dir :
6718 else if (should_turn_left && should_turn_right)
6719 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6720 else if (should_turn_left && should_move_on)
6721 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6722 else if (should_turn_right && should_move_on)
6723 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6724 else if (should_turn_left)
6725 MovDir[x][y] = left_dir;
6726 else if (should_turn_right)
6727 MovDir[x][y] = right_dir;
6728 else if (should_move_on)
6729 MovDir[x][y] = old_move_dir;
6731 else if (can_move_on && rnd > rnd_value / 8)
6732 MovDir[x][y] = old_move_dir;
6733 else if (can_turn_left && can_turn_right)
6734 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6735 else if (can_turn_left && rnd > rnd_value / 8)
6736 MovDir[x][y] = left_dir;
6737 else if (can_turn_right && rnd > rnd_value/8)
6738 MovDir[x][y] = right_dir;
6740 MovDir[x][y] = back_dir;
6742 xx = x + move_xy[MovDir[x][y]].dx;
6743 yy = y + move_xy[MovDir[x][y]].dy;
6745 if (!IN_LEV_FIELD(xx, yy) ||
6746 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6747 MovDir[x][y] = old_move_dir;
6751 else if (element == EL_DRAGON)
6753 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6754 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6755 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6757 int rnd = RND(rnd_value);
6759 if (can_move_on && rnd > rnd_value / 8)
6760 MovDir[x][y] = old_move_dir;
6761 else if (can_turn_left && can_turn_right)
6762 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6763 else if (can_turn_left && rnd > rnd_value / 8)
6764 MovDir[x][y] = left_dir;
6765 else if (can_turn_right && rnd > rnd_value / 8)
6766 MovDir[x][y] = right_dir;
6768 MovDir[x][y] = back_dir;
6770 xx = x + move_xy[MovDir[x][y]].dx;
6771 yy = y + move_xy[MovDir[x][y]].dy;
6773 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6774 MovDir[x][y] = old_move_dir;
6778 else if (element == EL_MOLE)
6780 boolean can_move_on =
6781 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6782 IS_AMOEBOID(Feld[move_x][move_y]) ||
6783 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6786 boolean can_turn_left =
6787 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6788 IS_AMOEBOID(Feld[left_x][left_y])));
6790 boolean can_turn_right =
6791 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6792 IS_AMOEBOID(Feld[right_x][right_y])));
6794 if (can_turn_left && can_turn_right)
6795 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6796 else if (can_turn_left)
6797 MovDir[x][y] = left_dir;
6799 MovDir[x][y] = right_dir;
6802 if (MovDir[x][y] != old_move_dir)
6805 else if (element == EL_BALLOON)
6807 MovDir[x][y] = game.wind_direction;
6810 else if (element == EL_SPRING)
6812 if (MovDir[x][y] & MV_HORIZONTAL)
6814 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6815 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6817 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6818 ResetGfxAnimation(move_x, move_y);
6819 TEST_DrawLevelField(move_x, move_y);
6821 MovDir[x][y] = back_dir;
6823 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6824 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6825 MovDir[x][y] = MV_NONE;
6830 else if (element == EL_ROBOT ||
6831 element == EL_SATELLITE ||
6832 element == EL_PENGUIN ||
6833 element == EL_EMC_ANDROID)
6835 int attr_x = -1, attr_y = -1;
6846 for (i = 0; i < MAX_PLAYERS; i++)
6848 struct PlayerInfo *player = &stored_player[i];
6849 int jx = player->jx, jy = player->jy;
6851 if (!player->active)
6855 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6863 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6864 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6865 game.engine_version < VERSION_IDENT(3,1,0,0)))
6871 if (element == EL_PENGUIN)
6874 static int xy[4][2] =
6882 for (i = 0; i < NUM_DIRECTIONS; i++)
6884 int ex = x + xy[i][0];
6885 int ey = y + xy[i][1];
6887 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6888 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6889 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6890 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6899 MovDir[x][y] = MV_NONE;
6901 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6902 else if (attr_x > x)
6903 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6905 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6906 else if (attr_y > y)
6907 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6909 if (element == EL_ROBOT)
6913 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6914 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6915 Moving2Blocked(x, y, &newx, &newy);
6917 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6918 MovDelay[x][y] = 8 + 8 * !RND(3);
6920 MovDelay[x][y] = 16;
6922 else if (element == EL_PENGUIN)
6928 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6930 boolean first_horiz = RND(2);
6931 int new_move_dir = MovDir[x][y];
6934 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6935 Moving2Blocked(x, y, &newx, &newy);
6937 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6941 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6942 Moving2Blocked(x, y, &newx, &newy);
6944 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6947 MovDir[x][y] = old_move_dir;
6951 else if (element == EL_SATELLITE)
6957 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6959 boolean first_horiz = RND(2);
6960 int new_move_dir = MovDir[x][y];
6963 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6964 Moving2Blocked(x, y, &newx, &newy);
6966 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6970 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6971 Moving2Blocked(x, y, &newx, &newy);
6973 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6976 MovDir[x][y] = old_move_dir;
6980 else if (element == EL_EMC_ANDROID)
6982 static int check_pos[16] =
6984 -1, /* 0 => (invalid) */
6985 7, /* 1 => MV_LEFT */
6986 3, /* 2 => MV_RIGHT */
6987 -1, /* 3 => (invalid) */
6989 0, /* 5 => MV_LEFT | MV_UP */
6990 2, /* 6 => MV_RIGHT | MV_UP */
6991 -1, /* 7 => (invalid) */
6992 5, /* 8 => MV_DOWN */
6993 6, /* 9 => MV_LEFT | MV_DOWN */
6994 4, /* 10 => MV_RIGHT | MV_DOWN */
6995 -1, /* 11 => (invalid) */
6996 -1, /* 12 => (invalid) */
6997 -1, /* 13 => (invalid) */
6998 -1, /* 14 => (invalid) */
6999 -1, /* 15 => (invalid) */
7007 { -1, -1, MV_LEFT | MV_UP },
7009 { +1, -1, MV_RIGHT | MV_UP },
7010 { +1, 0, MV_RIGHT },
7011 { +1, +1, MV_RIGHT | MV_DOWN },
7013 { -1, +1, MV_LEFT | MV_DOWN },
7016 int start_pos, check_order;
7017 boolean can_clone = FALSE;
7020 /* check if there is any free field around current position */
7021 for (i = 0; i < 8; i++)
7023 int newx = x + check_xy[i].dx;
7024 int newy = y + check_xy[i].dy;
7026 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7034 if (can_clone) /* randomly find an element to clone */
7038 start_pos = check_pos[RND(8)];
7039 check_order = (RND(2) ? -1 : +1);
7041 for (i = 0; i < 8; i++)
7043 int pos_raw = start_pos + i * check_order;
7044 int pos = (pos_raw + 8) % 8;
7045 int newx = x + check_xy[pos].dx;
7046 int newy = y + check_xy[pos].dy;
7048 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7050 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7051 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7053 Store[x][y] = Feld[newx][newy];
7062 if (can_clone) /* randomly find a direction to move */
7066 start_pos = check_pos[RND(8)];
7067 check_order = (RND(2) ? -1 : +1);
7069 for (i = 0; i < 8; i++)
7071 int pos_raw = start_pos + i * check_order;
7072 int pos = (pos_raw + 8) % 8;
7073 int newx = x + check_xy[pos].dx;
7074 int newy = y + check_xy[pos].dy;
7075 int new_move_dir = check_xy[pos].dir;
7077 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7079 MovDir[x][y] = new_move_dir;
7080 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7089 if (can_clone) /* cloning and moving successful */
7092 /* cannot clone -- try to move towards player */
7094 start_pos = check_pos[MovDir[x][y] & 0x0f];
7095 check_order = (RND(2) ? -1 : +1);
7097 for (i = 0; i < 3; i++)
7099 /* first check start_pos, then previous/next or (next/previous) pos */
7100 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7101 int pos = (pos_raw + 8) % 8;
7102 int newx = x + check_xy[pos].dx;
7103 int newy = y + check_xy[pos].dy;
7104 int new_move_dir = check_xy[pos].dir;
7106 if (IS_PLAYER(newx, newy))
7109 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7111 MovDir[x][y] = new_move_dir;
7112 MovDelay[x][y] = level.android_move_time * 8 + 1;
7119 else if (move_pattern == MV_TURNING_LEFT ||
7120 move_pattern == MV_TURNING_RIGHT ||
7121 move_pattern == MV_TURNING_LEFT_RIGHT ||
7122 move_pattern == MV_TURNING_RIGHT_LEFT ||
7123 move_pattern == MV_TURNING_RANDOM ||
7124 move_pattern == MV_ALL_DIRECTIONS)
7126 boolean can_turn_left =
7127 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7128 boolean can_turn_right =
7129 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7131 if (element_info[element].move_stepsize == 0) /* "not moving" */
7134 if (move_pattern == MV_TURNING_LEFT)
7135 MovDir[x][y] = left_dir;
7136 else if (move_pattern == MV_TURNING_RIGHT)
7137 MovDir[x][y] = right_dir;
7138 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7139 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7140 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7141 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7142 else if (move_pattern == MV_TURNING_RANDOM)
7143 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7144 can_turn_right && !can_turn_left ? right_dir :
7145 RND(2) ? left_dir : right_dir);
7146 else if (can_turn_left && can_turn_right)
7147 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7148 else if (can_turn_left)
7149 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7150 else if (can_turn_right)
7151 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7153 MovDir[x][y] = back_dir;
7155 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7157 else if (move_pattern == MV_HORIZONTAL ||
7158 move_pattern == MV_VERTICAL)
7160 if (move_pattern & old_move_dir)
7161 MovDir[x][y] = back_dir;
7162 else if (move_pattern == MV_HORIZONTAL)
7163 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7164 else if (move_pattern == MV_VERTICAL)
7165 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7167 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7169 else if (move_pattern & MV_ANY_DIRECTION)
7171 MovDir[x][y] = move_pattern;
7172 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7174 else if (move_pattern & MV_WIND_DIRECTION)
7176 MovDir[x][y] = game.wind_direction;
7177 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7179 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7181 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7182 MovDir[x][y] = left_dir;
7183 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7184 MovDir[x][y] = right_dir;
7186 if (MovDir[x][y] != old_move_dir)
7187 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7189 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7191 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7192 MovDir[x][y] = right_dir;
7193 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7194 MovDir[x][y] = left_dir;
7196 if (MovDir[x][y] != old_move_dir)
7197 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7199 else if (move_pattern == MV_TOWARDS_PLAYER ||
7200 move_pattern == MV_AWAY_FROM_PLAYER)
7202 int attr_x = -1, attr_y = -1;
7204 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7215 for (i = 0; i < MAX_PLAYERS; i++)
7217 struct PlayerInfo *player = &stored_player[i];
7218 int jx = player->jx, jy = player->jy;
7220 if (!player->active)
7224 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7232 MovDir[x][y] = MV_NONE;
7234 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7235 else if (attr_x > x)
7236 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7238 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7239 else if (attr_y > y)
7240 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7242 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7244 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7246 boolean first_horiz = RND(2);
7247 int new_move_dir = MovDir[x][y];
7249 if (element_info[element].move_stepsize == 0) /* "not moving" */
7251 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7252 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7258 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7259 Moving2Blocked(x, y, &newx, &newy);
7261 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7265 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7266 Moving2Blocked(x, y, &newx, &newy);
7268 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7271 MovDir[x][y] = old_move_dir;
7274 else if (move_pattern == MV_WHEN_PUSHED ||
7275 move_pattern == MV_WHEN_DROPPED)
7277 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7278 MovDir[x][y] = MV_NONE;
7282 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7284 static int test_xy[7][2] =
7294 static int test_dir[7] =
7304 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7305 int move_preference = -1000000; /* start with very low preference */
7306 int new_move_dir = MV_NONE;
7307 int start_test = RND(4);
7310 for (i = 0; i < NUM_DIRECTIONS; i++)
7312 int move_dir = test_dir[start_test + i];
7313 int move_dir_preference;
7315 xx = x + test_xy[start_test + i][0];
7316 yy = y + test_xy[start_test + i][1];
7318 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7319 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7321 new_move_dir = move_dir;
7326 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7329 move_dir_preference = -1 * RunnerVisit[xx][yy];
7330 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7331 move_dir_preference = PlayerVisit[xx][yy];
7333 if (move_dir_preference > move_preference)
7335 /* prefer field that has not been visited for the longest time */
7336 move_preference = move_dir_preference;
7337 new_move_dir = move_dir;
7339 else if (move_dir_preference == move_preference &&
7340 move_dir == old_move_dir)
7342 /* prefer last direction when all directions are preferred equally */
7343 move_preference = move_dir_preference;
7344 new_move_dir = move_dir;
7348 MovDir[x][y] = new_move_dir;
7349 if (old_move_dir != new_move_dir)
7350 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7354 static void TurnRound(int x, int y)
7356 int direction = MovDir[x][y];
7360 GfxDir[x][y] = MovDir[x][y];
7362 if (direction != MovDir[x][y])
7366 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7368 ResetGfxFrame(x, y);
7371 static boolean JustBeingPushed(int x, int y)
7375 for (i = 0; i < MAX_PLAYERS; i++)
7377 struct PlayerInfo *player = &stored_player[i];
7379 if (player->active && player->is_pushing && player->MovPos)
7381 int next_jx = player->jx + (player->jx - player->last_jx);
7382 int next_jy = player->jy + (player->jy - player->last_jy);
7384 if (x == next_jx && y == next_jy)
7392 void StartMoving(int x, int y)
7394 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7395 int element = Feld[x][y];
7400 if (MovDelay[x][y] == 0)
7401 GfxAction[x][y] = ACTION_DEFAULT;
7403 if (CAN_FALL(element) && y < lev_fieldy - 1)
7405 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7406 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7407 if (JustBeingPushed(x, y))
7410 if (element == EL_QUICKSAND_FULL)
7412 if (IS_FREE(x, y + 1))
7414 InitMovingField(x, y, MV_DOWN);
7415 started_moving = TRUE;
7417 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7418 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7419 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7420 Store[x][y] = EL_ROCK;
7422 Store[x][y] = EL_ROCK;
7425 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7427 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7429 if (!MovDelay[x][y])
7431 MovDelay[x][y] = TILEY + 1;
7433 ResetGfxAnimation(x, y);
7434 ResetGfxAnimation(x, y + 1);
7439 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7440 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7447 Feld[x][y] = EL_QUICKSAND_EMPTY;
7448 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7449 Store[x][y + 1] = Store[x][y];
7452 PlayLevelSoundAction(x, y, ACTION_FILLING);
7454 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7456 if (!MovDelay[x][y])
7458 MovDelay[x][y] = TILEY + 1;
7460 ResetGfxAnimation(x, y);
7461 ResetGfxAnimation(x, y + 1);
7466 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7467 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7474 Feld[x][y] = EL_QUICKSAND_EMPTY;
7475 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7476 Store[x][y + 1] = Store[x][y];
7479 PlayLevelSoundAction(x, y, ACTION_FILLING);
7482 else if (element == EL_QUICKSAND_FAST_FULL)
7484 if (IS_FREE(x, y + 1))
7486 InitMovingField(x, y, MV_DOWN);
7487 started_moving = TRUE;
7489 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7490 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7491 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7492 Store[x][y] = EL_ROCK;
7494 Store[x][y] = EL_ROCK;
7497 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7499 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7501 if (!MovDelay[x][y])
7503 MovDelay[x][y] = TILEY + 1;
7505 ResetGfxAnimation(x, y);
7506 ResetGfxAnimation(x, y + 1);
7511 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7512 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7519 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7520 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7521 Store[x][y + 1] = Store[x][y];
7524 PlayLevelSoundAction(x, y, ACTION_FILLING);
7526 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7528 if (!MovDelay[x][y])
7530 MovDelay[x][y] = TILEY + 1;
7532 ResetGfxAnimation(x, y);
7533 ResetGfxAnimation(x, y + 1);
7538 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7539 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7546 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7547 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7548 Store[x][y + 1] = Store[x][y];
7551 PlayLevelSoundAction(x, y, ACTION_FILLING);
7554 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7555 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7557 InitMovingField(x, y, MV_DOWN);
7558 started_moving = TRUE;
7560 Feld[x][y] = EL_QUICKSAND_FILLING;
7561 Store[x][y] = element;
7563 PlayLevelSoundAction(x, y, ACTION_FILLING);
7565 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7566 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7568 InitMovingField(x, y, MV_DOWN);
7569 started_moving = TRUE;
7571 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7572 Store[x][y] = element;
7574 PlayLevelSoundAction(x, y, ACTION_FILLING);
7576 else if (element == EL_MAGIC_WALL_FULL)
7578 if (IS_FREE(x, y + 1))
7580 InitMovingField(x, y, MV_DOWN);
7581 started_moving = TRUE;
7583 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7584 Store[x][y] = EL_CHANGED(Store[x][y]);
7586 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7588 if (!MovDelay[x][y])
7589 MovDelay[x][y] = TILEY / 4 + 1;
7598 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7599 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7600 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7604 else if (element == EL_BD_MAGIC_WALL_FULL)
7606 if (IS_FREE(x, y + 1))
7608 InitMovingField(x, y, MV_DOWN);
7609 started_moving = TRUE;
7611 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7612 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7614 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7616 if (!MovDelay[x][y])
7617 MovDelay[x][y] = TILEY / 4 + 1;
7626 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7627 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7628 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7632 else if (element == EL_DC_MAGIC_WALL_FULL)
7634 if (IS_FREE(x, y + 1))
7636 InitMovingField(x, y, MV_DOWN);
7637 started_moving = TRUE;
7639 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7640 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7642 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7644 if (!MovDelay[x][y])
7645 MovDelay[x][y] = TILEY / 4 + 1;
7654 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7655 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7656 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7660 else if ((CAN_PASS_MAGIC_WALL(element) &&
7661 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7662 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7663 (CAN_PASS_DC_MAGIC_WALL(element) &&
7664 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7667 InitMovingField(x, y, MV_DOWN);
7668 started_moving = TRUE;
7671 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7672 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7673 EL_DC_MAGIC_WALL_FILLING);
7674 Store[x][y] = element;
7676 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7678 SplashAcid(x, y + 1);
7680 InitMovingField(x, y, MV_DOWN);
7681 started_moving = TRUE;
7683 Store[x][y] = EL_ACID;
7686 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7687 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7688 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7689 CAN_FALL(element) && WasJustFalling[x][y] &&
7690 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7692 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7693 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7694 (Feld[x][y + 1] == EL_BLOCKED)))
7696 /* this is needed for a special case not covered by calling "Impact()"
7697 from "ContinueMoving()": if an element moves to a tile directly below
7698 another element which was just falling on that tile (which was empty
7699 in the previous frame), the falling element above would just stop
7700 instead of smashing the element below (in previous version, the above
7701 element was just checked for "moving" instead of "falling", resulting
7702 in incorrect smashes caused by horizontal movement of the above
7703 element; also, the case of the player being the element to smash was
7704 simply not covered here... :-/ ) */
7706 CheckCollision[x][y] = 0;
7707 CheckImpact[x][y] = 0;
7711 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7713 if (MovDir[x][y] == MV_NONE)
7715 InitMovingField(x, y, MV_DOWN);
7716 started_moving = TRUE;
7719 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7721 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7722 MovDir[x][y] = MV_DOWN;
7724 InitMovingField(x, y, MV_DOWN);
7725 started_moving = TRUE;
7727 else if (element == EL_AMOEBA_DROP)
7729 Feld[x][y] = EL_AMOEBA_GROWING;
7730 Store[x][y] = EL_AMOEBA_WET;
7732 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7733 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7734 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7735 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7737 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7738 (IS_FREE(x - 1, y + 1) ||
7739 Feld[x - 1][y + 1] == EL_ACID));
7740 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7741 (IS_FREE(x + 1, y + 1) ||
7742 Feld[x + 1][y + 1] == EL_ACID));
7743 boolean can_fall_any = (can_fall_left || can_fall_right);
7744 boolean can_fall_both = (can_fall_left && can_fall_right);
7745 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7747 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7749 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7750 can_fall_right = FALSE;
7751 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7752 can_fall_left = FALSE;
7753 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7754 can_fall_right = FALSE;
7755 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7756 can_fall_left = FALSE;
7758 can_fall_any = (can_fall_left || can_fall_right);
7759 can_fall_both = FALSE;
7764 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7765 can_fall_right = FALSE; /* slip down on left side */
7767 can_fall_left = !(can_fall_right = RND(2));
7769 can_fall_both = FALSE;
7774 /* if not determined otherwise, prefer left side for slipping down */
7775 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7776 started_moving = TRUE;
7779 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7781 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7782 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7783 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7784 int belt_dir = game.belt_dir[belt_nr];
7786 if ((belt_dir == MV_LEFT && left_is_free) ||
7787 (belt_dir == MV_RIGHT && right_is_free))
7789 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7791 InitMovingField(x, y, belt_dir);
7792 started_moving = TRUE;
7794 Pushed[x][y] = TRUE;
7795 Pushed[nextx][y] = TRUE;
7797 GfxAction[x][y] = ACTION_DEFAULT;
7801 MovDir[x][y] = 0; /* if element was moving, stop it */
7806 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7807 if (CAN_MOVE(element) && !started_moving)
7809 int move_pattern = element_info[element].move_pattern;
7812 Moving2Blocked(x, y, &newx, &newy);
7814 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7817 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7818 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7820 WasJustMoving[x][y] = 0;
7821 CheckCollision[x][y] = 0;
7823 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7825 if (Feld[x][y] != element) /* element has changed */
7829 if (!MovDelay[x][y]) /* start new movement phase */
7831 /* all objects that can change their move direction after each step
7832 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7834 if (element != EL_YAMYAM &&
7835 element != EL_DARK_YAMYAM &&
7836 element != EL_PACMAN &&
7837 !(move_pattern & MV_ANY_DIRECTION) &&
7838 move_pattern != MV_TURNING_LEFT &&
7839 move_pattern != MV_TURNING_RIGHT &&
7840 move_pattern != MV_TURNING_LEFT_RIGHT &&
7841 move_pattern != MV_TURNING_RIGHT_LEFT &&
7842 move_pattern != MV_TURNING_RANDOM)
7846 if (MovDelay[x][y] && (element == EL_BUG ||
7847 element == EL_SPACESHIP ||
7848 element == EL_SP_SNIKSNAK ||
7849 element == EL_SP_ELECTRON ||
7850 element == EL_MOLE))
7851 TEST_DrawLevelField(x, y);
7855 if (MovDelay[x][y]) /* wait some time before next movement */
7859 if (element == EL_ROBOT ||
7860 element == EL_YAMYAM ||
7861 element == EL_DARK_YAMYAM)
7863 DrawLevelElementAnimationIfNeeded(x, y, element);
7864 PlayLevelSoundAction(x, y, ACTION_WAITING);
7866 else if (element == EL_SP_ELECTRON)
7867 DrawLevelElementAnimationIfNeeded(x, y, element);
7868 else if (element == EL_DRAGON)
7871 int dir = MovDir[x][y];
7872 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7873 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7874 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7875 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7876 dir == MV_UP ? IMG_FLAMES_1_UP :
7877 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7878 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7880 GfxAction[x][y] = ACTION_ATTACKING;
7882 if (IS_PLAYER(x, y))
7883 DrawPlayerField(x, y);
7885 TEST_DrawLevelField(x, y);
7887 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7889 for (i = 1; i <= 3; i++)
7891 int xx = x + i * dx;
7892 int yy = y + i * dy;
7893 int sx = SCREENX(xx);
7894 int sy = SCREENY(yy);
7895 int flame_graphic = graphic + (i - 1);
7897 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7902 int flamed = MovingOrBlocked2Element(xx, yy);
7904 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7907 RemoveMovingField(xx, yy);
7909 ChangeDelay[xx][yy] = 0;
7911 Feld[xx][yy] = EL_FLAMES;
7913 if (IN_SCR_FIELD(sx, sy))
7915 TEST_DrawLevelFieldCrumbled(xx, yy);
7916 DrawGraphic(sx, sy, flame_graphic, frame);
7921 if (Feld[xx][yy] == EL_FLAMES)
7922 Feld[xx][yy] = EL_EMPTY;
7923 TEST_DrawLevelField(xx, yy);
7928 if (MovDelay[x][y]) /* element still has to wait some time */
7930 PlayLevelSoundAction(x, y, ACTION_WAITING);
7936 /* now make next step */
7938 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7940 if (DONT_COLLIDE_WITH(element) &&
7941 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7942 !PLAYER_ENEMY_PROTECTED(newx, newy))
7944 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7949 else if (CAN_MOVE_INTO_ACID(element) &&
7950 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7951 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7952 (MovDir[x][y] == MV_DOWN ||
7953 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7955 SplashAcid(newx, newy);
7956 Store[x][y] = EL_ACID;
7958 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7960 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7961 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7962 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7963 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7966 TEST_DrawLevelField(x, y);
7968 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7969 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7970 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7972 local_player->friends_still_needed--;
7973 if (!local_player->friends_still_needed &&
7974 !local_player->GameOver && AllPlayersGone)
7975 PlayerWins(local_player);
7979 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7981 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7982 TEST_DrawLevelField(newx, newy);
7984 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7986 else if (!IS_FREE(newx, newy))
7988 GfxAction[x][y] = ACTION_WAITING;
7990 if (IS_PLAYER(x, y))
7991 DrawPlayerField(x, y);
7993 TEST_DrawLevelField(x, y);
7998 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8000 if (IS_FOOD_PIG(Feld[newx][newy]))
8002 if (IS_MOVING(newx, newy))
8003 RemoveMovingField(newx, newy);
8006 Feld[newx][newy] = EL_EMPTY;
8007 TEST_DrawLevelField(newx, newy);
8010 PlayLevelSound(x, y, SND_PIG_DIGGING);
8012 else if (!IS_FREE(newx, newy))
8014 if (IS_PLAYER(x, y))
8015 DrawPlayerField(x, y);
8017 TEST_DrawLevelField(x, y);
8022 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8024 if (Store[x][y] != EL_EMPTY)
8026 boolean can_clone = FALSE;
8029 /* check if element to clone is still there */
8030 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8032 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8040 /* cannot clone or target field not free anymore -- do not clone */
8041 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8042 Store[x][y] = EL_EMPTY;
8045 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8047 if (IS_MV_DIAGONAL(MovDir[x][y]))
8049 int diagonal_move_dir = MovDir[x][y];
8050 int stored = Store[x][y];
8051 int change_delay = 8;
8054 /* android is moving diagonally */
8056 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8058 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8059 GfxElement[x][y] = EL_EMC_ANDROID;
8060 GfxAction[x][y] = ACTION_SHRINKING;
8061 GfxDir[x][y] = diagonal_move_dir;
8062 ChangeDelay[x][y] = change_delay;
8064 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8067 DrawLevelGraphicAnimation(x, y, graphic);
8068 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8070 if (Feld[newx][newy] == EL_ACID)
8072 SplashAcid(newx, newy);
8077 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8079 Store[newx][newy] = EL_EMC_ANDROID;
8080 GfxElement[newx][newy] = EL_EMC_ANDROID;
8081 GfxAction[newx][newy] = ACTION_GROWING;
8082 GfxDir[newx][newy] = diagonal_move_dir;
8083 ChangeDelay[newx][newy] = change_delay;
8085 graphic = el_act_dir2img(GfxElement[newx][newy],
8086 GfxAction[newx][newy], GfxDir[newx][newy]);
8088 DrawLevelGraphicAnimation(newx, newy, graphic);
8089 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8095 Feld[newx][newy] = EL_EMPTY;
8096 TEST_DrawLevelField(newx, newy);
8098 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8101 else if (!IS_FREE(newx, newy))
8106 else if (IS_CUSTOM_ELEMENT(element) &&
8107 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8109 if (!DigFieldByCE(newx, newy, element))
8112 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8114 RunnerVisit[x][y] = FrameCounter;
8115 PlayerVisit[x][y] /= 8; /* expire player visit path */
8118 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8120 if (!IS_FREE(newx, newy))
8122 if (IS_PLAYER(x, y))
8123 DrawPlayerField(x, y);
8125 TEST_DrawLevelField(x, y);
8131 boolean wanna_flame = !RND(10);
8132 int dx = newx - x, dy = newy - y;
8133 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8134 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8135 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8136 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8137 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8138 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8141 IS_CLASSIC_ENEMY(element1) ||
8142 IS_CLASSIC_ENEMY(element2)) &&
8143 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8144 element1 != EL_FLAMES && element2 != EL_FLAMES)
8146 ResetGfxAnimation(x, y);
8147 GfxAction[x][y] = ACTION_ATTACKING;
8149 if (IS_PLAYER(x, y))
8150 DrawPlayerField(x, y);
8152 TEST_DrawLevelField(x, y);
8154 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8156 MovDelay[x][y] = 50;
8158 Feld[newx][newy] = EL_FLAMES;
8159 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8160 Feld[newx1][newy1] = EL_FLAMES;
8161 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8162 Feld[newx2][newy2] = EL_FLAMES;
8168 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8169 Feld[newx][newy] == EL_DIAMOND)
8171 if (IS_MOVING(newx, newy))
8172 RemoveMovingField(newx, newy);
8175 Feld[newx][newy] = EL_EMPTY;
8176 TEST_DrawLevelField(newx, newy);
8179 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8181 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8182 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8184 if (AmoebaNr[newx][newy])
8186 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8187 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8188 Feld[newx][newy] == EL_BD_AMOEBA)
8189 AmoebaCnt[AmoebaNr[newx][newy]]--;
8192 if (IS_MOVING(newx, newy))
8194 RemoveMovingField(newx, newy);
8198 Feld[newx][newy] = EL_EMPTY;
8199 TEST_DrawLevelField(newx, newy);
8202 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8204 else if ((element == EL_PACMAN || element == EL_MOLE)
8205 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8207 if (AmoebaNr[newx][newy])
8209 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8210 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8211 Feld[newx][newy] == EL_BD_AMOEBA)
8212 AmoebaCnt[AmoebaNr[newx][newy]]--;
8215 if (element == EL_MOLE)
8217 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8218 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8220 ResetGfxAnimation(x, y);
8221 GfxAction[x][y] = ACTION_DIGGING;
8222 TEST_DrawLevelField(x, y);
8224 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8226 return; /* wait for shrinking amoeba */
8228 else /* element == EL_PACMAN */
8230 Feld[newx][newy] = EL_EMPTY;
8231 TEST_DrawLevelField(newx, newy);
8232 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8235 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8236 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8237 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8239 /* wait for shrinking amoeba to completely disappear */
8242 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8244 /* object was running against a wall */
8248 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8249 DrawLevelElementAnimation(x, y, element);
8251 if (DONT_TOUCH(element))
8252 TestIfBadThingTouchesPlayer(x, y);
8257 InitMovingField(x, y, MovDir[x][y]);
8259 PlayLevelSoundAction(x, y, ACTION_MOVING);
8263 ContinueMoving(x, y);
8266 void ContinueMoving(int x, int y)
8268 int element = Feld[x][y];
8269 struct ElementInfo *ei = &element_info[element];
8270 int direction = MovDir[x][y];
8271 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8272 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8273 int newx = x + dx, newy = y + dy;
8274 int stored = Store[x][y];
8275 int stored_new = Store[newx][newy];
8276 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8277 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8278 boolean last_line = (newy == lev_fieldy - 1);
8280 MovPos[x][y] += getElementMoveStepsize(x, y);
8282 if (pushed_by_player) /* special case: moving object pushed by player */
8283 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8285 if (ABS(MovPos[x][y]) < TILEX)
8287 TEST_DrawLevelField(x, y);
8289 return; /* element is still moving */
8292 /* element reached destination field */
8294 Feld[x][y] = EL_EMPTY;
8295 Feld[newx][newy] = element;
8296 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8298 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8300 element = Feld[newx][newy] = EL_ACID;
8302 else if (element == EL_MOLE)
8304 Feld[x][y] = EL_SAND;
8306 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8308 else if (element == EL_QUICKSAND_FILLING)
8310 element = Feld[newx][newy] = get_next_element(element);
8311 Store[newx][newy] = Store[x][y];
8313 else if (element == EL_QUICKSAND_EMPTYING)
8315 Feld[x][y] = get_next_element(element);
8316 element = Feld[newx][newy] = Store[x][y];
8318 else if (element == EL_QUICKSAND_FAST_FILLING)
8320 element = Feld[newx][newy] = get_next_element(element);
8321 Store[newx][newy] = Store[x][y];
8323 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8325 Feld[x][y] = get_next_element(element);
8326 element = Feld[newx][newy] = Store[x][y];
8328 else if (element == EL_MAGIC_WALL_FILLING)
8330 element = Feld[newx][newy] = get_next_element(element);
8331 if (!game.magic_wall_active)
8332 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8333 Store[newx][newy] = Store[x][y];
8335 else if (element == EL_MAGIC_WALL_EMPTYING)
8337 Feld[x][y] = get_next_element(element);
8338 if (!game.magic_wall_active)
8339 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8340 element = Feld[newx][newy] = Store[x][y];
8342 InitField(newx, newy, FALSE);
8344 else if (element == EL_BD_MAGIC_WALL_FILLING)
8346 element = Feld[newx][newy] = get_next_element(element);
8347 if (!game.magic_wall_active)
8348 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8349 Store[newx][newy] = Store[x][y];
8351 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8353 Feld[x][y] = get_next_element(element);
8354 if (!game.magic_wall_active)
8355 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8356 element = Feld[newx][newy] = Store[x][y];
8358 InitField(newx, newy, FALSE);
8360 else if (element == EL_DC_MAGIC_WALL_FILLING)
8362 element = Feld[newx][newy] = get_next_element(element);
8363 if (!game.magic_wall_active)
8364 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8365 Store[newx][newy] = Store[x][y];
8367 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8369 Feld[x][y] = get_next_element(element);
8370 if (!game.magic_wall_active)
8371 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8372 element = Feld[newx][newy] = Store[x][y];
8374 InitField(newx, newy, FALSE);
8376 else if (element == EL_AMOEBA_DROPPING)
8378 Feld[x][y] = get_next_element(element);
8379 element = Feld[newx][newy] = Store[x][y];
8381 else if (element == EL_SOKOBAN_OBJECT)
8384 Feld[x][y] = Back[x][y];
8386 if (Back[newx][newy])
8387 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8389 Back[x][y] = Back[newx][newy] = 0;
8392 Store[x][y] = EL_EMPTY;
8397 MovDelay[newx][newy] = 0;
8399 if (CAN_CHANGE_OR_HAS_ACTION(element))
8401 /* copy element change control values to new field */
8402 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8403 ChangePage[newx][newy] = ChangePage[x][y];
8404 ChangeCount[newx][newy] = ChangeCount[x][y];
8405 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8408 CustomValue[newx][newy] = CustomValue[x][y];
8410 ChangeDelay[x][y] = 0;
8411 ChangePage[x][y] = -1;
8412 ChangeCount[x][y] = 0;
8413 ChangeEvent[x][y] = -1;
8415 CustomValue[x][y] = 0;
8417 /* copy animation control values to new field */
8418 GfxFrame[newx][newy] = GfxFrame[x][y];
8419 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8420 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8421 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8423 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8425 /* some elements can leave other elements behind after moving */
8426 if (ei->move_leave_element != EL_EMPTY &&
8427 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8428 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8430 int move_leave_element = ei->move_leave_element;
8432 /* this makes it possible to leave the removed element again */
8433 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8434 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8436 Feld[x][y] = move_leave_element;
8438 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8439 MovDir[x][y] = direction;
8441 InitField(x, y, FALSE);
8443 if (GFX_CRUMBLED(Feld[x][y]))
8444 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8446 if (ELEM_IS_PLAYER(move_leave_element))
8447 RelocatePlayer(x, y, move_leave_element);
8450 /* do this after checking for left-behind element */
8451 ResetGfxAnimation(x, y); /* reset animation values for old field */
8453 if (!CAN_MOVE(element) ||
8454 (CAN_FALL(element) && direction == MV_DOWN &&
8455 (element == EL_SPRING ||
8456 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8457 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8458 GfxDir[x][y] = MovDir[newx][newy] = 0;
8460 TEST_DrawLevelField(x, y);
8461 TEST_DrawLevelField(newx, newy);
8463 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8465 /* prevent pushed element from moving on in pushed direction */
8466 if (pushed_by_player && CAN_MOVE(element) &&
8467 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8468 !(element_info[element].move_pattern & direction))
8469 TurnRound(newx, newy);
8471 /* prevent elements on conveyor belt from moving on in last direction */
8472 if (pushed_by_conveyor && CAN_FALL(element) &&
8473 direction & MV_HORIZONTAL)
8474 MovDir[newx][newy] = 0;
8476 if (!pushed_by_player)
8478 int nextx = newx + dx, nexty = newy + dy;
8479 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8481 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8483 if (CAN_FALL(element) && direction == MV_DOWN)
8484 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8486 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8487 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8489 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8490 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8493 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8495 TestIfBadThingTouchesPlayer(newx, newy);
8496 TestIfBadThingTouchesFriend(newx, newy);
8498 if (!IS_CUSTOM_ELEMENT(element))
8499 TestIfBadThingTouchesOtherBadThing(newx, newy);
8501 else if (element == EL_PENGUIN)
8502 TestIfFriendTouchesBadThing(newx, newy);
8504 if (DONT_GET_HIT_BY(element))
8506 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8509 /* give the player one last chance (one more frame) to move away */
8510 if (CAN_FALL(element) && direction == MV_DOWN &&
8511 (last_line || (!IS_FREE(x, newy + 1) &&
8512 (!IS_PLAYER(x, newy + 1) ||
8513 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8516 if (pushed_by_player && !game.use_change_when_pushing_bug)
8518 int push_side = MV_DIR_OPPOSITE(direction);
8519 struct PlayerInfo *player = PLAYERINFO(x, y);
8521 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8522 player->index_bit, push_side);
8523 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8524 player->index_bit, push_side);
8527 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8528 MovDelay[newx][newy] = 1;
8530 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8532 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8533 TestIfElementHitsCustomElement(newx, newy, direction);
8534 TestIfPlayerTouchesCustomElement(newx, newy);
8535 TestIfElementTouchesCustomElement(newx, newy);
8537 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8538 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8539 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8540 MV_DIR_OPPOSITE(direction));
8543 int AmoebeNachbarNr(int ax, int ay)
8546 int element = Feld[ax][ay];
8548 static int xy[4][2] =
8556 for (i = 0; i < NUM_DIRECTIONS; i++)
8558 int x = ax + xy[i][0];
8559 int y = ay + xy[i][1];
8561 if (!IN_LEV_FIELD(x, y))
8564 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8565 group_nr = AmoebaNr[x][y];
8571 void AmoebenVereinigen(int ax, int ay)
8573 int i, x, y, xx, yy;
8574 int new_group_nr = AmoebaNr[ax][ay];
8575 static int xy[4][2] =
8583 if (new_group_nr == 0)
8586 for (i = 0; i < NUM_DIRECTIONS; i++)
8591 if (!IN_LEV_FIELD(x, y))
8594 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8595 Feld[x][y] == EL_BD_AMOEBA ||
8596 Feld[x][y] == EL_AMOEBA_DEAD) &&
8597 AmoebaNr[x][y] != new_group_nr)
8599 int old_group_nr = AmoebaNr[x][y];
8601 if (old_group_nr == 0)
8604 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8605 AmoebaCnt[old_group_nr] = 0;
8606 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8607 AmoebaCnt2[old_group_nr] = 0;
8609 SCAN_PLAYFIELD(xx, yy)
8611 if (AmoebaNr[xx][yy] == old_group_nr)
8612 AmoebaNr[xx][yy] = new_group_nr;
8618 void AmoebeUmwandeln(int ax, int ay)
8622 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8624 int group_nr = AmoebaNr[ax][ay];
8629 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8630 printf("AmoebeUmwandeln(): This should never happen!\n");
8635 SCAN_PLAYFIELD(x, y)
8637 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8640 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8644 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8645 SND_AMOEBA_TURNING_TO_GEM :
8646 SND_AMOEBA_TURNING_TO_ROCK));
8651 static int xy[4][2] =
8659 for (i = 0; i < NUM_DIRECTIONS; i++)
8664 if (!IN_LEV_FIELD(x, y))
8667 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8669 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8670 SND_AMOEBA_TURNING_TO_GEM :
8671 SND_AMOEBA_TURNING_TO_ROCK));
8678 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8681 int group_nr = AmoebaNr[ax][ay];
8682 boolean done = FALSE;
8687 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8688 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8693 SCAN_PLAYFIELD(x, y)
8695 if (AmoebaNr[x][y] == group_nr &&
8696 (Feld[x][y] == EL_AMOEBA_DEAD ||
8697 Feld[x][y] == EL_BD_AMOEBA ||
8698 Feld[x][y] == EL_AMOEBA_GROWING))
8701 Feld[x][y] = new_element;
8702 InitField(x, y, FALSE);
8703 TEST_DrawLevelField(x, y);
8709 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8710 SND_BD_AMOEBA_TURNING_TO_ROCK :
8711 SND_BD_AMOEBA_TURNING_TO_GEM));
8714 void AmoebeWaechst(int x, int y)
8716 static unsigned int sound_delay = 0;
8717 static unsigned int sound_delay_value = 0;
8719 if (!MovDelay[x][y]) /* start new growing cycle */
8723 if (DelayReached(&sound_delay, sound_delay_value))
8725 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8726 sound_delay_value = 30;
8730 if (MovDelay[x][y]) /* wait some time before growing bigger */
8733 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8735 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8736 6 - MovDelay[x][y]);
8738 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8741 if (!MovDelay[x][y])
8743 Feld[x][y] = Store[x][y];
8745 TEST_DrawLevelField(x, y);
8750 void AmoebaDisappearing(int x, int y)
8752 static unsigned int sound_delay = 0;
8753 static unsigned int sound_delay_value = 0;
8755 if (!MovDelay[x][y]) /* start new shrinking cycle */
8759 if (DelayReached(&sound_delay, sound_delay_value))
8760 sound_delay_value = 30;
8763 if (MovDelay[x][y]) /* wait some time before shrinking */
8766 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8768 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8769 6 - MovDelay[x][y]);
8771 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8774 if (!MovDelay[x][y])
8776 Feld[x][y] = EL_EMPTY;
8777 TEST_DrawLevelField(x, y);
8779 /* don't let mole enter this field in this cycle;
8780 (give priority to objects falling to this field from above) */
8786 void AmoebeAbleger(int ax, int ay)
8789 int element = Feld[ax][ay];
8790 int graphic = el2img(element);
8791 int newax = ax, neway = ay;
8792 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8793 static int xy[4][2] =
8801 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8803 Feld[ax][ay] = EL_AMOEBA_DEAD;
8804 TEST_DrawLevelField(ax, ay);
8808 if (IS_ANIMATED(graphic))
8809 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8811 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8812 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8814 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8817 if (MovDelay[ax][ay])
8821 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8824 int x = ax + xy[start][0];
8825 int y = ay + xy[start][1];
8827 if (!IN_LEV_FIELD(x, y))
8830 if (IS_FREE(x, y) ||
8831 CAN_GROW_INTO(Feld[x][y]) ||
8832 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8833 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8839 if (newax == ax && neway == ay)
8842 else /* normal or "filled" (BD style) amoeba */
8845 boolean waiting_for_player = FALSE;
8847 for (i = 0; i < NUM_DIRECTIONS; i++)
8849 int j = (start + i) % 4;
8850 int x = ax + xy[j][0];
8851 int y = ay + xy[j][1];
8853 if (!IN_LEV_FIELD(x, y))
8856 if (IS_FREE(x, y) ||
8857 CAN_GROW_INTO(Feld[x][y]) ||
8858 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8859 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8865 else if (IS_PLAYER(x, y))
8866 waiting_for_player = TRUE;
8869 if (newax == ax && neway == ay) /* amoeba cannot grow */
8871 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8873 Feld[ax][ay] = EL_AMOEBA_DEAD;
8874 TEST_DrawLevelField(ax, ay);
8875 AmoebaCnt[AmoebaNr[ax][ay]]--;
8877 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8879 if (element == EL_AMOEBA_FULL)
8880 AmoebeUmwandeln(ax, ay);
8881 else if (element == EL_BD_AMOEBA)
8882 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8887 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8889 /* amoeba gets larger by growing in some direction */
8891 int new_group_nr = AmoebaNr[ax][ay];
8894 if (new_group_nr == 0)
8896 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8897 printf("AmoebeAbleger(): This should never happen!\n");
8902 AmoebaNr[newax][neway] = new_group_nr;
8903 AmoebaCnt[new_group_nr]++;
8904 AmoebaCnt2[new_group_nr]++;
8906 /* if amoeba touches other amoeba(s) after growing, unify them */
8907 AmoebenVereinigen(newax, neway);
8909 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8911 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8917 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8918 (neway == lev_fieldy - 1 && newax != ax))
8920 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8921 Store[newax][neway] = element;
8923 else if (neway == ay || element == EL_EMC_DRIPPER)
8925 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8927 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8931 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8932 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8933 Store[ax][ay] = EL_AMOEBA_DROP;
8934 ContinueMoving(ax, ay);
8938 TEST_DrawLevelField(newax, neway);
8941 void Life(int ax, int ay)
8945 int element = Feld[ax][ay];
8946 int graphic = el2img(element);
8947 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8949 boolean changed = FALSE;
8951 if (IS_ANIMATED(graphic))
8952 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8957 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8958 MovDelay[ax][ay] = life_time;
8960 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8963 if (MovDelay[ax][ay])
8967 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8969 int xx = ax+x1, yy = ay+y1;
8972 if (!IN_LEV_FIELD(xx, yy))
8975 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8977 int x = xx+x2, y = yy+y2;
8979 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8982 if (((Feld[x][y] == element ||
8983 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8985 (IS_FREE(x, y) && Stop[x][y]))
8989 if (xx == ax && yy == ay) /* field in the middle */
8991 if (nachbarn < life_parameter[0] ||
8992 nachbarn > life_parameter[1])
8994 Feld[xx][yy] = EL_EMPTY;
8996 TEST_DrawLevelField(xx, yy);
8997 Stop[xx][yy] = TRUE;
9001 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9002 { /* free border field */
9003 if (nachbarn >= life_parameter[2] &&
9004 nachbarn <= life_parameter[3])
9006 Feld[xx][yy] = element;
9007 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9009 TEST_DrawLevelField(xx, yy);
9010 Stop[xx][yy] = TRUE;
9017 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9018 SND_GAME_OF_LIFE_GROWING);
9021 static void InitRobotWheel(int x, int y)
9023 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9026 static void RunRobotWheel(int x, int y)
9028 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9031 static void StopRobotWheel(int x, int y)
9033 if (ZX == x && ZY == y)
9037 game.robot_wheel_active = FALSE;
9041 static void InitTimegateWheel(int x, int y)
9043 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9046 static void RunTimegateWheel(int x, int y)
9048 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9051 static void InitMagicBallDelay(int x, int y)
9053 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9056 static void ActivateMagicBall(int bx, int by)
9060 if (level.ball_random)
9062 int pos_border = RND(8); /* select one of the eight border elements */
9063 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9064 int xx = pos_content % 3;
9065 int yy = pos_content / 3;
9070 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9071 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9075 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9077 int xx = x - bx + 1;
9078 int yy = y - by + 1;
9080 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9081 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9085 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9088 void CheckExit(int x, int y)
9090 if (local_player->gems_still_needed > 0 ||
9091 local_player->sokobanfields_still_needed > 0 ||
9092 local_player->lights_still_needed > 0)
9094 int element = Feld[x][y];
9095 int graphic = el2img(element);
9097 if (IS_ANIMATED(graphic))
9098 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9103 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9106 Feld[x][y] = EL_EXIT_OPENING;
9108 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9111 void CheckExitEM(int x, int y)
9113 if (local_player->gems_still_needed > 0 ||
9114 local_player->sokobanfields_still_needed > 0 ||
9115 local_player->lights_still_needed > 0)
9117 int element = Feld[x][y];
9118 int graphic = el2img(element);
9120 if (IS_ANIMATED(graphic))
9121 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9126 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9129 Feld[x][y] = EL_EM_EXIT_OPENING;
9131 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9134 void CheckExitSteel(int x, int y)
9136 if (local_player->gems_still_needed > 0 ||
9137 local_player->sokobanfields_still_needed > 0 ||
9138 local_player->lights_still_needed > 0)
9140 int element = Feld[x][y];
9141 int graphic = el2img(element);
9143 if (IS_ANIMATED(graphic))
9144 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9149 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9152 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9154 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9157 void CheckExitSteelEM(int x, int y)
9159 if (local_player->gems_still_needed > 0 ||
9160 local_player->sokobanfields_still_needed > 0 ||
9161 local_player->lights_still_needed > 0)
9163 int element = Feld[x][y];
9164 int graphic = el2img(element);
9166 if (IS_ANIMATED(graphic))
9167 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9172 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9175 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9177 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9180 void CheckExitSP(int x, int y)
9182 if (local_player->gems_still_needed > 0)
9184 int element = Feld[x][y];
9185 int graphic = el2img(element);
9187 if (IS_ANIMATED(graphic))
9188 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9193 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9196 Feld[x][y] = EL_SP_EXIT_OPENING;
9198 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9201 static void CloseAllOpenTimegates()
9205 SCAN_PLAYFIELD(x, y)
9207 int element = Feld[x][y];
9209 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9211 Feld[x][y] = EL_TIMEGATE_CLOSING;
9213 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9218 void DrawTwinkleOnField(int x, int y)
9220 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9223 if (Feld[x][y] == EL_BD_DIAMOND)
9226 if (MovDelay[x][y] == 0) /* next animation frame */
9227 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9229 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9233 DrawLevelElementAnimation(x, y, Feld[x][y]);
9235 if (MovDelay[x][y] != 0)
9237 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9238 10 - MovDelay[x][y]);
9240 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9245 void MauerWaechst(int x, int y)
9249 if (!MovDelay[x][y]) /* next animation frame */
9250 MovDelay[x][y] = 3 * delay;
9252 if (MovDelay[x][y]) /* wait some time before next frame */
9256 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9258 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9259 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9261 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9264 if (!MovDelay[x][y])
9266 if (MovDir[x][y] == MV_LEFT)
9268 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9269 TEST_DrawLevelField(x - 1, y);
9271 else if (MovDir[x][y] == MV_RIGHT)
9273 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9274 TEST_DrawLevelField(x + 1, y);
9276 else if (MovDir[x][y] == MV_UP)
9278 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9279 TEST_DrawLevelField(x, y - 1);
9283 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9284 TEST_DrawLevelField(x, y + 1);
9287 Feld[x][y] = Store[x][y];
9289 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9290 TEST_DrawLevelField(x, y);
9295 void MauerAbleger(int ax, int ay)
9297 int element = Feld[ax][ay];
9298 int graphic = el2img(element);
9299 boolean oben_frei = FALSE, unten_frei = FALSE;
9300 boolean links_frei = FALSE, rechts_frei = FALSE;
9301 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9302 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9303 boolean new_wall = FALSE;
9305 if (IS_ANIMATED(graphic))
9306 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9308 if (!MovDelay[ax][ay]) /* start building new wall */
9309 MovDelay[ax][ay] = 6;
9311 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9314 if (MovDelay[ax][ay])
9318 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9320 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9322 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9324 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9327 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9328 element == EL_EXPANDABLE_WALL_ANY)
9332 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9333 Store[ax][ay-1] = element;
9334 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9335 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9336 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9337 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9342 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9343 Store[ax][ay+1] = element;
9344 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9345 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9346 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9347 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9352 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9353 element == EL_EXPANDABLE_WALL_ANY ||
9354 element == EL_EXPANDABLE_WALL ||
9355 element == EL_BD_EXPANDABLE_WALL)
9359 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9360 Store[ax-1][ay] = element;
9361 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9362 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9363 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9364 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9370 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9371 Store[ax+1][ay] = element;
9372 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9373 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9374 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9375 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9380 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9381 TEST_DrawLevelField(ax, ay);
9383 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9385 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9386 unten_massiv = TRUE;
9387 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9388 links_massiv = TRUE;
9389 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9390 rechts_massiv = TRUE;
9392 if (((oben_massiv && unten_massiv) ||
9393 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9394 element == EL_EXPANDABLE_WALL) &&
9395 ((links_massiv && rechts_massiv) ||
9396 element == EL_EXPANDABLE_WALL_VERTICAL))
9397 Feld[ax][ay] = EL_WALL;
9400 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9403 void MauerAblegerStahl(int ax, int ay)
9405 int element = Feld[ax][ay];
9406 int graphic = el2img(element);
9407 boolean oben_frei = FALSE, unten_frei = FALSE;
9408 boolean links_frei = FALSE, rechts_frei = FALSE;
9409 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9410 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9411 boolean new_wall = FALSE;
9413 if (IS_ANIMATED(graphic))
9414 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9416 if (!MovDelay[ax][ay]) /* start building new wall */
9417 MovDelay[ax][ay] = 6;
9419 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9422 if (MovDelay[ax][ay])
9426 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9428 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9430 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9432 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9435 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9436 element == EL_EXPANDABLE_STEELWALL_ANY)
9440 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9441 Store[ax][ay-1] = element;
9442 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9443 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9444 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9445 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9450 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9451 Store[ax][ay+1] = element;
9452 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9453 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9454 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9455 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9460 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9461 element == EL_EXPANDABLE_STEELWALL_ANY)
9465 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9466 Store[ax-1][ay] = element;
9467 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9468 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9469 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9470 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9476 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9477 Store[ax+1][ay] = element;
9478 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9479 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9480 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9481 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9486 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9488 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9489 unten_massiv = TRUE;
9490 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9491 links_massiv = TRUE;
9492 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9493 rechts_massiv = TRUE;
9495 if (((oben_massiv && unten_massiv) ||
9496 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9497 ((links_massiv && rechts_massiv) ||
9498 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9499 Feld[ax][ay] = EL_STEELWALL;
9502 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9505 void CheckForDragon(int x, int y)
9508 boolean dragon_found = FALSE;
9509 static int xy[4][2] =
9517 for (i = 0; i < NUM_DIRECTIONS; i++)
9519 for (j = 0; j < 4; j++)
9521 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9523 if (IN_LEV_FIELD(xx, yy) &&
9524 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9526 if (Feld[xx][yy] == EL_DRAGON)
9527 dragon_found = TRUE;
9536 for (i = 0; i < NUM_DIRECTIONS; i++)
9538 for (j = 0; j < 3; j++)
9540 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9542 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9544 Feld[xx][yy] = EL_EMPTY;
9545 TEST_DrawLevelField(xx, yy);
9554 static void InitBuggyBase(int x, int y)
9556 int element = Feld[x][y];
9557 int activating_delay = FRAMES_PER_SECOND / 4;
9560 (element == EL_SP_BUGGY_BASE ?
9561 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9562 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9564 element == EL_SP_BUGGY_BASE_ACTIVE ?
9565 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9568 static void WarnBuggyBase(int x, int y)
9571 static int xy[4][2] =
9579 for (i = 0; i < NUM_DIRECTIONS; i++)
9581 int xx = x + xy[i][0];
9582 int yy = y + xy[i][1];
9584 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9586 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9593 static void InitTrap(int x, int y)
9595 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9598 static void ActivateTrap(int x, int y)
9600 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9603 static void ChangeActiveTrap(int x, int y)
9605 int graphic = IMG_TRAP_ACTIVE;
9607 /* if new animation frame was drawn, correct crumbled sand border */
9608 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9609 TEST_DrawLevelFieldCrumbled(x, y);
9612 static int getSpecialActionElement(int element, int number, int base_element)
9614 return (element != EL_EMPTY ? element :
9615 number != -1 ? base_element + number - 1 :
9619 static int getModifiedActionNumber(int value_old, int operator, int operand,
9620 int value_min, int value_max)
9622 int value_new = (operator == CA_MODE_SET ? operand :
9623 operator == CA_MODE_ADD ? value_old + operand :
9624 operator == CA_MODE_SUBTRACT ? value_old - operand :
9625 operator == CA_MODE_MULTIPLY ? value_old * operand :
9626 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9627 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9630 return (value_new < value_min ? value_min :
9631 value_new > value_max ? value_max :
9635 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9637 struct ElementInfo *ei = &element_info[element];
9638 struct ElementChangeInfo *change = &ei->change_page[page];
9639 int target_element = change->target_element;
9640 int action_type = change->action_type;
9641 int action_mode = change->action_mode;
9642 int action_arg = change->action_arg;
9643 int action_element = change->action_element;
9646 if (!change->has_action)
9649 /* ---------- determine action paramater values -------------------------- */
9651 int level_time_value =
9652 (level.time > 0 ? TimeLeft :
9655 int action_arg_element_raw =
9656 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9657 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9658 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9659 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9660 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9661 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9662 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9664 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9666 int action_arg_direction =
9667 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9668 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9669 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9670 change->actual_trigger_side :
9671 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9672 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9675 int action_arg_number_min =
9676 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9679 int action_arg_number_max =
9680 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9681 action_type == CA_SET_LEVEL_GEMS ? 999 :
9682 action_type == CA_SET_LEVEL_TIME ? 9999 :
9683 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9684 action_type == CA_SET_CE_VALUE ? 9999 :
9685 action_type == CA_SET_CE_SCORE ? 9999 :
9688 int action_arg_number_reset =
9689 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9690 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9691 action_type == CA_SET_LEVEL_TIME ? level.time :
9692 action_type == CA_SET_LEVEL_SCORE ? 0 :
9693 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9694 action_type == CA_SET_CE_SCORE ? 0 :
9697 int action_arg_number =
9698 (action_arg <= CA_ARG_MAX ? action_arg :
9699 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9700 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9701 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9702 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9703 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9704 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9705 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9706 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9707 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9708 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9709 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9710 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9711 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9712 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9713 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9714 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9715 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9716 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9717 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9718 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9719 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9722 int action_arg_number_old =
9723 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9724 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9725 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9726 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9727 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9730 int action_arg_number_new =
9731 getModifiedActionNumber(action_arg_number_old,
9732 action_mode, action_arg_number,
9733 action_arg_number_min, action_arg_number_max);
9735 int trigger_player_bits =
9736 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9737 change->actual_trigger_player_bits : change->trigger_player);
9739 int action_arg_player_bits =
9740 (action_arg >= CA_ARG_PLAYER_1 &&
9741 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9742 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9743 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9746 /* ---------- execute action -------------------------------------------- */
9748 switch (action_type)
9755 /* ---------- level actions ------------------------------------------- */
9757 case CA_RESTART_LEVEL:
9759 game.restart_level = TRUE;
9764 case CA_SHOW_ENVELOPE:
9766 int element = getSpecialActionElement(action_arg_element,
9767 action_arg_number, EL_ENVELOPE_1);
9769 if (IS_ENVELOPE(element))
9770 local_player->show_envelope = element;
9775 case CA_SET_LEVEL_TIME:
9777 if (level.time > 0) /* only modify limited time value */
9779 TimeLeft = action_arg_number_new;
9781 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9783 DisplayGameControlValues();
9785 if (!TimeLeft && setup.time_limit)
9786 for (i = 0; i < MAX_PLAYERS; i++)
9787 KillPlayer(&stored_player[i]);
9793 case CA_SET_LEVEL_SCORE:
9795 local_player->score = action_arg_number_new;
9797 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9799 DisplayGameControlValues();
9804 case CA_SET_LEVEL_GEMS:
9806 local_player->gems_still_needed = action_arg_number_new;
9808 game.snapshot.collected_item = TRUE;
9810 game_panel_controls[GAME_PANEL_GEMS].value =
9811 local_player->gems_still_needed;
9813 DisplayGameControlValues();
9818 case CA_SET_LEVEL_WIND:
9820 game.wind_direction = action_arg_direction;
9825 case CA_SET_LEVEL_RANDOM_SEED:
9827 /* ensure that setting a new random seed while playing is predictable */
9828 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9833 /* ---------- player actions ------------------------------------------ */
9835 case CA_MOVE_PLAYER:
9837 /* automatically move to the next field in specified direction */
9838 for (i = 0; i < MAX_PLAYERS; i++)
9839 if (trigger_player_bits & (1 << i))
9840 stored_player[i].programmed_action = action_arg_direction;
9845 case CA_EXIT_PLAYER:
9847 for (i = 0; i < MAX_PLAYERS; i++)
9848 if (action_arg_player_bits & (1 << i))
9849 PlayerWins(&stored_player[i]);
9854 case CA_KILL_PLAYER:
9856 for (i = 0; i < MAX_PLAYERS; i++)
9857 if (action_arg_player_bits & (1 << i))
9858 KillPlayer(&stored_player[i]);
9863 case CA_SET_PLAYER_KEYS:
9865 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9866 int element = getSpecialActionElement(action_arg_element,
9867 action_arg_number, EL_KEY_1);
9869 if (IS_KEY(element))
9871 for (i = 0; i < MAX_PLAYERS; i++)
9873 if (trigger_player_bits & (1 << i))
9875 stored_player[i].key[KEY_NR(element)] = key_state;
9877 DrawGameDoorValues();
9885 case CA_SET_PLAYER_SPEED:
9887 for (i = 0; i < MAX_PLAYERS; i++)
9889 if (trigger_player_bits & (1 << i))
9891 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9893 if (action_arg == CA_ARG_SPEED_FASTER &&
9894 stored_player[i].cannot_move)
9896 action_arg_number = STEPSIZE_VERY_SLOW;
9898 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9899 action_arg == CA_ARG_SPEED_FASTER)
9901 action_arg_number = 2;
9902 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9905 else if (action_arg == CA_ARG_NUMBER_RESET)
9907 action_arg_number = level.initial_player_stepsize[i];
9911 getModifiedActionNumber(move_stepsize,
9914 action_arg_number_min,
9915 action_arg_number_max);
9917 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9924 case CA_SET_PLAYER_SHIELD:
9926 for (i = 0; i < MAX_PLAYERS; i++)
9928 if (trigger_player_bits & (1 << i))
9930 if (action_arg == CA_ARG_SHIELD_OFF)
9932 stored_player[i].shield_normal_time_left = 0;
9933 stored_player[i].shield_deadly_time_left = 0;
9935 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9937 stored_player[i].shield_normal_time_left = 999999;
9939 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9941 stored_player[i].shield_normal_time_left = 999999;
9942 stored_player[i].shield_deadly_time_left = 999999;
9950 case CA_SET_PLAYER_GRAVITY:
9952 for (i = 0; i < MAX_PLAYERS; i++)
9954 if (trigger_player_bits & (1 << i))
9956 stored_player[i].gravity =
9957 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9958 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9959 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9960 stored_player[i].gravity);
9967 case CA_SET_PLAYER_ARTWORK:
9969 for (i = 0; i < MAX_PLAYERS; i++)
9971 if (trigger_player_bits & (1 << i))
9973 int artwork_element = action_arg_element;
9975 if (action_arg == CA_ARG_ELEMENT_RESET)
9977 (level.use_artwork_element[i] ? level.artwork_element[i] :
9978 stored_player[i].element_nr);
9980 if (stored_player[i].artwork_element != artwork_element)
9981 stored_player[i].Frame = 0;
9983 stored_player[i].artwork_element = artwork_element;
9985 SetPlayerWaiting(&stored_player[i], FALSE);
9987 /* set number of special actions for bored and sleeping animation */
9988 stored_player[i].num_special_action_bored =
9989 get_num_special_action(artwork_element,
9990 ACTION_BORING_1, ACTION_BORING_LAST);
9991 stored_player[i].num_special_action_sleeping =
9992 get_num_special_action(artwork_element,
9993 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10000 case CA_SET_PLAYER_INVENTORY:
10002 for (i = 0; i < MAX_PLAYERS; i++)
10004 struct PlayerInfo *player = &stored_player[i];
10007 if (trigger_player_bits & (1 << i))
10009 int inventory_element = action_arg_element;
10011 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10012 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10013 action_arg == CA_ARG_ELEMENT_ACTION)
10015 int element = inventory_element;
10016 int collect_count = element_info[element].collect_count_initial;
10018 if (!IS_CUSTOM_ELEMENT(element))
10021 if (collect_count == 0)
10022 player->inventory_infinite_element = element;
10024 for (k = 0; k < collect_count; k++)
10025 if (player->inventory_size < MAX_INVENTORY_SIZE)
10026 player->inventory_element[player->inventory_size++] =
10029 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10030 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10031 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10033 if (player->inventory_infinite_element != EL_UNDEFINED &&
10034 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10035 action_arg_element_raw))
10036 player->inventory_infinite_element = EL_UNDEFINED;
10038 for (k = 0, j = 0; j < player->inventory_size; j++)
10040 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10041 action_arg_element_raw))
10042 player->inventory_element[k++] = player->inventory_element[j];
10045 player->inventory_size = k;
10047 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10049 if (player->inventory_size > 0)
10051 for (j = 0; j < player->inventory_size - 1; j++)
10052 player->inventory_element[j] = player->inventory_element[j + 1];
10054 player->inventory_size--;
10057 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10059 if (player->inventory_size > 0)
10060 player->inventory_size--;
10062 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10064 player->inventory_infinite_element = EL_UNDEFINED;
10065 player->inventory_size = 0;
10067 else if (action_arg == CA_ARG_INVENTORY_RESET)
10069 player->inventory_infinite_element = EL_UNDEFINED;
10070 player->inventory_size = 0;
10072 if (level.use_initial_inventory[i])
10074 for (j = 0; j < level.initial_inventory_size[i]; j++)
10076 int element = level.initial_inventory_content[i][j];
10077 int collect_count = element_info[element].collect_count_initial;
10079 if (!IS_CUSTOM_ELEMENT(element))
10082 if (collect_count == 0)
10083 player->inventory_infinite_element = element;
10085 for (k = 0; k < collect_count; k++)
10086 if (player->inventory_size < MAX_INVENTORY_SIZE)
10087 player->inventory_element[player->inventory_size++] =
10098 /* ---------- CE actions ---------------------------------------------- */
10100 case CA_SET_CE_VALUE:
10102 int last_ce_value = CustomValue[x][y];
10104 CustomValue[x][y] = action_arg_number_new;
10106 if (CustomValue[x][y] != last_ce_value)
10108 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10109 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10111 if (CustomValue[x][y] == 0)
10113 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10114 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10121 case CA_SET_CE_SCORE:
10123 int last_ce_score = ei->collect_score;
10125 ei->collect_score = action_arg_number_new;
10127 if (ei->collect_score != last_ce_score)
10129 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10130 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10132 if (ei->collect_score == 0)
10136 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10137 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10140 This is a very special case that seems to be a mixture between
10141 CheckElementChange() and CheckTriggeredElementChange(): while
10142 the first one only affects single elements that are triggered
10143 directly, the second one affects multiple elements in the playfield
10144 that are triggered indirectly by another element. This is a third
10145 case: Changing the CE score always affects multiple identical CEs,
10146 so every affected CE must be checked, not only the single CE for
10147 which the CE score was changed in the first place (as every instance
10148 of that CE shares the same CE score, and therefore also can change)!
10150 SCAN_PLAYFIELD(xx, yy)
10152 if (Feld[xx][yy] == element)
10153 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10154 CE_SCORE_GETS_ZERO);
10162 case CA_SET_CE_ARTWORK:
10164 int artwork_element = action_arg_element;
10165 boolean reset_frame = FALSE;
10168 if (action_arg == CA_ARG_ELEMENT_RESET)
10169 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10172 if (ei->gfx_element != artwork_element)
10173 reset_frame = TRUE;
10175 ei->gfx_element = artwork_element;
10177 SCAN_PLAYFIELD(xx, yy)
10179 if (Feld[xx][yy] == element)
10183 ResetGfxAnimation(xx, yy);
10184 ResetRandomAnimationValue(xx, yy);
10187 TEST_DrawLevelField(xx, yy);
10194 /* ---------- engine actions ------------------------------------------ */
10196 case CA_SET_ENGINE_SCAN_MODE:
10198 InitPlayfieldScanMode(action_arg);
10208 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10210 int old_element = Feld[x][y];
10211 int new_element = GetElementFromGroupElement(element);
10212 int previous_move_direction = MovDir[x][y];
10213 int last_ce_value = CustomValue[x][y];
10214 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10215 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10216 boolean add_player_onto_element = (new_element_is_player &&
10217 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10218 IS_WALKABLE(old_element));
10220 if (!add_player_onto_element)
10222 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10223 RemoveMovingField(x, y);
10227 Feld[x][y] = new_element;
10229 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10230 MovDir[x][y] = previous_move_direction;
10232 if (element_info[new_element].use_last_ce_value)
10233 CustomValue[x][y] = last_ce_value;
10235 InitField_WithBug1(x, y, FALSE);
10237 new_element = Feld[x][y]; /* element may have changed */
10239 ResetGfxAnimation(x, y);
10240 ResetRandomAnimationValue(x, y);
10242 TEST_DrawLevelField(x, y);
10244 if (GFX_CRUMBLED(new_element))
10245 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10248 /* check if element under the player changes from accessible to unaccessible
10249 (needed for special case of dropping element which then changes) */
10250 /* (must be checked after creating new element for walkable group elements) */
10251 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10252 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10259 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10260 if (new_element_is_player)
10261 RelocatePlayer(x, y, new_element);
10264 ChangeCount[x][y]++; /* count number of changes in the same frame */
10266 TestIfBadThingTouchesPlayer(x, y);
10267 TestIfPlayerTouchesCustomElement(x, y);
10268 TestIfElementTouchesCustomElement(x, y);
10271 static void CreateField(int x, int y, int element)
10273 CreateFieldExt(x, y, element, FALSE);
10276 static void CreateElementFromChange(int x, int y, int element)
10278 element = GET_VALID_RUNTIME_ELEMENT(element);
10280 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10282 int old_element = Feld[x][y];
10284 /* prevent changed element from moving in same engine frame
10285 unless both old and new element can either fall or move */
10286 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10287 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10291 CreateFieldExt(x, y, element, TRUE);
10294 static boolean ChangeElement(int x, int y, int element, int page)
10296 struct ElementInfo *ei = &element_info[element];
10297 struct ElementChangeInfo *change = &ei->change_page[page];
10298 int ce_value = CustomValue[x][y];
10299 int ce_score = ei->collect_score;
10300 int target_element;
10301 int old_element = Feld[x][y];
10303 /* always use default change event to prevent running into a loop */
10304 if (ChangeEvent[x][y] == -1)
10305 ChangeEvent[x][y] = CE_DELAY;
10307 if (ChangeEvent[x][y] == CE_DELAY)
10309 /* reset actual trigger element, trigger player and action element */
10310 change->actual_trigger_element = EL_EMPTY;
10311 change->actual_trigger_player = EL_EMPTY;
10312 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10313 change->actual_trigger_side = CH_SIDE_NONE;
10314 change->actual_trigger_ce_value = 0;
10315 change->actual_trigger_ce_score = 0;
10318 /* do not change elements more than a specified maximum number of changes */
10319 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10322 ChangeCount[x][y]++; /* count number of changes in the same frame */
10324 if (change->explode)
10331 if (change->use_target_content)
10333 boolean complete_replace = TRUE;
10334 boolean can_replace[3][3];
10337 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10340 boolean is_walkable;
10341 boolean is_diggable;
10342 boolean is_collectible;
10343 boolean is_removable;
10344 boolean is_destructible;
10345 int ex = x + xx - 1;
10346 int ey = y + yy - 1;
10347 int content_element = change->target_content.e[xx][yy];
10350 can_replace[xx][yy] = TRUE;
10352 if (ex == x && ey == y) /* do not check changing element itself */
10355 if (content_element == EL_EMPTY_SPACE)
10357 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10362 if (!IN_LEV_FIELD(ex, ey))
10364 can_replace[xx][yy] = FALSE;
10365 complete_replace = FALSE;
10372 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10373 e = MovingOrBlocked2Element(ex, ey);
10375 is_empty = (IS_FREE(ex, ey) ||
10376 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10378 is_walkable = (is_empty || IS_WALKABLE(e));
10379 is_diggable = (is_empty || IS_DIGGABLE(e));
10380 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10381 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10382 is_removable = (is_diggable || is_collectible);
10384 can_replace[xx][yy] =
10385 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10386 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10387 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10388 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10389 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10390 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10391 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10393 if (!can_replace[xx][yy])
10394 complete_replace = FALSE;
10397 if (!change->only_if_complete || complete_replace)
10399 boolean something_has_changed = FALSE;
10401 if (change->only_if_complete && change->use_random_replace &&
10402 RND(100) < change->random_percentage)
10405 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10407 int ex = x + xx - 1;
10408 int ey = y + yy - 1;
10409 int content_element;
10411 if (can_replace[xx][yy] && (!change->use_random_replace ||
10412 RND(100) < change->random_percentage))
10414 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10415 RemoveMovingField(ex, ey);
10417 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10419 content_element = change->target_content.e[xx][yy];
10420 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10421 ce_value, ce_score);
10423 CreateElementFromChange(ex, ey, target_element);
10425 something_has_changed = TRUE;
10427 /* for symmetry reasons, freeze newly created border elements */
10428 if (ex != x || ey != y)
10429 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10433 if (something_has_changed)
10435 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10436 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10442 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10443 ce_value, ce_score);
10445 if (element == EL_DIAGONAL_GROWING ||
10446 element == EL_DIAGONAL_SHRINKING)
10448 target_element = Store[x][y];
10450 Store[x][y] = EL_EMPTY;
10453 CreateElementFromChange(x, y, target_element);
10455 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10456 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10459 /* this uses direct change before indirect change */
10460 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10465 static void HandleElementChange(int x, int y, int page)
10467 int element = MovingOrBlocked2Element(x, y);
10468 struct ElementInfo *ei = &element_info[element];
10469 struct ElementChangeInfo *change = &ei->change_page[page];
10470 boolean handle_action_before_change = FALSE;
10473 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10474 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10477 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10478 x, y, element, element_info[element].token_name);
10479 printf("HandleElementChange(): This should never happen!\n");
10484 /* this can happen with classic bombs on walkable, changing elements */
10485 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10490 if (ChangeDelay[x][y] == 0) /* initialize element change */
10492 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10494 if (change->can_change)
10496 /* !!! not clear why graphic animation should be reset at all here !!! */
10497 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10498 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10501 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10503 When using an animation frame delay of 1 (this only happens with
10504 "sp_zonk.moving.left/right" in the classic graphics), the default
10505 (non-moving) animation shows wrong animation frames (while the
10506 moving animation, like "sp_zonk.moving.left/right", is correct,
10507 so this graphical bug never shows up with the classic graphics).
10508 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10509 be drawn instead of the correct frames 0,1,2,3. This is caused by
10510 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10511 an element change: First when the change delay ("ChangeDelay[][]")
10512 counter has reached zero after decrementing, then a second time in
10513 the next frame (after "GfxFrame[][]" was already incremented) when
10514 "ChangeDelay[][]" is reset to the initial delay value again.
10516 This causes frame 0 to be drawn twice, while the last frame won't
10517 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10519 As some animations may already be cleverly designed around this bug
10520 (at least the "Snake Bite" snake tail animation does this), it cannot
10521 simply be fixed here without breaking such existing animations.
10522 Unfortunately, it cannot easily be detected if a graphics set was
10523 designed "before" or "after" the bug was fixed. As a workaround,
10524 a new graphics set option "game.graphics_engine_version" was added
10525 to be able to specify the game's major release version for which the
10526 graphics set was designed, which can then be used to decide if the
10527 bugfix should be used (version 4 and above) or not (version 3 or
10528 below, or if no version was specified at all, as with old sets).
10530 (The wrong/fixed animation frames can be tested with the test level set
10531 "test_gfxframe" and level "000", which contains a specially prepared
10532 custom element at level position (x/y) == (11/9) which uses the zonk
10533 animation mentioned above. Using "game.graphics_engine_version: 4"
10534 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10535 This can also be seen from the debug output for this test element.)
10538 /* when a custom element is about to change (for example by change delay),
10539 do not reset graphic animation when the custom element is moving */
10540 if (game.graphics_engine_version < 4 &&
10543 ResetGfxAnimation(x, y);
10544 ResetRandomAnimationValue(x, y);
10547 if (change->pre_change_function)
10548 change->pre_change_function(x, y);
10552 ChangeDelay[x][y]--;
10554 if (ChangeDelay[x][y] != 0) /* continue element change */
10556 if (change->can_change)
10558 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10560 if (IS_ANIMATED(graphic))
10561 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10563 if (change->change_function)
10564 change->change_function(x, y);
10567 else /* finish element change */
10569 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10571 page = ChangePage[x][y];
10572 ChangePage[x][y] = -1;
10574 change = &ei->change_page[page];
10577 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10579 ChangeDelay[x][y] = 1; /* try change after next move step */
10580 ChangePage[x][y] = page; /* remember page to use for change */
10585 /* special case: set new level random seed before changing element */
10586 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10587 handle_action_before_change = TRUE;
10589 if (change->has_action && handle_action_before_change)
10590 ExecuteCustomElementAction(x, y, element, page);
10592 if (change->can_change)
10594 if (ChangeElement(x, y, element, page))
10596 if (change->post_change_function)
10597 change->post_change_function(x, y);
10601 if (change->has_action && !handle_action_before_change)
10602 ExecuteCustomElementAction(x, y, element, page);
10606 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10607 int trigger_element,
10609 int trigger_player,
10613 boolean change_done_any = FALSE;
10614 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10617 if (!(trigger_events[trigger_element][trigger_event]))
10620 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10622 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10624 int element = EL_CUSTOM_START + i;
10625 boolean change_done = FALSE;
10628 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10629 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10632 for (p = 0; p < element_info[element].num_change_pages; p++)
10634 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10636 if (change->can_change_or_has_action &&
10637 change->has_event[trigger_event] &&
10638 change->trigger_side & trigger_side &&
10639 change->trigger_player & trigger_player &&
10640 change->trigger_page & trigger_page_bits &&
10641 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10643 change->actual_trigger_element = trigger_element;
10644 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10645 change->actual_trigger_player_bits = trigger_player;
10646 change->actual_trigger_side = trigger_side;
10647 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10648 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10650 if ((change->can_change && !change_done) || change->has_action)
10654 SCAN_PLAYFIELD(x, y)
10656 if (Feld[x][y] == element)
10658 if (change->can_change && !change_done)
10660 /* if element already changed in this frame, not only prevent
10661 another element change (checked in ChangeElement()), but
10662 also prevent additional element actions for this element */
10664 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10665 !level.use_action_after_change_bug)
10668 ChangeDelay[x][y] = 1;
10669 ChangeEvent[x][y] = trigger_event;
10671 HandleElementChange(x, y, p);
10673 else if (change->has_action)
10675 /* if element already changed in this frame, not only prevent
10676 another element change (checked in ChangeElement()), but
10677 also prevent additional element actions for this element */
10679 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10680 !level.use_action_after_change_bug)
10683 ExecuteCustomElementAction(x, y, element, p);
10684 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10689 if (change->can_change)
10691 change_done = TRUE;
10692 change_done_any = TRUE;
10699 RECURSION_LOOP_DETECTION_END();
10701 return change_done_any;
10704 static boolean CheckElementChangeExt(int x, int y,
10706 int trigger_element,
10708 int trigger_player,
10711 boolean change_done = FALSE;
10714 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10715 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10718 if (Feld[x][y] == EL_BLOCKED)
10720 Blocked2Moving(x, y, &x, &y);
10721 element = Feld[x][y];
10724 /* check if element has already changed or is about to change after moving */
10725 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10726 Feld[x][y] != element) ||
10728 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10729 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10730 ChangePage[x][y] != -1)))
10733 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10735 for (p = 0; p < element_info[element].num_change_pages; p++)
10737 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10739 /* check trigger element for all events where the element that is checked
10740 for changing interacts with a directly adjacent element -- this is
10741 different to element changes that affect other elements to change on the
10742 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10743 boolean check_trigger_element =
10744 (trigger_event == CE_TOUCHING_X ||
10745 trigger_event == CE_HITTING_X ||
10746 trigger_event == CE_HIT_BY_X ||
10747 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10749 if (change->can_change_or_has_action &&
10750 change->has_event[trigger_event] &&
10751 change->trigger_side & trigger_side &&
10752 change->trigger_player & trigger_player &&
10753 (!check_trigger_element ||
10754 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10756 change->actual_trigger_element = trigger_element;
10757 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10758 change->actual_trigger_player_bits = trigger_player;
10759 change->actual_trigger_side = trigger_side;
10760 change->actual_trigger_ce_value = CustomValue[x][y];
10761 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10763 /* special case: trigger element not at (x,y) position for some events */
10764 if (check_trigger_element)
10776 { 0, 0 }, { 0, 0 }, { 0, 0 },
10780 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10781 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10783 change->actual_trigger_ce_value = CustomValue[xx][yy];
10784 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10787 if (change->can_change && !change_done)
10789 ChangeDelay[x][y] = 1;
10790 ChangeEvent[x][y] = trigger_event;
10792 HandleElementChange(x, y, p);
10794 change_done = TRUE;
10796 else if (change->has_action)
10798 ExecuteCustomElementAction(x, y, element, p);
10799 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10804 RECURSION_LOOP_DETECTION_END();
10806 return change_done;
10809 static void PlayPlayerSound(struct PlayerInfo *player)
10811 int jx = player->jx, jy = player->jy;
10812 int sound_element = player->artwork_element;
10813 int last_action = player->last_action_waiting;
10814 int action = player->action_waiting;
10816 if (player->is_waiting)
10818 if (action != last_action)
10819 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10821 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10825 if (action != last_action)
10826 StopSound(element_info[sound_element].sound[last_action]);
10828 if (last_action == ACTION_SLEEPING)
10829 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10833 static void PlayAllPlayersSound()
10837 for (i = 0; i < MAX_PLAYERS; i++)
10838 if (stored_player[i].active)
10839 PlayPlayerSound(&stored_player[i]);
10842 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10844 boolean last_waiting = player->is_waiting;
10845 int move_dir = player->MovDir;
10847 player->dir_waiting = move_dir;
10848 player->last_action_waiting = player->action_waiting;
10852 if (!last_waiting) /* not waiting -> waiting */
10854 player->is_waiting = TRUE;
10856 player->frame_counter_bored =
10858 game.player_boring_delay_fixed +
10859 GetSimpleRandom(game.player_boring_delay_random);
10860 player->frame_counter_sleeping =
10862 game.player_sleeping_delay_fixed +
10863 GetSimpleRandom(game.player_sleeping_delay_random);
10865 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10868 if (game.player_sleeping_delay_fixed +
10869 game.player_sleeping_delay_random > 0 &&
10870 player->anim_delay_counter == 0 &&
10871 player->post_delay_counter == 0 &&
10872 FrameCounter >= player->frame_counter_sleeping)
10873 player->is_sleeping = TRUE;
10874 else if (game.player_boring_delay_fixed +
10875 game.player_boring_delay_random > 0 &&
10876 FrameCounter >= player->frame_counter_bored)
10877 player->is_bored = TRUE;
10879 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10880 player->is_bored ? ACTION_BORING :
10883 if (player->is_sleeping && player->use_murphy)
10885 /* special case for sleeping Murphy when leaning against non-free tile */
10887 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10888 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10889 !IS_MOVING(player->jx - 1, player->jy)))
10890 move_dir = MV_LEFT;
10891 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10892 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10893 !IS_MOVING(player->jx + 1, player->jy)))
10894 move_dir = MV_RIGHT;
10896 player->is_sleeping = FALSE;
10898 player->dir_waiting = move_dir;
10901 if (player->is_sleeping)
10903 if (player->num_special_action_sleeping > 0)
10905 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10907 int last_special_action = player->special_action_sleeping;
10908 int num_special_action = player->num_special_action_sleeping;
10909 int special_action =
10910 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10911 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10912 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10913 last_special_action + 1 : ACTION_SLEEPING);
10914 int special_graphic =
10915 el_act_dir2img(player->artwork_element, special_action, move_dir);
10917 player->anim_delay_counter =
10918 graphic_info[special_graphic].anim_delay_fixed +
10919 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10920 player->post_delay_counter =
10921 graphic_info[special_graphic].post_delay_fixed +
10922 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10924 player->special_action_sleeping = special_action;
10927 if (player->anim_delay_counter > 0)
10929 player->action_waiting = player->special_action_sleeping;
10930 player->anim_delay_counter--;
10932 else if (player->post_delay_counter > 0)
10934 player->post_delay_counter--;
10938 else if (player->is_bored)
10940 if (player->num_special_action_bored > 0)
10942 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10944 int special_action =
10945 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10946 int special_graphic =
10947 el_act_dir2img(player->artwork_element, special_action, move_dir);
10949 player->anim_delay_counter =
10950 graphic_info[special_graphic].anim_delay_fixed +
10951 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10952 player->post_delay_counter =
10953 graphic_info[special_graphic].post_delay_fixed +
10954 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10956 player->special_action_bored = special_action;
10959 if (player->anim_delay_counter > 0)
10961 player->action_waiting = player->special_action_bored;
10962 player->anim_delay_counter--;
10964 else if (player->post_delay_counter > 0)
10966 player->post_delay_counter--;
10971 else if (last_waiting) /* waiting -> not waiting */
10973 player->is_waiting = FALSE;
10974 player->is_bored = FALSE;
10975 player->is_sleeping = FALSE;
10977 player->frame_counter_bored = -1;
10978 player->frame_counter_sleeping = -1;
10980 player->anim_delay_counter = 0;
10981 player->post_delay_counter = 0;
10983 player->dir_waiting = player->MovDir;
10984 player->action_waiting = ACTION_DEFAULT;
10986 player->special_action_bored = ACTION_DEFAULT;
10987 player->special_action_sleeping = ACTION_DEFAULT;
10991 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10993 if ((!player->is_moving && player->was_moving) ||
10994 (player->MovPos == 0 && player->was_moving) ||
10995 (player->is_snapping && !player->was_snapping) ||
10996 (player->is_dropping && !player->was_dropping))
10998 if (!CheckSaveEngineSnapshotToList())
11001 player->was_moving = FALSE;
11002 player->was_snapping = TRUE;
11003 player->was_dropping = TRUE;
11007 if (player->is_moving)
11008 player->was_moving = TRUE;
11010 if (!player->is_snapping)
11011 player->was_snapping = FALSE;
11013 if (!player->is_dropping)
11014 player->was_dropping = FALSE;
11018 static void CheckSingleStepMode(struct PlayerInfo *player)
11020 if (tape.single_step && tape.recording && !tape.pausing)
11022 /* as it is called "single step mode", just return to pause mode when the
11023 player stopped moving after one tile (or never starts moving at all) */
11024 if (!player->is_moving &&
11025 !player->is_pushing &&
11026 !player->is_dropping_pressed)
11028 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11029 SnapField(player, 0, 0); /* stop snapping */
11033 CheckSaveEngineSnapshot(player);
11036 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11038 int left = player_action & JOY_LEFT;
11039 int right = player_action & JOY_RIGHT;
11040 int up = player_action & JOY_UP;
11041 int down = player_action & JOY_DOWN;
11042 int button1 = player_action & JOY_BUTTON_1;
11043 int button2 = player_action & JOY_BUTTON_2;
11044 int dx = (left ? -1 : right ? 1 : 0);
11045 int dy = (up ? -1 : down ? 1 : 0);
11047 if (!player->active || tape.pausing)
11053 SnapField(player, dx, dy);
11057 DropElement(player);
11059 MovePlayer(player, dx, dy);
11062 CheckSingleStepMode(player);
11064 SetPlayerWaiting(player, FALSE);
11066 return player_action;
11070 /* no actions for this player (no input at player's configured device) */
11072 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11073 SnapField(player, 0, 0);
11074 CheckGravityMovementWhenNotMoving(player);
11076 if (player->MovPos == 0)
11077 SetPlayerWaiting(player, TRUE);
11079 if (player->MovPos == 0) /* needed for tape.playing */
11080 player->is_moving = FALSE;
11082 player->is_dropping = FALSE;
11083 player->is_dropping_pressed = FALSE;
11084 player->drop_pressed_delay = 0;
11086 CheckSingleStepMode(player);
11092 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11095 if (!tape.use_mouse)
11098 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11099 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11100 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11103 static void SetTapeActionFromMouseAction(byte *tape_action,
11104 struct MouseActionInfo *mouse_action)
11106 if (!tape.use_mouse)
11109 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11110 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11111 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11114 static void CheckLevelTime()
11118 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11119 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11121 if (level.native_em_level->lev->home == 0) /* all players at home */
11123 PlayerWins(local_player);
11125 AllPlayersGone = TRUE;
11127 level.native_em_level->lev->home = -1;
11130 if (level.native_em_level->ply[0]->alive == 0 &&
11131 level.native_em_level->ply[1]->alive == 0 &&
11132 level.native_em_level->ply[2]->alive == 0 &&
11133 level.native_em_level->ply[3]->alive == 0) /* all dead */
11134 AllPlayersGone = TRUE;
11136 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11138 if (game_sp.LevelSolved &&
11139 !game_sp.GameOver) /* game won */
11141 PlayerWins(local_player);
11143 game_sp.GameOver = TRUE;
11145 AllPlayersGone = TRUE;
11148 if (game_sp.GameOver) /* game lost */
11149 AllPlayersGone = TRUE;
11151 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11153 if (game_mm.level_solved &&
11154 !game_mm.game_over) /* game won */
11156 PlayerWins(local_player);
11158 game_mm.game_over = TRUE;
11160 AllPlayersGone = TRUE;
11163 if (game_mm.game_over) /* game lost */
11164 AllPlayersGone = TRUE;
11167 if (TimeFrames >= FRAMES_PER_SECOND)
11172 for (i = 0; i < MAX_PLAYERS; i++)
11174 struct PlayerInfo *player = &stored_player[i];
11176 if (SHIELD_ON(player))
11178 player->shield_normal_time_left--;
11180 if (player->shield_deadly_time_left > 0)
11181 player->shield_deadly_time_left--;
11185 if (!local_player->LevelSolved && !level.use_step_counter)
11193 if (TimeLeft <= 10 && setup.time_limit)
11194 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11196 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11197 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11199 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11201 if (!TimeLeft && setup.time_limit)
11203 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11204 level.native_em_level->lev->killed_out_of_time = TRUE;
11206 for (i = 0; i < MAX_PLAYERS; i++)
11207 KillPlayer(&stored_player[i]);
11210 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11212 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11215 level.native_em_level->lev->time =
11216 (game.no_time_limit ? TimePlayed : TimeLeft);
11219 if (tape.recording || tape.playing)
11220 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11223 if (tape.recording || tape.playing)
11224 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11226 UpdateAndDisplayGameControlValues();
11229 void AdvanceFrameAndPlayerCounters(int player_nr)
11233 /* advance frame counters (global frame counter and time frame counter) */
11237 /* advance player counters (counters for move delay, move animation etc.) */
11238 for (i = 0; i < MAX_PLAYERS; i++)
11240 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11241 int move_delay_value = stored_player[i].move_delay_value;
11242 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11244 if (!advance_player_counters) /* not all players may be affected */
11247 if (move_frames == 0) /* less than one move per game frame */
11249 int stepsize = TILEX / move_delay_value;
11250 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11251 int count = (stored_player[i].is_moving ?
11252 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11254 if (count % delay == 0)
11258 stored_player[i].Frame += move_frames;
11260 if (stored_player[i].MovPos != 0)
11261 stored_player[i].StepFrame += move_frames;
11263 if (stored_player[i].move_delay > 0)
11264 stored_player[i].move_delay--;
11266 /* due to bugs in previous versions, counter must count up, not down */
11267 if (stored_player[i].push_delay != -1)
11268 stored_player[i].push_delay++;
11270 if (stored_player[i].drop_delay > 0)
11271 stored_player[i].drop_delay--;
11273 if (stored_player[i].is_dropping_pressed)
11274 stored_player[i].drop_pressed_delay++;
11278 void StartGameActions(boolean init_network_game, boolean record_tape,
11281 unsigned int new_random_seed = InitRND(random_seed);
11284 TapeStartRecording(new_random_seed);
11286 if (init_network_game)
11288 SendToServer_StartPlaying();
11296 void GameActionsExt()
11299 static unsigned int game_frame_delay = 0;
11301 unsigned int game_frame_delay_value;
11302 byte *recorded_player_action;
11303 byte summarized_player_action = 0;
11304 byte tape_action[MAX_PLAYERS];
11307 /* detect endless loops, caused by custom element programming */
11308 if (recursion_loop_detected && recursion_loop_depth == 0)
11310 char *message = getStringCat3("Internal Error! Element ",
11311 EL_NAME(recursion_loop_element),
11312 " caused endless loop! Quit the game?");
11314 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11315 EL_NAME(recursion_loop_element));
11317 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11319 recursion_loop_detected = FALSE; /* if game should be continued */
11326 if (game.restart_level)
11327 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11329 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11330 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11332 if (level.native_em_level->lev->home == 0) /* all players at home */
11334 PlayerWins(local_player);
11336 AllPlayersGone = TRUE;
11338 level.native_em_level->lev->home = -1;
11341 if (level.native_em_level->ply[0]->alive == 0 &&
11342 level.native_em_level->ply[1]->alive == 0 &&
11343 level.native_em_level->ply[2]->alive == 0 &&
11344 level.native_em_level->ply[3]->alive == 0) /* all dead */
11345 AllPlayersGone = TRUE;
11347 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11349 if (game_sp.LevelSolved &&
11350 !game_sp.GameOver) /* game won */
11352 PlayerWins(local_player);
11354 game_sp.GameOver = TRUE;
11356 AllPlayersGone = TRUE;
11359 if (game_sp.GameOver) /* game lost */
11360 AllPlayersGone = TRUE;
11362 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11364 if (game_mm.level_solved &&
11365 !game_mm.game_over) /* game won */
11367 PlayerWins(local_player);
11369 game_mm.game_over = TRUE;
11371 AllPlayersGone = TRUE;
11374 if (game_mm.game_over) /* game lost */
11375 AllPlayersGone = TRUE;
11378 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11381 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11384 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11387 game_frame_delay_value =
11388 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11390 if (tape.playing && tape.warp_forward && !tape.pausing)
11391 game_frame_delay_value = 0;
11393 SetVideoFrameDelay(game_frame_delay_value);
11397 /* ---------- main game synchronization point ---------- */
11399 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11401 printf("::: skip == %d\n", skip);
11404 /* ---------- main game synchronization point ---------- */
11406 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11410 if (network_playing && !network_player_action_received)
11412 /* try to get network player actions in time */
11414 /* last chance to get network player actions without main loop delay */
11415 HandleNetworking();
11417 /* game was quit by network peer */
11418 if (game_status != GAME_MODE_PLAYING)
11421 if (!network_player_action_received)
11422 return; /* failed to get network player actions in time */
11424 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11430 /* at this point we know that we really continue executing the game */
11432 network_player_action_received = FALSE;
11434 /* when playing tape, read previously recorded player input from tape data */
11435 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11437 local_player->effective_mouse_action = local_player->mouse_action;
11439 if (recorded_player_action != NULL)
11440 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11441 recorded_player_action);
11443 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11447 if (tape.set_centered_player)
11449 game.centered_player_nr_next = tape.centered_player_nr_next;
11450 game.set_centered_player = TRUE;
11453 for (i = 0; i < MAX_PLAYERS; i++)
11455 summarized_player_action |= stored_player[i].action;
11457 if (!network_playing && (game.team_mode || tape.playing))
11458 stored_player[i].effective_action = stored_player[i].action;
11461 if (network_playing)
11462 SendToServer_MovePlayer(summarized_player_action);
11464 // summarize all actions at local players mapped input device position
11465 // (this allows using different input devices in single player mode)
11466 if (!network.enabled && !game.team_mode)
11467 stored_player[map_player_action[local_player->index_nr]].effective_action =
11468 summarized_player_action;
11470 if (tape.recording &&
11472 setup.input_on_focus &&
11473 game.centered_player_nr != -1)
11475 for (i = 0; i < MAX_PLAYERS; i++)
11476 stored_player[i].effective_action =
11477 (i == game.centered_player_nr ? summarized_player_action : 0);
11480 if (recorded_player_action != NULL)
11481 for (i = 0; i < MAX_PLAYERS; i++)
11482 stored_player[i].effective_action = recorded_player_action[i];
11484 for (i = 0; i < MAX_PLAYERS; i++)
11486 tape_action[i] = stored_player[i].effective_action;
11488 /* (this may happen in the RND game engine if a player was not present on
11489 the playfield on level start, but appeared later from a custom element */
11490 if (setup.team_mode &&
11493 !tape.player_participates[i])
11494 tape.player_participates[i] = TRUE;
11497 SetTapeActionFromMouseAction(tape_action,
11498 &local_player->effective_mouse_action);
11500 /* only record actions from input devices, but not programmed actions */
11501 if (tape.recording)
11502 TapeRecordAction(tape_action);
11504 #if USE_NEW_PLAYER_ASSIGNMENTS
11505 // !!! also map player actions in single player mode !!!
11506 // if (game.team_mode)
11509 byte mapped_action[MAX_PLAYERS];
11511 #if DEBUG_PLAYER_ACTIONS
11513 for (i = 0; i < MAX_PLAYERS; i++)
11514 printf(" %d, ", stored_player[i].effective_action);
11517 for (i = 0; i < MAX_PLAYERS; i++)
11518 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11520 for (i = 0; i < MAX_PLAYERS; i++)
11521 stored_player[i].effective_action = mapped_action[i];
11523 #if DEBUG_PLAYER_ACTIONS
11525 for (i = 0; i < MAX_PLAYERS; i++)
11526 printf(" %d, ", stored_player[i].effective_action);
11530 #if DEBUG_PLAYER_ACTIONS
11534 for (i = 0; i < MAX_PLAYERS; i++)
11535 printf(" %d, ", stored_player[i].effective_action);
11541 for (i = 0; i < MAX_PLAYERS; i++)
11543 // allow engine snapshot in case of changed movement attempt
11544 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11545 (stored_player[i].effective_action & KEY_MOTION))
11546 game.snapshot.changed_action = TRUE;
11548 // allow engine snapshot in case of snapping/dropping attempt
11549 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11550 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11551 game.snapshot.changed_action = TRUE;
11553 game.snapshot.last_action[i] = stored_player[i].effective_action;
11556 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11558 GameActions_EM_Main();
11560 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11562 GameActions_SP_Main();
11564 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11566 GameActions_MM_Main();
11570 GameActions_RND_Main();
11573 BlitScreenToBitmap(backbuffer);
11577 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11579 if (global.show_frames_per_second)
11581 static unsigned int fps_counter = 0;
11582 static int fps_frames = 0;
11583 unsigned int fps_delay_ms = Counter() - fps_counter;
11587 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11589 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11592 fps_counter = Counter();
11594 /* always draw FPS to screen after FPS value was updated */
11595 redraw_mask |= REDRAW_FPS;
11598 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11599 if (GetDrawDeactivationMask() == REDRAW_NONE)
11600 redraw_mask |= REDRAW_FPS;
11604 static void GameActions_CheckSaveEngineSnapshot()
11606 if (!game.snapshot.save_snapshot)
11609 // clear flag for saving snapshot _before_ saving snapshot
11610 game.snapshot.save_snapshot = FALSE;
11612 SaveEngineSnapshotToList();
11619 GameActions_CheckSaveEngineSnapshot();
11622 void GameActions_EM_Main()
11624 byte effective_action[MAX_PLAYERS];
11625 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11628 for (i = 0; i < MAX_PLAYERS; i++)
11629 effective_action[i] = stored_player[i].effective_action;
11631 GameActions_EM(effective_action, warp_mode);
11634 void GameActions_SP_Main()
11636 byte effective_action[MAX_PLAYERS];
11637 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11640 for (i = 0; i < MAX_PLAYERS; i++)
11641 effective_action[i] = stored_player[i].effective_action;
11643 GameActions_SP(effective_action, warp_mode);
11645 for (i = 0; i < MAX_PLAYERS; i++)
11647 if (stored_player[i].force_dropping)
11648 stored_player[i].action |= KEY_BUTTON_DROP;
11650 stored_player[i].force_dropping = FALSE;
11654 void GameActions_MM_Main()
11656 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11658 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11661 void GameActions_RND_Main()
11666 void GameActions_RND()
11668 int magic_wall_x = 0, magic_wall_y = 0;
11669 int i, x, y, element, graphic, last_gfx_frame;
11671 InitPlayfieldScanModeVars();
11673 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11675 SCAN_PLAYFIELD(x, y)
11677 ChangeCount[x][y] = 0;
11678 ChangeEvent[x][y] = -1;
11682 if (game.set_centered_player)
11684 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11686 /* switching to "all players" only possible if all players fit to screen */
11687 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11689 game.centered_player_nr_next = game.centered_player_nr;
11690 game.set_centered_player = FALSE;
11693 /* do not switch focus to non-existing (or non-active) player */
11694 if (game.centered_player_nr_next >= 0 &&
11695 !stored_player[game.centered_player_nr_next].active)
11697 game.centered_player_nr_next = game.centered_player_nr;
11698 game.set_centered_player = FALSE;
11702 if (game.set_centered_player &&
11703 ScreenMovPos == 0) /* screen currently aligned at tile position */
11707 if (game.centered_player_nr_next == -1)
11709 setScreenCenteredToAllPlayers(&sx, &sy);
11713 sx = stored_player[game.centered_player_nr_next].jx;
11714 sy = stored_player[game.centered_player_nr_next].jy;
11717 game.centered_player_nr = game.centered_player_nr_next;
11718 game.set_centered_player = FALSE;
11720 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11721 DrawGameDoorValues();
11724 for (i = 0; i < MAX_PLAYERS; i++)
11726 int actual_player_action = stored_player[i].effective_action;
11729 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11730 - rnd_equinox_tetrachloride 048
11731 - rnd_equinox_tetrachloride_ii 096
11732 - rnd_emanuel_schmieg 002
11733 - doctor_sloan_ww 001, 020
11735 if (stored_player[i].MovPos == 0)
11736 CheckGravityMovement(&stored_player[i]);
11739 /* overwrite programmed action with tape action */
11740 if (stored_player[i].programmed_action)
11741 actual_player_action = stored_player[i].programmed_action;
11743 PlayerActions(&stored_player[i], actual_player_action);
11745 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11748 ScrollScreen(NULL, SCROLL_GO_ON);
11750 /* for backwards compatibility, the following code emulates a fixed bug that
11751 occured when pushing elements (causing elements that just made their last
11752 pushing step to already (if possible) make their first falling step in the
11753 same game frame, which is bad); this code is also needed to use the famous
11754 "spring push bug" which is used in older levels and might be wanted to be
11755 used also in newer levels, but in this case the buggy pushing code is only
11756 affecting the "spring" element and no other elements */
11758 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11760 for (i = 0; i < MAX_PLAYERS; i++)
11762 struct PlayerInfo *player = &stored_player[i];
11763 int x = player->jx;
11764 int y = player->jy;
11766 if (player->active && player->is_pushing && player->is_moving &&
11768 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11769 Feld[x][y] == EL_SPRING))
11771 ContinueMoving(x, y);
11773 /* continue moving after pushing (this is actually a bug) */
11774 if (!IS_MOVING(x, y))
11775 Stop[x][y] = FALSE;
11780 SCAN_PLAYFIELD(x, y)
11782 ChangeCount[x][y] = 0;
11783 ChangeEvent[x][y] = -1;
11785 /* this must be handled before main playfield loop */
11786 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11789 if (MovDelay[x][y] <= 0)
11793 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11796 if (MovDelay[x][y] <= 0)
11799 TEST_DrawLevelField(x, y);
11801 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11806 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11808 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11809 printf("GameActions(): This should never happen!\n");
11811 ChangePage[x][y] = -1;
11815 Stop[x][y] = FALSE;
11816 if (WasJustMoving[x][y] > 0)
11817 WasJustMoving[x][y]--;
11818 if (WasJustFalling[x][y] > 0)
11819 WasJustFalling[x][y]--;
11820 if (CheckCollision[x][y] > 0)
11821 CheckCollision[x][y]--;
11822 if (CheckImpact[x][y] > 0)
11823 CheckImpact[x][y]--;
11827 /* reset finished pushing action (not done in ContinueMoving() to allow
11828 continuous pushing animation for elements with zero push delay) */
11829 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11831 ResetGfxAnimation(x, y);
11832 TEST_DrawLevelField(x, y);
11836 if (IS_BLOCKED(x, y))
11840 Blocked2Moving(x, y, &oldx, &oldy);
11841 if (!IS_MOVING(oldx, oldy))
11843 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11844 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11845 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11846 printf("GameActions(): This should never happen!\n");
11852 SCAN_PLAYFIELD(x, y)
11854 element = Feld[x][y];
11855 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11856 last_gfx_frame = GfxFrame[x][y];
11858 ResetGfxFrame(x, y);
11860 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11861 DrawLevelGraphicAnimation(x, y, graphic);
11863 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11864 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11865 ResetRandomAnimationValue(x, y);
11867 SetRandomAnimationValue(x, y);
11869 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11871 if (IS_INACTIVE(element))
11873 if (IS_ANIMATED(graphic))
11874 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11879 /* this may take place after moving, so 'element' may have changed */
11880 if (IS_CHANGING(x, y) &&
11881 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11883 int page = element_info[element].event_page_nr[CE_DELAY];
11885 HandleElementChange(x, y, page);
11887 element = Feld[x][y];
11888 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11891 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11895 element = Feld[x][y];
11896 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11898 if (IS_ANIMATED(graphic) &&
11899 !IS_MOVING(x, y) &&
11901 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11903 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11904 TEST_DrawTwinkleOnField(x, y);
11906 else if (element == EL_ACID)
11909 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11911 else if ((element == EL_EXIT_OPEN ||
11912 element == EL_EM_EXIT_OPEN ||
11913 element == EL_SP_EXIT_OPEN ||
11914 element == EL_STEEL_EXIT_OPEN ||
11915 element == EL_EM_STEEL_EXIT_OPEN ||
11916 element == EL_SP_TERMINAL ||
11917 element == EL_SP_TERMINAL_ACTIVE ||
11918 element == EL_EXTRA_TIME ||
11919 element == EL_SHIELD_NORMAL ||
11920 element == EL_SHIELD_DEADLY) &&
11921 IS_ANIMATED(graphic))
11922 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11923 else if (IS_MOVING(x, y))
11924 ContinueMoving(x, y);
11925 else if (IS_ACTIVE_BOMB(element))
11926 CheckDynamite(x, y);
11927 else if (element == EL_AMOEBA_GROWING)
11928 AmoebeWaechst(x, y);
11929 else if (element == EL_AMOEBA_SHRINKING)
11930 AmoebaDisappearing(x, y);
11932 #if !USE_NEW_AMOEBA_CODE
11933 else if (IS_AMOEBALIVE(element))
11934 AmoebeAbleger(x, y);
11937 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11939 else if (element == EL_EXIT_CLOSED)
11941 else if (element == EL_EM_EXIT_CLOSED)
11943 else if (element == EL_STEEL_EXIT_CLOSED)
11944 CheckExitSteel(x, y);
11945 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11946 CheckExitSteelEM(x, y);
11947 else if (element == EL_SP_EXIT_CLOSED)
11949 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11950 element == EL_EXPANDABLE_STEELWALL_GROWING)
11951 MauerWaechst(x, y);
11952 else if (element == EL_EXPANDABLE_WALL ||
11953 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11954 element == EL_EXPANDABLE_WALL_VERTICAL ||
11955 element == EL_EXPANDABLE_WALL_ANY ||
11956 element == EL_BD_EXPANDABLE_WALL)
11957 MauerAbleger(x, y);
11958 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11959 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11960 element == EL_EXPANDABLE_STEELWALL_ANY)
11961 MauerAblegerStahl(x, y);
11962 else if (element == EL_FLAMES)
11963 CheckForDragon(x, y);
11964 else if (element == EL_EXPLOSION)
11965 ; /* drawing of correct explosion animation is handled separately */
11966 else if (element == EL_ELEMENT_SNAPPING ||
11967 element == EL_DIAGONAL_SHRINKING ||
11968 element == EL_DIAGONAL_GROWING)
11970 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11972 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11974 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11975 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11977 if (IS_BELT_ACTIVE(element))
11978 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11980 if (game.magic_wall_active)
11982 int jx = local_player->jx, jy = local_player->jy;
11984 /* play the element sound at the position nearest to the player */
11985 if ((element == EL_MAGIC_WALL_FULL ||
11986 element == EL_MAGIC_WALL_ACTIVE ||
11987 element == EL_MAGIC_WALL_EMPTYING ||
11988 element == EL_BD_MAGIC_WALL_FULL ||
11989 element == EL_BD_MAGIC_WALL_ACTIVE ||
11990 element == EL_BD_MAGIC_WALL_EMPTYING ||
11991 element == EL_DC_MAGIC_WALL_FULL ||
11992 element == EL_DC_MAGIC_WALL_ACTIVE ||
11993 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11994 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12002 #if USE_NEW_AMOEBA_CODE
12003 /* new experimental amoeba growth stuff */
12004 if (!(FrameCounter % 8))
12006 static unsigned int random = 1684108901;
12008 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12010 x = RND(lev_fieldx);
12011 y = RND(lev_fieldy);
12012 element = Feld[x][y];
12014 if (!IS_PLAYER(x,y) &&
12015 (element == EL_EMPTY ||
12016 CAN_GROW_INTO(element) ||
12017 element == EL_QUICKSAND_EMPTY ||
12018 element == EL_QUICKSAND_FAST_EMPTY ||
12019 element == EL_ACID_SPLASH_LEFT ||
12020 element == EL_ACID_SPLASH_RIGHT))
12022 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12023 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12024 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12025 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12026 Feld[x][y] = EL_AMOEBA_DROP;
12029 random = random * 129 + 1;
12034 game.explosions_delayed = FALSE;
12036 SCAN_PLAYFIELD(x, y)
12038 element = Feld[x][y];
12040 if (ExplodeField[x][y])
12041 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12042 else if (element == EL_EXPLOSION)
12043 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12045 ExplodeField[x][y] = EX_TYPE_NONE;
12048 game.explosions_delayed = TRUE;
12050 if (game.magic_wall_active)
12052 if (!(game.magic_wall_time_left % 4))
12054 int element = Feld[magic_wall_x][magic_wall_y];
12056 if (element == EL_BD_MAGIC_WALL_FULL ||
12057 element == EL_BD_MAGIC_WALL_ACTIVE ||
12058 element == EL_BD_MAGIC_WALL_EMPTYING)
12059 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12060 else if (element == EL_DC_MAGIC_WALL_FULL ||
12061 element == EL_DC_MAGIC_WALL_ACTIVE ||
12062 element == EL_DC_MAGIC_WALL_EMPTYING)
12063 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12065 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12068 if (game.magic_wall_time_left > 0)
12070 game.magic_wall_time_left--;
12072 if (!game.magic_wall_time_left)
12074 SCAN_PLAYFIELD(x, y)
12076 element = Feld[x][y];
12078 if (element == EL_MAGIC_WALL_ACTIVE ||
12079 element == EL_MAGIC_WALL_FULL)
12081 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12082 TEST_DrawLevelField(x, y);
12084 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12085 element == EL_BD_MAGIC_WALL_FULL)
12087 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12088 TEST_DrawLevelField(x, y);
12090 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12091 element == EL_DC_MAGIC_WALL_FULL)
12093 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12094 TEST_DrawLevelField(x, y);
12098 game.magic_wall_active = FALSE;
12103 if (game.light_time_left > 0)
12105 game.light_time_left--;
12107 if (game.light_time_left == 0)
12108 RedrawAllLightSwitchesAndInvisibleElements();
12111 if (game.timegate_time_left > 0)
12113 game.timegate_time_left--;
12115 if (game.timegate_time_left == 0)
12116 CloseAllOpenTimegates();
12119 if (game.lenses_time_left > 0)
12121 game.lenses_time_left--;
12123 if (game.lenses_time_left == 0)
12124 RedrawAllInvisibleElementsForLenses();
12127 if (game.magnify_time_left > 0)
12129 game.magnify_time_left--;
12131 if (game.magnify_time_left == 0)
12132 RedrawAllInvisibleElementsForMagnifier();
12135 for (i = 0; i < MAX_PLAYERS; i++)
12137 struct PlayerInfo *player = &stored_player[i];
12139 if (SHIELD_ON(player))
12141 if (player->shield_deadly_time_left)
12142 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12143 else if (player->shield_normal_time_left)
12144 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12148 #if USE_DELAYED_GFX_REDRAW
12149 SCAN_PLAYFIELD(x, y)
12151 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12153 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12154 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12156 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12157 DrawLevelField(x, y);
12159 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12160 DrawLevelFieldCrumbled(x, y);
12162 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12163 DrawLevelFieldCrumbledNeighbours(x, y);
12165 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12166 DrawTwinkleOnField(x, y);
12169 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12174 PlayAllPlayersSound();
12176 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12178 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12180 local_player->show_envelope = 0;
12183 /* use random number generator in every frame to make it less predictable */
12184 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12188 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12190 int min_x = x, min_y = y, max_x = x, max_y = y;
12193 for (i = 0; i < MAX_PLAYERS; i++)
12195 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12197 if (!stored_player[i].active || &stored_player[i] == player)
12200 min_x = MIN(min_x, jx);
12201 min_y = MIN(min_y, jy);
12202 max_x = MAX(max_x, jx);
12203 max_y = MAX(max_y, jy);
12206 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12209 static boolean AllPlayersInVisibleScreen()
12213 for (i = 0; i < MAX_PLAYERS; i++)
12215 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12217 if (!stored_player[i].active)
12220 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12227 void ScrollLevel(int dx, int dy)
12229 int scroll_offset = 2 * TILEX_VAR;
12232 BlitBitmap(drawto_field, drawto_field,
12233 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12234 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12235 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12236 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12237 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12238 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12242 x = (dx == 1 ? BX1 : BX2);
12243 for (y = BY1; y <= BY2; y++)
12244 DrawScreenField(x, y);
12249 y = (dy == 1 ? BY1 : BY2);
12250 for (x = BX1; x <= BX2; x++)
12251 DrawScreenField(x, y);
12254 redraw_mask |= REDRAW_FIELD;
12257 static boolean canFallDown(struct PlayerInfo *player)
12259 int jx = player->jx, jy = player->jy;
12261 return (IN_LEV_FIELD(jx, jy + 1) &&
12262 (IS_FREE(jx, jy + 1) ||
12263 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12264 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12265 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12268 static boolean canPassField(int x, int y, int move_dir)
12270 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12271 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12272 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12273 int nextx = x + dx;
12274 int nexty = y + dy;
12275 int element = Feld[x][y];
12277 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12278 !CAN_MOVE(element) &&
12279 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12280 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12281 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12284 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12286 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12287 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12288 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12292 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12293 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12294 (IS_DIGGABLE(Feld[newx][newy]) ||
12295 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12296 canPassField(newx, newy, move_dir)));
12299 static void CheckGravityMovement(struct PlayerInfo *player)
12301 if (player->gravity && !player->programmed_action)
12303 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12304 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12305 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12306 int jx = player->jx, jy = player->jy;
12307 boolean player_is_moving_to_valid_field =
12308 (!player_is_snapping &&
12309 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12310 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12311 boolean player_can_fall_down = canFallDown(player);
12313 if (player_can_fall_down &&
12314 !player_is_moving_to_valid_field)
12315 player->programmed_action = MV_DOWN;
12319 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12321 return CheckGravityMovement(player);
12323 if (player->gravity && !player->programmed_action)
12325 int jx = player->jx, jy = player->jy;
12326 boolean field_under_player_is_free =
12327 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12328 boolean player_is_standing_on_valid_field =
12329 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12330 (IS_WALKABLE(Feld[jx][jy]) &&
12331 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12333 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12334 player->programmed_action = MV_DOWN;
12339 MovePlayerOneStep()
12340 -----------------------------------------------------------------------------
12341 dx, dy: direction (non-diagonal) to try to move the player to
12342 real_dx, real_dy: direction as read from input device (can be diagonal)
12345 boolean MovePlayerOneStep(struct PlayerInfo *player,
12346 int dx, int dy, int real_dx, int real_dy)
12348 int jx = player->jx, jy = player->jy;
12349 int new_jx = jx + dx, new_jy = jy + dy;
12351 boolean player_can_move = !player->cannot_move;
12353 if (!player->active || (!dx && !dy))
12354 return MP_NO_ACTION;
12356 player->MovDir = (dx < 0 ? MV_LEFT :
12357 dx > 0 ? MV_RIGHT :
12359 dy > 0 ? MV_DOWN : MV_NONE);
12361 if (!IN_LEV_FIELD(new_jx, new_jy))
12362 return MP_NO_ACTION;
12364 if (!player_can_move)
12366 if (player->MovPos == 0)
12368 player->is_moving = FALSE;
12369 player->is_digging = FALSE;
12370 player->is_collecting = FALSE;
12371 player->is_snapping = FALSE;
12372 player->is_pushing = FALSE;
12376 if (!network.enabled && game.centered_player_nr == -1 &&
12377 !AllPlayersInSight(player, new_jx, new_jy))
12378 return MP_NO_ACTION;
12380 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12381 if (can_move != MP_MOVING)
12384 /* check if DigField() has caused relocation of the player */
12385 if (player->jx != jx || player->jy != jy)
12386 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12388 StorePlayer[jx][jy] = 0;
12389 player->last_jx = jx;
12390 player->last_jy = jy;
12391 player->jx = new_jx;
12392 player->jy = new_jy;
12393 StorePlayer[new_jx][new_jy] = player->element_nr;
12395 if (player->move_delay_value_next != -1)
12397 player->move_delay_value = player->move_delay_value_next;
12398 player->move_delay_value_next = -1;
12402 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12404 player->step_counter++;
12406 PlayerVisit[jx][jy] = FrameCounter;
12408 player->is_moving = TRUE;
12411 /* should better be called in MovePlayer(), but this breaks some tapes */
12412 ScrollPlayer(player, SCROLL_INIT);
12418 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12420 int jx = player->jx, jy = player->jy;
12421 int old_jx = jx, old_jy = jy;
12422 int moved = MP_NO_ACTION;
12424 if (!player->active)
12429 if (player->MovPos == 0)
12431 player->is_moving = FALSE;
12432 player->is_digging = FALSE;
12433 player->is_collecting = FALSE;
12434 player->is_snapping = FALSE;
12435 player->is_pushing = FALSE;
12441 if (player->move_delay > 0)
12444 player->move_delay = -1; /* set to "uninitialized" value */
12446 /* store if player is automatically moved to next field */
12447 player->is_auto_moving = (player->programmed_action != MV_NONE);
12449 /* remove the last programmed player action */
12450 player->programmed_action = 0;
12452 if (player->MovPos)
12454 /* should only happen if pre-1.2 tape recordings are played */
12455 /* this is only for backward compatibility */
12457 int original_move_delay_value = player->move_delay_value;
12460 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12464 /* scroll remaining steps with finest movement resolution */
12465 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12467 while (player->MovPos)
12469 ScrollPlayer(player, SCROLL_GO_ON);
12470 ScrollScreen(NULL, SCROLL_GO_ON);
12472 AdvanceFrameAndPlayerCounters(player->index_nr);
12475 BackToFront_WithFrameDelay(0);
12478 player->move_delay_value = original_move_delay_value;
12481 player->is_active = FALSE;
12483 if (player->last_move_dir & MV_HORIZONTAL)
12485 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12486 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12490 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12491 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12494 if (!moved && !player->is_active)
12496 player->is_moving = FALSE;
12497 player->is_digging = FALSE;
12498 player->is_collecting = FALSE;
12499 player->is_snapping = FALSE;
12500 player->is_pushing = FALSE;
12506 if (moved & MP_MOVING && !ScreenMovPos &&
12507 (player->index_nr == game.centered_player_nr ||
12508 game.centered_player_nr == -1))
12510 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12511 int offset = game.scroll_delay_value;
12513 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12515 /* actual player has left the screen -- scroll in that direction */
12516 if (jx != old_jx) /* player has moved horizontally */
12517 scroll_x += (jx - old_jx);
12518 else /* player has moved vertically */
12519 scroll_y += (jy - old_jy);
12523 if (jx != old_jx) /* player has moved horizontally */
12525 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12526 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12527 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12529 /* don't scroll over playfield boundaries */
12530 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12531 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12533 /* don't scroll more than one field at a time */
12534 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12536 /* don't scroll against the player's moving direction */
12537 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12538 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12539 scroll_x = old_scroll_x;
12541 else /* player has moved vertically */
12543 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12544 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12545 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12547 /* don't scroll over playfield boundaries */
12548 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12549 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12551 /* don't scroll more than one field at a time */
12552 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12554 /* don't scroll against the player's moving direction */
12555 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12556 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12557 scroll_y = old_scroll_y;
12561 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12563 if (!network.enabled && game.centered_player_nr == -1 &&
12564 !AllPlayersInVisibleScreen())
12566 scroll_x = old_scroll_x;
12567 scroll_y = old_scroll_y;
12571 ScrollScreen(player, SCROLL_INIT);
12572 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12577 player->StepFrame = 0;
12579 if (moved & MP_MOVING)
12581 if (old_jx != jx && old_jy == jy)
12582 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12583 else if (old_jx == jx && old_jy != jy)
12584 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12586 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12588 player->last_move_dir = player->MovDir;
12589 player->is_moving = TRUE;
12590 player->is_snapping = FALSE;
12591 player->is_switching = FALSE;
12592 player->is_dropping = FALSE;
12593 player->is_dropping_pressed = FALSE;
12594 player->drop_pressed_delay = 0;
12597 /* should better be called here than above, but this breaks some tapes */
12598 ScrollPlayer(player, SCROLL_INIT);
12603 CheckGravityMovementWhenNotMoving(player);
12605 player->is_moving = FALSE;
12607 /* at this point, the player is allowed to move, but cannot move right now
12608 (e.g. because of something blocking the way) -- ensure that the player
12609 is also allowed to move in the next frame (in old versions before 3.1.1,
12610 the player was forced to wait again for eight frames before next try) */
12612 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12613 player->move_delay = 0; /* allow direct movement in the next frame */
12616 if (player->move_delay == -1) /* not yet initialized by DigField() */
12617 player->move_delay = player->move_delay_value;
12619 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12621 TestIfPlayerTouchesBadThing(jx, jy);
12622 TestIfPlayerTouchesCustomElement(jx, jy);
12625 if (!player->active)
12626 RemovePlayer(player);
12631 void ScrollPlayer(struct PlayerInfo *player, int mode)
12633 int jx = player->jx, jy = player->jy;
12634 int last_jx = player->last_jx, last_jy = player->last_jy;
12635 int move_stepsize = TILEX / player->move_delay_value;
12637 if (!player->active)
12640 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12643 if (mode == SCROLL_INIT)
12645 player->actual_frame_counter = FrameCounter;
12646 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12648 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12649 Feld[last_jx][last_jy] == EL_EMPTY)
12651 int last_field_block_delay = 0; /* start with no blocking at all */
12652 int block_delay_adjustment = player->block_delay_adjustment;
12654 /* if player blocks last field, add delay for exactly one move */
12655 if (player->block_last_field)
12657 last_field_block_delay += player->move_delay_value;
12659 /* when blocking enabled, prevent moving up despite gravity */
12660 if (player->gravity && player->MovDir == MV_UP)
12661 block_delay_adjustment = -1;
12664 /* add block delay adjustment (also possible when not blocking) */
12665 last_field_block_delay += block_delay_adjustment;
12667 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12668 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12671 if (player->MovPos != 0) /* player has not yet reached destination */
12674 else if (!FrameReached(&player->actual_frame_counter, 1))
12677 if (player->MovPos != 0)
12679 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12680 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12682 /* before DrawPlayer() to draw correct player graphic for this case */
12683 if (player->MovPos == 0)
12684 CheckGravityMovement(player);
12687 if (player->MovPos == 0) /* player reached destination field */
12689 if (player->move_delay_reset_counter > 0)
12691 player->move_delay_reset_counter--;
12693 if (player->move_delay_reset_counter == 0)
12695 /* continue with normal speed after quickly moving through gate */
12696 HALVE_PLAYER_SPEED(player);
12698 /* be able to make the next move without delay */
12699 player->move_delay = 0;
12703 player->last_jx = jx;
12704 player->last_jy = jy;
12706 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12707 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12708 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12709 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12710 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12711 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12712 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12713 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12715 DrawPlayer(player); /* needed here only to cleanup last field */
12716 RemovePlayer(player);
12718 if (local_player->friends_still_needed == 0 ||
12719 IS_SP_ELEMENT(Feld[jx][jy]))
12720 PlayerWins(player);
12723 /* this breaks one level: "machine", level 000 */
12725 int move_direction = player->MovDir;
12726 int enter_side = MV_DIR_OPPOSITE(move_direction);
12727 int leave_side = move_direction;
12728 int old_jx = last_jx;
12729 int old_jy = last_jy;
12730 int old_element = Feld[old_jx][old_jy];
12731 int new_element = Feld[jx][jy];
12733 if (IS_CUSTOM_ELEMENT(old_element))
12734 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12736 player->index_bit, leave_side);
12738 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12739 CE_PLAYER_LEAVES_X,
12740 player->index_bit, leave_side);
12742 if (IS_CUSTOM_ELEMENT(new_element))
12743 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12744 player->index_bit, enter_side);
12746 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12747 CE_PLAYER_ENTERS_X,
12748 player->index_bit, enter_side);
12750 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12751 CE_MOVE_OF_X, move_direction);
12754 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12756 TestIfPlayerTouchesBadThing(jx, jy);
12757 TestIfPlayerTouchesCustomElement(jx, jy);
12759 /* needed because pushed element has not yet reached its destination,
12760 so it would trigger a change event at its previous field location */
12761 if (!player->is_pushing)
12762 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12764 if (!player->active)
12765 RemovePlayer(player);
12768 if (!local_player->LevelSolved && level.use_step_counter)
12778 if (TimeLeft <= 10 && setup.time_limit)
12779 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12781 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12783 DisplayGameControlValues();
12785 if (!TimeLeft && setup.time_limit)
12786 for (i = 0; i < MAX_PLAYERS; i++)
12787 KillPlayer(&stored_player[i]);
12789 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12791 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12793 DisplayGameControlValues();
12797 if (tape.single_step && tape.recording && !tape.pausing &&
12798 !player->programmed_action)
12799 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12801 if (!player->programmed_action)
12802 CheckSaveEngineSnapshot(player);
12806 void ScrollScreen(struct PlayerInfo *player, int mode)
12808 static unsigned int screen_frame_counter = 0;
12810 if (mode == SCROLL_INIT)
12812 /* set scrolling step size according to actual player's moving speed */
12813 ScrollStepSize = TILEX / player->move_delay_value;
12815 screen_frame_counter = FrameCounter;
12816 ScreenMovDir = player->MovDir;
12817 ScreenMovPos = player->MovPos;
12818 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12821 else if (!FrameReached(&screen_frame_counter, 1))
12826 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12827 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12828 redraw_mask |= REDRAW_FIELD;
12831 ScreenMovDir = MV_NONE;
12834 void TestIfPlayerTouchesCustomElement(int x, int y)
12836 static int xy[4][2] =
12843 static int trigger_sides[4][2] =
12845 /* center side border side */
12846 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12847 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12848 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12849 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12851 static int touch_dir[4] =
12853 MV_LEFT | MV_RIGHT,
12858 int center_element = Feld[x][y]; /* should always be non-moving! */
12861 for (i = 0; i < NUM_DIRECTIONS; i++)
12863 int xx = x + xy[i][0];
12864 int yy = y + xy[i][1];
12865 int center_side = trigger_sides[i][0];
12866 int border_side = trigger_sides[i][1];
12867 int border_element;
12869 if (!IN_LEV_FIELD(xx, yy))
12872 if (IS_PLAYER(x, y)) /* player found at center element */
12874 struct PlayerInfo *player = PLAYERINFO(x, y);
12876 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12877 border_element = Feld[xx][yy]; /* may be moving! */
12878 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12879 border_element = Feld[xx][yy];
12880 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12881 border_element = MovingOrBlocked2Element(xx, yy);
12883 continue; /* center and border element do not touch */
12885 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12886 player->index_bit, border_side);
12887 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12888 CE_PLAYER_TOUCHES_X,
12889 player->index_bit, border_side);
12892 /* use player element that is initially defined in the level playfield,
12893 not the player element that corresponds to the runtime player number
12894 (example: a level that contains EL_PLAYER_3 as the only player would
12895 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12896 int player_element = PLAYERINFO(x, y)->initial_element;
12898 CheckElementChangeBySide(xx, yy, border_element, player_element,
12899 CE_TOUCHING_X, border_side);
12902 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12904 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12906 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12908 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12909 continue; /* center and border element do not touch */
12912 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12913 player->index_bit, center_side);
12914 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12915 CE_PLAYER_TOUCHES_X,
12916 player->index_bit, center_side);
12919 /* use player element that is initially defined in the level playfield,
12920 not the player element that corresponds to the runtime player number
12921 (example: a level that contains EL_PLAYER_3 as the only player would
12922 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12923 int player_element = PLAYERINFO(xx, yy)->initial_element;
12925 CheckElementChangeBySide(x, y, center_element, player_element,
12926 CE_TOUCHING_X, center_side);
12934 void TestIfElementTouchesCustomElement(int x, int y)
12936 static int xy[4][2] =
12943 static int trigger_sides[4][2] =
12945 /* center side border side */
12946 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12947 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12948 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12949 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12951 static int touch_dir[4] =
12953 MV_LEFT | MV_RIGHT,
12958 boolean change_center_element = FALSE;
12959 int center_element = Feld[x][y]; /* should always be non-moving! */
12960 int border_element_old[NUM_DIRECTIONS];
12963 for (i = 0; i < NUM_DIRECTIONS; i++)
12965 int xx = x + xy[i][0];
12966 int yy = y + xy[i][1];
12967 int border_element;
12969 border_element_old[i] = -1;
12971 if (!IN_LEV_FIELD(xx, yy))
12974 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12975 border_element = Feld[xx][yy]; /* may be moving! */
12976 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12977 border_element = Feld[xx][yy];
12978 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12979 border_element = MovingOrBlocked2Element(xx, yy);
12981 continue; /* center and border element do not touch */
12983 border_element_old[i] = border_element;
12986 for (i = 0; i < NUM_DIRECTIONS; i++)
12988 int xx = x + xy[i][0];
12989 int yy = y + xy[i][1];
12990 int center_side = trigger_sides[i][0];
12991 int border_element = border_element_old[i];
12993 if (border_element == -1)
12996 /* check for change of border element */
12997 CheckElementChangeBySide(xx, yy, border_element, center_element,
12998 CE_TOUCHING_X, center_side);
13000 /* (center element cannot be player, so we dont have to check this here) */
13003 for (i = 0; i < NUM_DIRECTIONS; i++)
13005 int xx = x + xy[i][0];
13006 int yy = y + xy[i][1];
13007 int border_side = trigger_sides[i][1];
13008 int border_element = border_element_old[i];
13010 if (border_element == -1)
13013 /* check for change of center element (but change it only once) */
13014 if (!change_center_element)
13015 change_center_element =
13016 CheckElementChangeBySide(x, y, center_element, border_element,
13017 CE_TOUCHING_X, border_side);
13019 if (IS_PLAYER(xx, yy))
13021 /* use player element that is initially defined in the level playfield,
13022 not the player element that corresponds to the runtime player number
13023 (example: a level that contains EL_PLAYER_3 as the only player would
13024 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13025 int player_element = PLAYERINFO(xx, yy)->initial_element;
13027 CheckElementChangeBySide(x, y, center_element, player_element,
13028 CE_TOUCHING_X, border_side);
13033 void TestIfElementHitsCustomElement(int x, int y, int direction)
13035 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13036 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13037 int hitx = x + dx, hity = y + dy;
13038 int hitting_element = Feld[x][y];
13039 int touched_element;
13041 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13044 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13045 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13047 if (IN_LEV_FIELD(hitx, hity))
13049 int opposite_direction = MV_DIR_OPPOSITE(direction);
13050 int hitting_side = direction;
13051 int touched_side = opposite_direction;
13052 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13053 MovDir[hitx][hity] != direction ||
13054 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13060 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13061 CE_HITTING_X, touched_side);
13063 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13064 CE_HIT_BY_X, hitting_side);
13066 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13067 CE_HIT_BY_SOMETHING, opposite_direction);
13069 if (IS_PLAYER(hitx, hity))
13071 /* use player element that is initially defined in the level playfield,
13072 not the player element that corresponds to the runtime player number
13073 (example: a level that contains EL_PLAYER_3 as the only player would
13074 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13075 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13077 CheckElementChangeBySide(x, y, hitting_element, player_element,
13078 CE_HITTING_X, touched_side);
13083 /* "hitting something" is also true when hitting the playfield border */
13084 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13085 CE_HITTING_SOMETHING, direction);
13088 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13090 int i, kill_x = -1, kill_y = -1;
13092 int bad_element = -1;
13093 static int test_xy[4][2] =
13100 static int test_dir[4] =
13108 for (i = 0; i < NUM_DIRECTIONS; i++)
13110 int test_x, test_y, test_move_dir, test_element;
13112 test_x = good_x + test_xy[i][0];
13113 test_y = good_y + test_xy[i][1];
13115 if (!IN_LEV_FIELD(test_x, test_y))
13119 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13121 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13123 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13124 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13126 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13127 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13131 bad_element = test_element;
13137 if (kill_x != -1 || kill_y != -1)
13139 if (IS_PLAYER(good_x, good_y))
13141 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13143 if (player->shield_deadly_time_left > 0 &&
13144 !IS_INDESTRUCTIBLE(bad_element))
13145 Bang(kill_x, kill_y);
13146 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13147 KillPlayer(player);
13150 Bang(good_x, good_y);
13154 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13156 int i, kill_x = -1, kill_y = -1;
13157 int bad_element = Feld[bad_x][bad_y];
13158 static int test_xy[4][2] =
13165 static int touch_dir[4] =
13167 MV_LEFT | MV_RIGHT,
13172 static int test_dir[4] =
13180 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13183 for (i = 0; i < NUM_DIRECTIONS; i++)
13185 int test_x, test_y, test_move_dir, test_element;
13187 test_x = bad_x + test_xy[i][0];
13188 test_y = bad_y + test_xy[i][1];
13190 if (!IN_LEV_FIELD(test_x, test_y))
13194 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13196 test_element = Feld[test_x][test_y];
13198 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13199 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13201 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13202 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13204 /* good thing is player or penguin that does not move away */
13205 if (IS_PLAYER(test_x, test_y))
13207 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13209 if (bad_element == EL_ROBOT && player->is_moving)
13210 continue; /* robot does not kill player if he is moving */
13212 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13214 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13215 continue; /* center and border element do not touch */
13223 else if (test_element == EL_PENGUIN)
13233 if (kill_x != -1 || kill_y != -1)
13235 if (IS_PLAYER(kill_x, kill_y))
13237 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13239 if (player->shield_deadly_time_left > 0 &&
13240 !IS_INDESTRUCTIBLE(bad_element))
13241 Bang(bad_x, bad_y);
13242 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13243 KillPlayer(player);
13246 Bang(kill_x, kill_y);
13250 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13252 int bad_element = Feld[bad_x][bad_y];
13253 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13254 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13255 int test_x = bad_x + dx, test_y = bad_y + dy;
13256 int test_move_dir, test_element;
13257 int kill_x = -1, kill_y = -1;
13259 if (!IN_LEV_FIELD(test_x, test_y))
13263 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13265 test_element = Feld[test_x][test_y];
13267 if (test_move_dir != bad_move_dir)
13269 /* good thing can be player or penguin that does not move away */
13270 if (IS_PLAYER(test_x, test_y))
13272 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13274 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13275 player as being hit when he is moving towards the bad thing, because
13276 the "get hit by" condition would be lost after the player stops) */
13277 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13278 return; /* player moves away from bad thing */
13283 else if (test_element == EL_PENGUIN)
13290 if (kill_x != -1 || kill_y != -1)
13292 if (IS_PLAYER(kill_x, kill_y))
13294 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13296 if (player->shield_deadly_time_left > 0 &&
13297 !IS_INDESTRUCTIBLE(bad_element))
13298 Bang(bad_x, bad_y);
13299 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13300 KillPlayer(player);
13303 Bang(kill_x, kill_y);
13307 void TestIfPlayerTouchesBadThing(int x, int y)
13309 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13312 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13314 TestIfGoodThingHitsBadThing(x, y, move_dir);
13317 void TestIfBadThingTouchesPlayer(int x, int y)
13319 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13322 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13324 TestIfBadThingHitsGoodThing(x, y, move_dir);
13327 void TestIfFriendTouchesBadThing(int x, int y)
13329 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13332 void TestIfBadThingTouchesFriend(int x, int y)
13334 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13337 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13339 int i, kill_x = bad_x, kill_y = bad_y;
13340 static int xy[4][2] =
13348 for (i = 0; i < NUM_DIRECTIONS; i++)
13352 x = bad_x + xy[i][0];
13353 y = bad_y + xy[i][1];
13354 if (!IN_LEV_FIELD(x, y))
13357 element = Feld[x][y];
13358 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13359 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13367 if (kill_x != bad_x || kill_y != bad_y)
13368 Bang(bad_x, bad_y);
13371 void KillPlayer(struct PlayerInfo *player)
13373 int jx = player->jx, jy = player->jy;
13375 if (!player->active)
13379 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13380 player->killed, player->active, player->reanimated);
13383 /* the following code was introduced to prevent an infinite loop when calling
13385 -> CheckTriggeredElementChangeExt()
13386 -> ExecuteCustomElementAction()
13388 -> (infinitely repeating the above sequence of function calls)
13389 which occurs when killing the player while having a CE with the setting
13390 "kill player X when explosion of <player X>"; the solution using a new
13391 field "player->killed" was chosen for backwards compatibility, although
13392 clever use of the fields "player->active" etc. would probably also work */
13394 if (player->killed)
13398 player->killed = TRUE;
13400 /* remove accessible field at the player's position */
13401 Feld[jx][jy] = EL_EMPTY;
13403 /* deactivate shield (else Bang()/Explode() would not work right) */
13404 player->shield_normal_time_left = 0;
13405 player->shield_deadly_time_left = 0;
13408 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13409 player->killed, player->active, player->reanimated);
13415 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13416 player->killed, player->active, player->reanimated);
13419 if (player->reanimated) /* killed player may have been reanimated */
13420 player->killed = player->reanimated = FALSE;
13422 BuryPlayer(player);
13425 static void KillPlayerUnlessEnemyProtected(int x, int y)
13427 if (!PLAYER_ENEMY_PROTECTED(x, y))
13428 KillPlayer(PLAYERINFO(x, y));
13431 static void KillPlayerUnlessExplosionProtected(int x, int y)
13433 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13434 KillPlayer(PLAYERINFO(x, y));
13437 void BuryPlayer(struct PlayerInfo *player)
13439 int jx = player->jx, jy = player->jy;
13441 if (!player->active)
13444 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13445 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13447 player->GameOver = TRUE;
13448 RemovePlayer(player);
13451 void RemovePlayer(struct PlayerInfo *player)
13453 int jx = player->jx, jy = player->jy;
13454 int i, found = FALSE;
13456 player->present = FALSE;
13457 player->active = FALSE;
13459 if (!ExplodeField[jx][jy])
13460 StorePlayer[jx][jy] = 0;
13462 if (player->is_moving)
13463 TEST_DrawLevelField(player->last_jx, player->last_jy);
13465 for (i = 0; i < MAX_PLAYERS; i++)
13466 if (stored_player[i].active)
13470 AllPlayersGone = TRUE;
13476 static void setFieldForSnapping(int x, int y, int element, int direction)
13478 struct ElementInfo *ei = &element_info[element];
13479 int direction_bit = MV_DIR_TO_BIT(direction);
13480 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13481 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13482 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13484 Feld[x][y] = EL_ELEMENT_SNAPPING;
13485 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13487 ResetGfxAnimation(x, y);
13489 GfxElement[x][y] = element;
13490 GfxAction[x][y] = action;
13491 GfxDir[x][y] = direction;
13492 GfxFrame[x][y] = -1;
13496 =============================================================================
13497 checkDiagonalPushing()
13498 -----------------------------------------------------------------------------
13499 check if diagonal input device direction results in pushing of object
13500 (by checking if the alternative direction is walkable, diggable, ...)
13501 =============================================================================
13504 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13505 int x, int y, int real_dx, int real_dy)
13507 int jx, jy, dx, dy, xx, yy;
13509 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13512 /* diagonal direction: check alternative direction */
13517 xx = jx + (dx == 0 ? real_dx : 0);
13518 yy = jy + (dy == 0 ? real_dy : 0);
13520 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13524 =============================================================================
13526 -----------------------------------------------------------------------------
13527 x, y: field next to player (non-diagonal) to try to dig to
13528 real_dx, real_dy: direction as read from input device (can be diagonal)
13529 =============================================================================
13532 static int DigField(struct PlayerInfo *player,
13533 int oldx, int oldy, int x, int y,
13534 int real_dx, int real_dy, int mode)
13536 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13537 boolean player_was_pushing = player->is_pushing;
13538 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13539 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13540 int jx = oldx, jy = oldy;
13541 int dx = x - jx, dy = y - jy;
13542 int nextx = x + dx, nexty = y + dy;
13543 int move_direction = (dx == -1 ? MV_LEFT :
13544 dx == +1 ? MV_RIGHT :
13546 dy == +1 ? MV_DOWN : MV_NONE);
13547 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13548 int dig_side = MV_DIR_OPPOSITE(move_direction);
13549 int old_element = Feld[jx][jy];
13550 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13553 if (is_player) /* function can also be called by EL_PENGUIN */
13555 if (player->MovPos == 0)
13557 player->is_digging = FALSE;
13558 player->is_collecting = FALSE;
13561 if (player->MovPos == 0) /* last pushing move finished */
13562 player->is_pushing = FALSE;
13564 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13566 player->is_switching = FALSE;
13567 player->push_delay = -1;
13569 return MP_NO_ACTION;
13573 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13574 old_element = Back[jx][jy];
13576 /* in case of element dropped at player position, check background */
13577 else if (Back[jx][jy] != EL_EMPTY &&
13578 game.engine_version >= VERSION_IDENT(2,2,0,0))
13579 old_element = Back[jx][jy];
13581 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13582 return MP_NO_ACTION; /* field has no opening in this direction */
13584 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13585 return MP_NO_ACTION; /* field has no opening in this direction */
13587 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13591 Feld[jx][jy] = player->artwork_element;
13592 InitMovingField(jx, jy, MV_DOWN);
13593 Store[jx][jy] = EL_ACID;
13594 ContinueMoving(jx, jy);
13595 BuryPlayer(player);
13597 return MP_DONT_RUN_INTO;
13600 if (player_can_move && DONT_RUN_INTO(element))
13602 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13604 return MP_DONT_RUN_INTO;
13607 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13608 return MP_NO_ACTION;
13610 collect_count = element_info[element].collect_count_initial;
13612 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13613 return MP_NO_ACTION;
13615 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13616 player_can_move = player_can_move_or_snap;
13618 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13619 game.engine_version >= VERSION_IDENT(2,2,0,0))
13621 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13622 player->index_bit, dig_side);
13623 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13624 player->index_bit, dig_side);
13626 if (element == EL_DC_LANDMINE)
13629 if (Feld[x][y] != element) /* field changed by snapping */
13632 return MP_NO_ACTION;
13635 if (player->gravity && is_player && !player->is_auto_moving &&
13636 canFallDown(player) && move_direction != MV_DOWN &&
13637 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13638 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13640 if (player_can_move &&
13641 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13643 int sound_element = SND_ELEMENT(element);
13644 int sound_action = ACTION_WALKING;
13646 if (IS_RND_GATE(element))
13648 if (!player->key[RND_GATE_NR(element)])
13649 return MP_NO_ACTION;
13651 else if (IS_RND_GATE_GRAY(element))
13653 if (!player->key[RND_GATE_GRAY_NR(element)])
13654 return MP_NO_ACTION;
13656 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13658 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13659 return MP_NO_ACTION;
13661 else if (element == EL_EXIT_OPEN ||
13662 element == EL_EM_EXIT_OPEN ||
13663 element == EL_EM_EXIT_OPENING ||
13664 element == EL_STEEL_EXIT_OPEN ||
13665 element == EL_EM_STEEL_EXIT_OPEN ||
13666 element == EL_EM_STEEL_EXIT_OPENING ||
13667 element == EL_SP_EXIT_OPEN ||
13668 element == EL_SP_EXIT_OPENING)
13670 sound_action = ACTION_PASSING; /* player is passing exit */
13672 else if (element == EL_EMPTY)
13674 sound_action = ACTION_MOVING; /* nothing to walk on */
13677 /* play sound from background or player, whatever is available */
13678 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13679 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13681 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13683 else if (player_can_move &&
13684 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13686 if (!ACCESS_FROM(element, opposite_direction))
13687 return MP_NO_ACTION; /* field not accessible from this direction */
13689 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13690 return MP_NO_ACTION;
13692 if (IS_EM_GATE(element))
13694 if (!player->key[EM_GATE_NR(element)])
13695 return MP_NO_ACTION;
13697 else if (IS_EM_GATE_GRAY(element))
13699 if (!player->key[EM_GATE_GRAY_NR(element)])
13700 return MP_NO_ACTION;
13702 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13704 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13705 return MP_NO_ACTION;
13707 else if (IS_EMC_GATE(element))
13709 if (!player->key[EMC_GATE_NR(element)])
13710 return MP_NO_ACTION;
13712 else if (IS_EMC_GATE_GRAY(element))
13714 if (!player->key[EMC_GATE_GRAY_NR(element)])
13715 return MP_NO_ACTION;
13717 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13719 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13720 return MP_NO_ACTION;
13722 else if (element == EL_DC_GATE_WHITE ||
13723 element == EL_DC_GATE_WHITE_GRAY ||
13724 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13726 if (player->num_white_keys == 0)
13727 return MP_NO_ACTION;
13729 player->num_white_keys--;
13731 else if (IS_SP_PORT(element))
13733 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13734 element == EL_SP_GRAVITY_PORT_RIGHT ||
13735 element == EL_SP_GRAVITY_PORT_UP ||
13736 element == EL_SP_GRAVITY_PORT_DOWN)
13737 player->gravity = !player->gravity;
13738 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13739 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13740 element == EL_SP_GRAVITY_ON_PORT_UP ||
13741 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13742 player->gravity = TRUE;
13743 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13744 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13745 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13746 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13747 player->gravity = FALSE;
13750 /* automatically move to the next field with double speed */
13751 player->programmed_action = move_direction;
13753 if (player->move_delay_reset_counter == 0)
13755 player->move_delay_reset_counter = 2; /* two double speed steps */
13757 DOUBLE_PLAYER_SPEED(player);
13760 PlayLevelSoundAction(x, y, ACTION_PASSING);
13762 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13766 if (mode != DF_SNAP)
13768 GfxElement[x][y] = GFX_ELEMENT(element);
13769 player->is_digging = TRUE;
13772 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13774 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13775 player->index_bit, dig_side);
13777 if (mode == DF_SNAP)
13779 if (level.block_snap_field)
13780 setFieldForSnapping(x, y, element, move_direction);
13782 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13784 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13785 player->index_bit, dig_side);
13788 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13792 if (is_player && mode != DF_SNAP)
13794 GfxElement[x][y] = element;
13795 player->is_collecting = TRUE;
13798 if (element == EL_SPEED_PILL)
13800 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13802 else if (element == EL_EXTRA_TIME && level.time > 0)
13804 TimeLeft += level.extra_time;
13806 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13808 DisplayGameControlValues();
13810 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13812 player->shield_normal_time_left += level.shield_normal_time;
13813 if (element == EL_SHIELD_DEADLY)
13814 player->shield_deadly_time_left += level.shield_deadly_time;
13816 else if (element == EL_DYNAMITE ||
13817 element == EL_EM_DYNAMITE ||
13818 element == EL_SP_DISK_RED)
13820 if (player->inventory_size < MAX_INVENTORY_SIZE)
13821 player->inventory_element[player->inventory_size++] = element;
13823 DrawGameDoorValues();
13825 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13827 player->dynabomb_count++;
13828 player->dynabombs_left++;
13830 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13832 player->dynabomb_size++;
13834 else if (element == EL_DYNABOMB_INCREASE_POWER)
13836 player->dynabomb_xl = TRUE;
13838 else if (IS_KEY(element))
13840 player->key[KEY_NR(element)] = TRUE;
13842 DrawGameDoorValues();
13844 else if (element == EL_DC_KEY_WHITE)
13846 player->num_white_keys++;
13848 /* display white keys? */
13849 /* DrawGameDoorValues(); */
13851 else if (IS_ENVELOPE(element))
13853 player->show_envelope = element;
13855 else if (element == EL_EMC_LENSES)
13857 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13859 RedrawAllInvisibleElementsForLenses();
13861 else if (element == EL_EMC_MAGNIFIER)
13863 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13865 RedrawAllInvisibleElementsForMagnifier();
13867 else if (IS_DROPPABLE(element) ||
13868 IS_THROWABLE(element)) /* can be collected and dropped */
13872 if (collect_count == 0)
13873 player->inventory_infinite_element = element;
13875 for (i = 0; i < collect_count; i++)
13876 if (player->inventory_size < MAX_INVENTORY_SIZE)
13877 player->inventory_element[player->inventory_size++] = element;
13879 DrawGameDoorValues();
13881 else if (collect_count > 0)
13883 local_player->gems_still_needed -= collect_count;
13884 if (local_player->gems_still_needed < 0)
13885 local_player->gems_still_needed = 0;
13887 game.snapshot.collected_item = TRUE;
13889 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13891 DisplayGameControlValues();
13894 RaiseScoreElement(element);
13895 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13898 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13899 player->index_bit, dig_side);
13901 if (mode == DF_SNAP)
13903 if (level.block_snap_field)
13904 setFieldForSnapping(x, y, element, move_direction);
13906 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13908 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13909 player->index_bit, dig_side);
13912 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13914 if (mode == DF_SNAP && element != EL_BD_ROCK)
13915 return MP_NO_ACTION;
13917 if (CAN_FALL(element) && dy)
13918 return MP_NO_ACTION;
13920 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13921 !(element == EL_SPRING && level.use_spring_bug))
13922 return MP_NO_ACTION;
13924 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13925 ((move_direction & MV_VERTICAL &&
13926 ((element_info[element].move_pattern & MV_LEFT &&
13927 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13928 (element_info[element].move_pattern & MV_RIGHT &&
13929 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13930 (move_direction & MV_HORIZONTAL &&
13931 ((element_info[element].move_pattern & MV_UP &&
13932 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13933 (element_info[element].move_pattern & MV_DOWN &&
13934 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13935 return MP_NO_ACTION;
13937 /* do not push elements already moving away faster than player */
13938 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13939 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13940 return MP_NO_ACTION;
13942 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13944 if (player->push_delay_value == -1 || !player_was_pushing)
13945 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13947 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13949 if (player->push_delay_value == -1)
13950 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13952 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13954 if (!player->is_pushing)
13955 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13958 player->is_pushing = TRUE;
13959 player->is_active = TRUE;
13961 if (!(IN_LEV_FIELD(nextx, nexty) &&
13962 (IS_FREE(nextx, nexty) ||
13963 (IS_SB_ELEMENT(element) &&
13964 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13965 (IS_CUSTOM_ELEMENT(element) &&
13966 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13967 return MP_NO_ACTION;
13969 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13970 return MP_NO_ACTION;
13972 if (player->push_delay == -1) /* new pushing; restart delay */
13973 player->push_delay = 0;
13975 if (player->push_delay < player->push_delay_value &&
13976 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13977 element != EL_SPRING && element != EL_BALLOON)
13979 /* make sure that there is no move delay before next try to push */
13980 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13981 player->move_delay = 0;
13983 return MP_NO_ACTION;
13986 if (IS_CUSTOM_ELEMENT(element) &&
13987 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13989 if (!DigFieldByCE(nextx, nexty, element))
13990 return MP_NO_ACTION;
13993 if (IS_SB_ELEMENT(element))
13995 if (element == EL_SOKOBAN_FIELD_FULL)
13997 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13998 local_player->sokobanfields_still_needed++;
14001 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14003 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14004 local_player->sokobanfields_still_needed--;
14007 Feld[x][y] = EL_SOKOBAN_OBJECT;
14009 if (Back[x][y] == Back[nextx][nexty])
14010 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14011 else if (Back[x][y] != 0)
14012 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14015 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14018 if (local_player->sokobanfields_still_needed == 0 &&
14019 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14021 PlayerWins(player);
14023 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14027 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14029 InitMovingField(x, y, move_direction);
14030 GfxAction[x][y] = ACTION_PUSHING;
14032 if (mode == DF_SNAP)
14033 ContinueMoving(x, y);
14035 MovPos[x][y] = (dx != 0 ? dx : dy);
14037 Pushed[x][y] = TRUE;
14038 Pushed[nextx][nexty] = TRUE;
14040 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14041 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14043 player->push_delay_value = -1; /* get new value later */
14045 /* check for element change _after_ element has been pushed */
14046 if (game.use_change_when_pushing_bug)
14048 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14049 player->index_bit, dig_side);
14050 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14051 player->index_bit, dig_side);
14054 else if (IS_SWITCHABLE(element))
14056 if (PLAYER_SWITCHING(player, x, y))
14058 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14059 player->index_bit, dig_side);
14064 player->is_switching = TRUE;
14065 player->switch_x = x;
14066 player->switch_y = y;
14068 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14070 if (element == EL_ROBOT_WHEEL)
14072 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14076 game.robot_wheel_active = TRUE;
14078 TEST_DrawLevelField(x, y);
14080 else if (element == EL_SP_TERMINAL)
14084 SCAN_PLAYFIELD(xx, yy)
14086 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14090 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14092 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14094 ResetGfxAnimation(xx, yy);
14095 TEST_DrawLevelField(xx, yy);
14099 else if (IS_BELT_SWITCH(element))
14101 ToggleBeltSwitch(x, y);
14103 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14104 element == EL_SWITCHGATE_SWITCH_DOWN ||
14105 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14106 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14108 ToggleSwitchgateSwitch(x, y);
14110 else if (element == EL_LIGHT_SWITCH ||
14111 element == EL_LIGHT_SWITCH_ACTIVE)
14113 ToggleLightSwitch(x, y);
14115 else if (element == EL_TIMEGATE_SWITCH ||
14116 element == EL_DC_TIMEGATE_SWITCH)
14118 ActivateTimegateSwitch(x, y);
14120 else if (element == EL_BALLOON_SWITCH_LEFT ||
14121 element == EL_BALLOON_SWITCH_RIGHT ||
14122 element == EL_BALLOON_SWITCH_UP ||
14123 element == EL_BALLOON_SWITCH_DOWN ||
14124 element == EL_BALLOON_SWITCH_NONE ||
14125 element == EL_BALLOON_SWITCH_ANY)
14127 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14128 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14129 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14130 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14131 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14134 else if (element == EL_LAMP)
14136 Feld[x][y] = EL_LAMP_ACTIVE;
14137 local_player->lights_still_needed--;
14139 ResetGfxAnimation(x, y);
14140 TEST_DrawLevelField(x, y);
14142 else if (element == EL_TIME_ORB_FULL)
14144 Feld[x][y] = EL_TIME_ORB_EMPTY;
14146 if (level.time > 0 || level.use_time_orb_bug)
14148 TimeLeft += level.time_orb_time;
14149 game.no_time_limit = FALSE;
14151 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14153 DisplayGameControlValues();
14156 ResetGfxAnimation(x, y);
14157 TEST_DrawLevelField(x, y);
14159 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14160 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14164 game.ball_state = !game.ball_state;
14166 SCAN_PLAYFIELD(xx, yy)
14168 int e = Feld[xx][yy];
14170 if (game.ball_state)
14172 if (e == EL_EMC_MAGIC_BALL)
14173 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14174 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14175 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14179 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14180 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14181 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14182 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14187 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14188 player->index_bit, dig_side);
14190 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14191 player->index_bit, dig_side);
14193 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14194 player->index_bit, dig_side);
14200 if (!PLAYER_SWITCHING(player, x, y))
14202 player->is_switching = TRUE;
14203 player->switch_x = x;
14204 player->switch_y = y;
14206 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14207 player->index_bit, dig_side);
14208 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14209 player->index_bit, dig_side);
14211 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14212 player->index_bit, dig_side);
14213 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14214 player->index_bit, dig_side);
14217 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14218 player->index_bit, dig_side);
14219 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14220 player->index_bit, dig_side);
14222 return MP_NO_ACTION;
14225 player->push_delay = -1;
14227 if (is_player) /* function can also be called by EL_PENGUIN */
14229 if (Feld[x][y] != element) /* really digged/collected something */
14231 player->is_collecting = !player->is_digging;
14232 player->is_active = TRUE;
14239 static boolean DigFieldByCE(int x, int y, int digging_element)
14241 int element = Feld[x][y];
14243 if (!IS_FREE(x, y))
14245 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14246 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14249 /* no element can dig solid indestructible elements */
14250 if (IS_INDESTRUCTIBLE(element) &&
14251 !IS_DIGGABLE(element) &&
14252 !IS_COLLECTIBLE(element))
14255 if (AmoebaNr[x][y] &&
14256 (element == EL_AMOEBA_FULL ||
14257 element == EL_BD_AMOEBA ||
14258 element == EL_AMOEBA_GROWING))
14260 AmoebaCnt[AmoebaNr[x][y]]--;
14261 AmoebaCnt2[AmoebaNr[x][y]]--;
14264 if (IS_MOVING(x, y))
14265 RemoveMovingField(x, y);
14269 TEST_DrawLevelField(x, y);
14272 /* if digged element was about to explode, prevent the explosion */
14273 ExplodeField[x][y] = EX_TYPE_NONE;
14275 PlayLevelSoundAction(x, y, action);
14278 Store[x][y] = EL_EMPTY;
14280 /* this makes it possible to leave the removed element again */
14281 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14282 Store[x][y] = element;
14287 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14289 int jx = player->jx, jy = player->jy;
14290 int x = jx + dx, y = jy + dy;
14291 int snap_direction = (dx == -1 ? MV_LEFT :
14292 dx == +1 ? MV_RIGHT :
14294 dy == +1 ? MV_DOWN : MV_NONE);
14295 boolean can_continue_snapping = (level.continuous_snapping &&
14296 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14298 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14301 if (!player->active || !IN_LEV_FIELD(x, y))
14309 if (player->MovPos == 0)
14310 player->is_pushing = FALSE;
14312 player->is_snapping = FALSE;
14314 if (player->MovPos == 0)
14316 player->is_moving = FALSE;
14317 player->is_digging = FALSE;
14318 player->is_collecting = FALSE;
14324 /* prevent snapping with already pressed snap key when not allowed */
14325 if (player->is_snapping && !can_continue_snapping)
14328 player->MovDir = snap_direction;
14330 if (player->MovPos == 0)
14332 player->is_moving = FALSE;
14333 player->is_digging = FALSE;
14334 player->is_collecting = FALSE;
14337 player->is_dropping = FALSE;
14338 player->is_dropping_pressed = FALSE;
14339 player->drop_pressed_delay = 0;
14341 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14344 player->is_snapping = TRUE;
14345 player->is_active = TRUE;
14347 if (player->MovPos == 0)
14349 player->is_moving = FALSE;
14350 player->is_digging = FALSE;
14351 player->is_collecting = FALSE;
14354 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14355 TEST_DrawLevelField(player->last_jx, player->last_jy);
14357 TEST_DrawLevelField(x, y);
14362 static boolean DropElement(struct PlayerInfo *player)
14364 int old_element, new_element;
14365 int dropx = player->jx, dropy = player->jy;
14366 int drop_direction = player->MovDir;
14367 int drop_side = drop_direction;
14368 int drop_element = get_next_dropped_element(player);
14370 /* do not drop an element on top of another element; when holding drop key
14371 pressed without moving, dropped element must move away before the next
14372 element can be dropped (this is especially important if the next element
14373 is dynamite, which can be placed on background for historical reasons) */
14374 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14377 if (IS_THROWABLE(drop_element))
14379 dropx += GET_DX_FROM_DIR(drop_direction);
14380 dropy += GET_DY_FROM_DIR(drop_direction);
14382 if (!IN_LEV_FIELD(dropx, dropy))
14386 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14387 new_element = drop_element; /* default: no change when dropping */
14389 /* check if player is active, not moving and ready to drop */
14390 if (!player->active || player->MovPos || player->drop_delay > 0)
14393 /* check if player has anything that can be dropped */
14394 if (new_element == EL_UNDEFINED)
14397 /* only set if player has anything that can be dropped */
14398 player->is_dropping_pressed = TRUE;
14400 /* check if drop key was pressed long enough for EM style dynamite */
14401 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14404 /* check if anything can be dropped at the current position */
14405 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14408 /* collected custom elements can only be dropped on empty fields */
14409 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14412 if (old_element != EL_EMPTY)
14413 Back[dropx][dropy] = old_element; /* store old element on this field */
14415 ResetGfxAnimation(dropx, dropy);
14416 ResetRandomAnimationValue(dropx, dropy);
14418 if (player->inventory_size > 0 ||
14419 player->inventory_infinite_element != EL_UNDEFINED)
14421 if (player->inventory_size > 0)
14423 player->inventory_size--;
14425 DrawGameDoorValues();
14427 if (new_element == EL_DYNAMITE)
14428 new_element = EL_DYNAMITE_ACTIVE;
14429 else if (new_element == EL_EM_DYNAMITE)
14430 new_element = EL_EM_DYNAMITE_ACTIVE;
14431 else if (new_element == EL_SP_DISK_RED)
14432 new_element = EL_SP_DISK_RED_ACTIVE;
14435 Feld[dropx][dropy] = new_element;
14437 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14438 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14439 el2img(Feld[dropx][dropy]), 0);
14441 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14443 /* needed if previous element just changed to "empty" in the last frame */
14444 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14446 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14447 player->index_bit, drop_side);
14448 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14450 player->index_bit, drop_side);
14452 TestIfElementTouchesCustomElement(dropx, dropy);
14454 else /* player is dropping a dyna bomb */
14456 player->dynabombs_left--;
14458 Feld[dropx][dropy] = new_element;
14460 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14461 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14462 el2img(Feld[dropx][dropy]), 0);
14464 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14467 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14468 InitField_WithBug1(dropx, dropy, FALSE);
14470 new_element = Feld[dropx][dropy]; /* element might have changed */
14472 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14473 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14475 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14476 MovDir[dropx][dropy] = drop_direction;
14478 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14480 /* do not cause impact style collision by dropping elements that can fall */
14481 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14484 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14485 player->is_dropping = TRUE;
14487 player->drop_pressed_delay = 0;
14488 player->is_dropping_pressed = FALSE;
14490 player->drop_x = dropx;
14491 player->drop_y = dropy;
14496 /* ------------------------------------------------------------------------- */
14497 /* game sound playing functions */
14498 /* ------------------------------------------------------------------------- */
14500 static int *loop_sound_frame = NULL;
14501 static int *loop_sound_volume = NULL;
14503 void InitPlayLevelSound()
14505 int num_sounds = getSoundListSize();
14507 checked_free(loop_sound_frame);
14508 checked_free(loop_sound_volume);
14510 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14511 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14514 static void PlayLevelSound(int x, int y, int nr)
14516 int sx = SCREENX(x), sy = SCREENY(y);
14517 int volume, stereo_position;
14518 int max_distance = 8;
14519 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14521 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14522 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14525 if (!IN_LEV_FIELD(x, y) ||
14526 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14527 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14530 volume = SOUND_MAX_VOLUME;
14532 if (!IN_SCR_FIELD(sx, sy))
14534 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14535 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14537 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14540 stereo_position = (SOUND_MAX_LEFT +
14541 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14542 (SCR_FIELDX + 2 * max_distance));
14544 if (IS_LOOP_SOUND(nr))
14546 /* This assures that quieter loop sounds do not overwrite louder ones,
14547 while restarting sound volume comparison with each new game frame. */
14549 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14552 loop_sound_volume[nr] = volume;
14553 loop_sound_frame[nr] = FrameCounter;
14556 PlaySoundExt(nr, volume, stereo_position, type);
14559 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14561 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14562 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14563 y < LEVELY(BY1) ? LEVELY(BY1) :
14564 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14568 static void PlayLevelSoundAction(int x, int y, int action)
14570 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14573 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14575 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14577 if (sound_effect != SND_UNDEFINED)
14578 PlayLevelSound(x, y, sound_effect);
14581 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14584 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14586 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14587 PlayLevelSound(x, y, sound_effect);
14590 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14592 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14594 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14595 PlayLevelSound(x, y, sound_effect);
14598 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14600 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14602 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14603 StopSound(sound_effect);
14606 static int getLevelMusicNr()
14608 if (levelset.music[level_nr] != MUS_UNDEFINED)
14609 return levelset.music[level_nr]; /* from config file */
14611 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14614 static void FadeLevelSounds()
14619 static void FadeLevelMusic()
14621 int music_nr = getLevelMusicNr();
14622 char *curr_music = getCurrentlyPlayingMusicFilename();
14623 char *next_music = getMusicInfoEntryFilename(music_nr);
14625 if (!strEqual(curr_music, next_music))
14629 void FadeLevelSoundsAndMusic()
14635 static void PlayLevelMusic()
14637 int music_nr = getLevelMusicNr();
14638 char *curr_music = getCurrentlyPlayingMusicFilename();
14639 char *next_music = getMusicInfoEntryFilename(music_nr);
14641 if (!strEqual(curr_music, next_music))
14642 PlayMusic(music_nr);
14645 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14647 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14648 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14649 int x = xx - 1 - offset;
14650 int y = yy - 1 - offset;
14655 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14659 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14663 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14667 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14671 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14675 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14679 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14682 case SAMPLE_android_clone:
14683 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14686 case SAMPLE_android_move:
14687 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14690 case SAMPLE_spring:
14691 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14695 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14699 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14702 case SAMPLE_eater_eat:
14703 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14707 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14710 case SAMPLE_collect:
14711 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14714 case SAMPLE_diamond:
14715 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14718 case SAMPLE_squash:
14719 /* !!! CHECK THIS !!! */
14721 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14723 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14727 case SAMPLE_wonderfall:
14728 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14732 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14736 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14740 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14744 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14748 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14752 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14755 case SAMPLE_wonder:
14756 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14760 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14763 case SAMPLE_exit_open:
14764 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14767 case SAMPLE_exit_leave:
14768 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14771 case SAMPLE_dynamite:
14772 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14776 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14780 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14784 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14788 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14792 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14796 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14800 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14805 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14807 int element = map_element_SP_to_RND(element_sp);
14808 int action = map_action_SP_to_RND(action_sp);
14809 int offset = (setup.sp_show_border_elements ? 0 : 1);
14810 int x = xx - offset;
14811 int y = yy - offset;
14813 PlayLevelSoundElementAction(x, y, element, action);
14816 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14818 int element = map_element_MM_to_RND(element_mm);
14819 int action = map_action_MM_to_RND(action_mm);
14821 int x = xx - offset;
14822 int y = yy - offset;
14824 if (!IS_MM_ELEMENT(element))
14825 element = EL_MM_DEFAULT;
14827 PlayLevelSoundElementAction(x, y, element, action);
14830 void PlaySound_MM(int sound_mm)
14832 int sound = map_sound_MM_to_RND(sound_mm);
14834 if (sound == SND_UNDEFINED)
14840 void PlaySoundLoop_MM(int sound_mm)
14842 int sound = map_sound_MM_to_RND(sound_mm);
14844 if (sound == SND_UNDEFINED)
14847 PlaySoundLoop(sound);
14850 void StopSound_MM(int sound_mm)
14852 int sound = map_sound_MM_to_RND(sound_mm);
14854 if (sound == SND_UNDEFINED)
14860 void RaiseScore(int value)
14862 local_player->score += value;
14864 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14866 DisplayGameControlValues();
14869 void RaiseScoreElement(int element)
14874 case EL_BD_DIAMOND:
14875 case EL_EMERALD_YELLOW:
14876 case EL_EMERALD_RED:
14877 case EL_EMERALD_PURPLE:
14878 case EL_SP_INFOTRON:
14879 RaiseScore(level.score[SC_EMERALD]);
14882 RaiseScore(level.score[SC_DIAMOND]);
14885 RaiseScore(level.score[SC_CRYSTAL]);
14888 RaiseScore(level.score[SC_PEARL]);
14891 case EL_BD_BUTTERFLY:
14892 case EL_SP_ELECTRON:
14893 RaiseScore(level.score[SC_BUG]);
14896 case EL_BD_FIREFLY:
14897 case EL_SP_SNIKSNAK:
14898 RaiseScore(level.score[SC_SPACESHIP]);
14901 case EL_DARK_YAMYAM:
14902 RaiseScore(level.score[SC_YAMYAM]);
14905 RaiseScore(level.score[SC_ROBOT]);
14908 RaiseScore(level.score[SC_PACMAN]);
14911 RaiseScore(level.score[SC_NUT]);
14914 case EL_EM_DYNAMITE:
14915 case EL_SP_DISK_RED:
14916 case EL_DYNABOMB_INCREASE_NUMBER:
14917 case EL_DYNABOMB_INCREASE_SIZE:
14918 case EL_DYNABOMB_INCREASE_POWER:
14919 RaiseScore(level.score[SC_DYNAMITE]);
14921 case EL_SHIELD_NORMAL:
14922 case EL_SHIELD_DEADLY:
14923 RaiseScore(level.score[SC_SHIELD]);
14925 case EL_EXTRA_TIME:
14926 RaiseScore(level.extra_time_score);
14940 case EL_DC_KEY_WHITE:
14941 RaiseScore(level.score[SC_KEY]);
14944 RaiseScore(element_info[element].collect_score);
14949 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14951 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14953 /* closing door required in case of envelope style request dialogs */
14955 CloseDoor(DOOR_CLOSE_1);
14957 if (network.enabled)
14958 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14962 FadeSkipNextFadeIn();
14964 SetGameStatus(GAME_MODE_MAIN);
14969 else /* continue playing the game */
14971 if (tape.playing && tape.deactivate_display)
14972 TapeDeactivateDisplayOff(TRUE);
14974 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14976 if (tape.playing && tape.deactivate_display)
14977 TapeDeactivateDisplayOn();
14981 void RequestQuitGame(boolean ask_if_really_quit)
14983 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14984 boolean skip_request = AllPlayersGone || quick_quit;
14986 RequestQuitGameExt(skip_request, quick_quit,
14987 "Do you really want to quit the game?");
14990 void RequestRestartGame(char *message)
14992 game.restart_game_message = NULL;
14994 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14996 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15000 SetGameStatus(GAME_MODE_MAIN);
15007 /* ------------------------------------------------------------------------- */
15008 /* random generator functions */
15009 /* ------------------------------------------------------------------------- */
15011 unsigned int InitEngineRandom_RND(int seed)
15013 game.num_random_calls = 0;
15015 return InitEngineRandom(seed);
15018 unsigned int RND(int max)
15022 game.num_random_calls++;
15024 return GetEngineRandom(max);
15031 /* ------------------------------------------------------------------------- */
15032 /* game engine snapshot handling functions */
15033 /* ------------------------------------------------------------------------- */
15035 struct EngineSnapshotInfo
15037 /* runtime values for custom element collect score */
15038 int collect_score[NUM_CUSTOM_ELEMENTS];
15040 /* runtime values for group element choice position */
15041 int choice_pos[NUM_GROUP_ELEMENTS];
15043 /* runtime values for belt position animations */
15044 int belt_graphic[4][NUM_BELT_PARTS];
15045 int belt_anim_mode[4][NUM_BELT_PARTS];
15048 static struct EngineSnapshotInfo engine_snapshot_rnd;
15049 static char *snapshot_level_identifier = NULL;
15050 static int snapshot_level_nr = -1;
15052 static void SaveEngineSnapshotValues_RND()
15054 static int belt_base_active_element[4] =
15056 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15057 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15058 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15059 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15063 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15065 int element = EL_CUSTOM_START + i;
15067 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15070 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15072 int element = EL_GROUP_START + i;
15074 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15077 for (i = 0; i < 4; i++)
15079 for (j = 0; j < NUM_BELT_PARTS; j++)
15081 int element = belt_base_active_element[i] + j;
15082 int graphic = el2img(element);
15083 int anim_mode = graphic_info[graphic].anim_mode;
15085 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15086 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15091 static void LoadEngineSnapshotValues_RND()
15093 unsigned int num_random_calls = game.num_random_calls;
15096 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15098 int element = EL_CUSTOM_START + i;
15100 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15103 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15105 int element = EL_GROUP_START + i;
15107 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15110 for (i = 0; i < 4; i++)
15112 for (j = 0; j < NUM_BELT_PARTS; j++)
15114 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15115 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15117 graphic_info[graphic].anim_mode = anim_mode;
15121 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15123 InitRND(tape.random_seed);
15124 for (i = 0; i < num_random_calls; i++)
15128 if (game.num_random_calls != num_random_calls)
15130 Error(ERR_INFO, "number of random calls out of sync");
15131 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15132 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15133 Error(ERR_EXIT, "this should not happen -- please debug");
15137 void FreeEngineSnapshotSingle()
15139 FreeSnapshotSingle();
15141 setString(&snapshot_level_identifier, NULL);
15142 snapshot_level_nr = -1;
15145 void FreeEngineSnapshotList()
15147 FreeSnapshotList();
15150 ListNode *SaveEngineSnapshotBuffers()
15152 ListNode *buffers = NULL;
15154 /* copy some special values to a structure better suited for the snapshot */
15156 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15157 SaveEngineSnapshotValues_RND();
15158 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15159 SaveEngineSnapshotValues_EM();
15160 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15161 SaveEngineSnapshotValues_SP(&buffers);
15162 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15163 SaveEngineSnapshotValues_MM(&buffers);
15165 /* save values stored in special snapshot structure */
15167 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15168 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15169 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15170 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15171 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15173 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15174 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15176 /* save further RND engine values */
15178 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15180 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15182 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15184 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15185 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15187 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15188 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15189 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15190 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15191 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15193 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15194 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15195 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15197 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15199 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15201 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15202 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15204 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15205 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15206 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15207 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15208 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15209 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15210 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15211 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15212 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15213 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15214 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15215 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15216 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15217 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15218 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15219 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15220 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15221 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15223 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15224 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15226 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15227 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15228 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15230 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15231 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15233 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15234 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15235 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15236 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15237 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15239 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15240 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15243 ListNode *node = engine_snapshot_list_rnd;
15246 while (node != NULL)
15248 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15253 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15259 void SaveEngineSnapshotSingle()
15261 ListNode *buffers = SaveEngineSnapshotBuffers();
15263 /* finally save all snapshot buffers to single snapshot */
15264 SaveSnapshotSingle(buffers);
15266 /* save level identification information */
15267 setString(&snapshot_level_identifier, leveldir_current->identifier);
15268 snapshot_level_nr = level_nr;
15271 boolean CheckSaveEngineSnapshotToList()
15273 boolean save_snapshot =
15274 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15275 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15276 game.snapshot.changed_action) ||
15277 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15278 game.snapshot.collected_item));
15280 game.snapshot.changed_action = FALSE;
15281 game.snapshot.collected_item = FALSE;
15282 game.snapshot.save_snapshot = save_snapshot;
15284 return save_snapshot;
15287 void SaveEngineSnapshotToList()
15289 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15293 ListNode *buffers = SaveEngineSnapshotBuffers();
15295 /* finally save all snapshot buffers to snapshot list */
15296 SaveSnapshotToList(buffers);
15299 void SaveEngineSnapshotToListInitial()
15301 FreeEngineSnapshotList();
15303 SaveEngineSnapshotToList();
15306 void LoadEngineSnapshotValues()
15308 /* restore special values from snapshot structure */
15310 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15311 LoadEngineSnapshotValues_RND();
15312 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15313 LoadEngineSnapshotValues_EM();
15314 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15315 LoadEngineSnapshotValues_SP();
15316 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15317 LoadEngineSnapshotValues_MM();
15320 void LoadEngineSnapshotSingle()
15322 LoadSnapshotSingle();
15324 LoadEngineSnapshotValues();
15327 void LoadEngineSnapshot_Undo(int steps)
15329 LoadSnapshotFromList_Older(steps);
15331 LoadEngineSnapshotValues();
15334 void LoadEngineSnapshot_Redo(int steps)
15336 LoadSnapshotFromList_Newer(steps);
15338 LoadEngineSnapshotValues();
15341 boolean CheckEngineSnapshotSingle()
15343 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15344 snapshot_level_nr == level_nr);
15347 boolean CheckEngineSnapshotList()
15349 return CheckSnapshotList();
15353 /* ---------- new game button stuff ---------------------------------------- */
15360 boolean *setup_value;
15361 boolean allowed_on_tape;
15363 } gamebutton_info[NUM_GAME_BUTTONS] =
15366 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15367 GAME_CTRL_ID_STOP, NULL,
15371 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15372 GAME_CTRL_ID_PAUSE, NULL,
15376 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15377 GAME_CTRL_ID_PLAY, NULL,
15381 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15382 GAME_CTRL_ID_UNDO, NULL,
15386 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15387 GAME_CTRL_ID_REDO, NULL,
15391 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15392 GAME_CTRL_ID_SAVE, NULL,
15396 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15397 GAME_CTRL_ID_PAUSE2, NULL,
15401 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15402 GAME_CTRL_ID_LOAD, NULL,
15406 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15407 GAME_CTRL_ID_PANEL_STOP, NULL,
15411 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15412 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15413 FALSE, "pause game"
15416 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15417 GAME_CTRL_ID_PANEL_PLAY, NULL,
15421 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15422 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15423 TRUE, "background music on/off"
15426 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15427 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15428 TRUE, "sound loops on/off"
15431 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15432 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15433 TRUE, "normal sounds on/off"
15436 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15437 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15438 FALSE, "background music on/off"
15441 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15442 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15443 FALSE, "sound loops on/off"
15446 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15447 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15448 FALSE, "normal sounds on/off"
15452 void CreateGameButtons()
15456 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15458 int graphic = gamebutton_info[i].graphic;
15459 struct GraphicInfo *gfx = &graphic_info[graphic];
15460 struct XY *pos = gamebutton_info[i].pos;
15461 struct GadgetInfo *gi;
15464 unsigned int event_mask;
15465 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15466 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15467 int base_x = (on_tape ? VX : DX);
15468 int base_y = (on_tape ? VY : DY);
15469 int gd_x = gfx->src_x;
15470 int gd_y = gfx->src_y;
15471 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15472 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15473 int gd_xa = gfx->src_x + gfx->active_xoffset;
15474 int gd_ya = gfx->src_y + gfx->active_yoffset;
15475 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15476 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15479 if (gfx->bitmap == NULL)
15481 game_gadget[id] = NULL;
15486 if (id == GAME_CTRL_ID_STOP ||
15487 id == GAME_CTRL_ID_PANEL_STOP ||
15488 id == GAME_CTRL_ID_PLAY ||
15489 id == GAME_CTRL_ID_PANEL_PLAY ||
15490 id == GAME_CTRL_ID_SAVE ||
15491 id == GAME_CTRL_ID_LOAD)
15493 button_type = GD_TYPE_NORMAL_BUTTON;
15495 event_mask = GD_EVENT_RELEASED;
15497 else if (id == GAME_CTRL_ID_UNDO ||
15498 id == GAME_CTRL_ID_REDO)
15500 button_type = GD_TYPE_NORMAL_BUTTON;
15502 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15506 button_type = GD_TYPE_CHECK_BUTTON;
15507 checked = (gamebutton_info[i].setup_value != NULL ?
15508 *gamebutton_info[i].setup_value : FALSE);
15509 event_mask = GD_EVENT_PRESSED;
15512 gi = CreateGadget(GDI_CUSTOM_ID, id,
15513 GDI_IMAGE_ID, graphic,
15514 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15515 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15516 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15517 GDI_WIDTH, gfx->width,
15518 GDI_HEIGHT, gfx->height,
15519 GDI_TYPE, button_type,
15520 GDI_STATE, GD_BUTTON_UNPRESSED,
15521 GDI_CHECKED, checked,
15522 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15523 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15524 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15525 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15526 GDI_DIRECT_DRAW, FALSE,
15527 GDI_EVENT_MASK, event_mask,
15528 GDI_CALLBACK_ACTION, HandleGameButtons,
15532 Error(ERR_EXIT, "cannot create gadget");
15534 game_gadget[id] = gi;
15538 void FreeGameButtons()
15542 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15543 FreeGadget(game_gadget[i]);
15546 static void UnmapGameButtonsAtSamePosition(int id)
15550 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15552 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15553 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15554 UnmapGadget(game_gadget[i]);
15557 static void UnmapGameButtonsAtSamePosition_All()
15559 if (setup.show_snapshot_buttons)
15561 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15562 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15563 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15567 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15568 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15569 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15571 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15572 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15573 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15577 static void MapGameButtonsAtSamePosition(int id)
15581 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15583 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15584 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15585 MapGadget(game_gadget[i]);
15587 UnmapGameButtonsAtSamePosition_All();
15590 void MapUndoRedoButtons()
15592 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15593 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15595 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15596 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15598 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15601 void UnmapUndoRedoButtons()
15603 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15604 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15606 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15607 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15609 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15612 void MapGameButtonsExt(boolean on_tape)
15616 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15617 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15618 i != GAME_CTRL_ID_UNDO &&
15619 i != GAME_CTRL_ID_REDO)
15620 MapGadget(game_gadget[i]);
15622 UnmapGameButtonsAtSamePosition_All();
15624 RedrawGameButtons();
15627 void UnmapGameButtonsExt(boolean on_tape)
15631 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15632 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15633 UnmapGadget(game_gadget[i]);
15636 void RedrawGameButtonsExt(boolean on_tape)
15640 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15641 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15642 RedrawGadget(game_gadget[i]);
15644 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15645 redraw_mask &= ~REDRAW_ALL;
15648 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15653 gi->checked = state;
15656 void RedrawSoundButtonGadget(int id)
15658 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15659 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15660 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15661 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15662 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15663 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15666 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15667 RedrawGadget(game_gadget[id2]);
15670 void MapGameButtons()
15672 MapGameButtonsExt(FALSE);
15675 void UnmapGameButtons()
15677 UnmapGameButtonsExt(FALSE);
15680 void RedrawGameButtons()
15682 RedrawGameButtonsExt(FALSE);
15685 void MapGameButtonsOnTape()
15687 MapGameButtonsExt(TRUE);
15690 void UnmapGameButtonsOnTape()
15692 UnmapGameButtonsExt(TRUE);
15695 void RedrawGameButtonsOnTape()
15697 RedrawGameButtonsExt(TRUE);
15700 void GameUndoRedoExt()
15702 ClearPlayerAction();
15704 tape.pausing = TRUE;
15707 UpdateAndDisplayGameControlValues();
15709 DrawCompleteVideoDisplay();
15710 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15711 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15712 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15717 void GameUndo(int steps)
15719 if (!CheckEngineSnapshotList())
15722 LoadEngineSnapshot_Undo(steps);
15727 void GameRedo(int steps)
15729 if (!CheckEngineSnapshotList())
15732 LoadEngineSnapshot_Redo(steps);
15737 static void HandleGameButtonsExt(int id, int button)
15739 static boolean game_undo_executed = FALSE;
15740 int steps = BUTTON_STEPSIZE(button);
15741 boolean handle_game_buttons =
15742 (game_status == GAME_MODE_PLAYING ||
15743 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15745 if (!handle_game_buttons)
15750 case GAME_CTRL_ID_STOP:
15751 case GAME_CTRL_ID_PANEL_STOP:
15752 if (game_status == GAME_MODE_MAIN)
15758 RequestQuitGame(TRUE);
15762 case GAME_CTRL_ID_PAUSE:
15763 case GAME_CTRL_ID_PAUSE2:
15764 case GAME_CTRL_ID_PANEL_PAUSE:
15765 if (network.enabled && game_status == GAME_MODE_PLAYING)
15768 SendToServer_ContinuePlaying();
15770 SendToServer_PausePlaying();
15773 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15775 game_undo_executed = FALSE;
15779 case GAME_CTRL_ID_PLAY:
15780 case GAME_CTRL_ID_PANEL_PLAY:
15781 if (game_status == GAME_MODE_MAIN)
15783 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15785 else if (tape.pausing)
15787 if (network.enabled)
15788 SendToServer_ContinuePlaying();
15790 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15794 case GAME_CTRL_ID_UNDO:
15795 // Important: When using "save snapshot when collecting an item" mode,
15796 // load last (current) snapshot for first "undo" after pressing "pause"
15797 // (else the last-but-one snapshot would be loaded, because the snapshot
15798 // pointer already points to the last snapshot when pressing "pause",
15799 // which is fine for "every step/move" mode, but not for "every collect")
15800 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15801 !game_undo_executed)
15804 game_undo_executed = TRUE;
15809 case GAME_CTRL_ID_REDO:
15813 case GAME_CTRL_ID_SAVE:
15817 case GAME_CTRL_ID_LOAD:
15821 case SOUND_CTRL_ID_MUSIC:
15822 case SOUND_CTRL_ID_PANEL_MUSIC:
15823 if (setup.sound_music)
15825 setup.sound_music = FALSE;
15829 else if (audio.music_available)
15831 setup.sound = setup.sound_music = TRUE;
15833 SetAudioMode(setup.sound);
15835 if (game_status == GAME_MODE_PLAYING)
15839 RedrawSoundButtonGadget(id);
15843 case SOUND_CTRL_ID_LOOPS:
15844 case SOUND_CTRL_ID_PANEL_LOOPS:
15845 if (setup.sound_loops)
15846 setup.sound_loops = FALSE;
15847 else if (audio.loops_available)
15849 setup.sound = setup.sound_loops = TRUE;
15851 SetAudioMode(setup.sound);
15854 RedrawSoundButtonGadget(id);
15858 case SOUND_CTRL_ID_SIMPLE:
15859 case SOUND_CTRL_ID_PANEL_SIMPLE:
15860 if (setup.sound_simple)
15861 setup.sound_simple = FALSE;
15862 else if (audio.sound_available)
15864 setup.sound = setup.sound_simple = TRUE;
15866 SetAudioMode(setup.sound);
15869 RedrawSoundButtonGadget(id);
15878 static void HandleGameButtons(struct GadgetInfo *gi)
15880 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15883 void HandleSoundButtonKeys(Key key)
15885 if (key == setup.shortcut.sound_simple)
15886 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15887 else if (key == setup.shortcut.sound_loops)
15888 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15889 else if (key == setup.shortcut.sound_music)
15890 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);