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 (!options.network || 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 = (options.network && !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 #if defined(NETWORK_AVALIABLE)
3507 /* initial null action */
3508 if (network_playing)
3509 SendToServer_MovePlayer(MV_NONE);
3518 TimeLeft = level.time;
3521 ScreenMovDir = MV_NONE;
3525 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3527 AllPlayersGone = FALSE;
3529 game.no_time_limit = (level.time == 0);
3531 game.yamyam_content_nr = 0;
3532 game.robot_wheel_active = FALSE;
3533 game.magic_wall_active = FALSE;
3534 game.magic_wall_time_left = 0;
3535 game.light_time_left = 0;
3536 game.timegate_time_left = 0;
3537 game.switchgate_pos = 0;
3538 game.wind_direction = level.wind_direction_initial;
3540 game.lenses_time_left = 0;
3541 game.magnify_time_left = 0;
3543 game.ball_state = level.ball_state_initial;
3544 game.ball_content_nr = 0;
3546 game.envelope_active = FALSE;
3548 /* set focus to local player for network games, else to all players */
3549 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3550 game.centered_player_nr_next = game.centered_player_nr;
3551 game.set_centered_player = FALSE;
3553 if (network_playing && tape.recording)
3555 /* store client dependent player focus when recording network games */
3556 tape.centered_player_nr_next = game.centered_player_nr_next;
3557 tape.set_centered_player = TRUE;
3560 for (i = 0; i < NUM_BELTS; i++)
3562 game.belt_dir[i] = MV_NONE;
3563 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3566 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3567 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3569 #if DEBUG_INIT_PLAYER
3572 printf("Player status at level initialization:\n");
3576 SCAN_PLAYFIELD(x, y)
3578 Feld[x][y] = level.field[x][y];
3579 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3580 ChangeDelay[x][y] = 0;
3581 ChangePage[x][y] = -1;
3582 CustomValue[x][y] = 0; /* initialized in InitField() */
3583 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3585 WasJustMoving[x][y] = 0;
3586 WasJustFalling[x][y] = 0;
3587 CheckCollision[x][y] = 0;
3588 CheckImpact[x][y] = 0;
3590 Pushed[x][y] = FALSE;
3592 ChangeCount[x][y] = 0;
3593 ChangeEvent[x][y] = -1;
3595 ExplodePhase[x][y] = 0;
3596 ExplodeDelay[x][y] = 0;
3597 ExplodeField[x][y] = EX_TYPE_NONE;
3599 RunnerVisit[x][y] = 0;
3600 PlayerVisit[x][y] = 0;
3603 GfxRandom[x][y] = INIT_GFX_RANDOM();
3604 GfxElement[x][y] = EL_UNDEFINED;
3605 GfxAction[x][y] = ACTION_DEFAULT;
3606 GfxDir[x][y] = MV_NONE;
3607 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3610 SCAN_PLAYFIELD(x, y)
3612 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3614 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3616 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3619 InitField(x, y, TRUE);
3621 ResetGfxAnimation(x, y);
3626 for (i = 0; i < MAX_PLAYERS; i++)
3628 struct PlayerInfo *player = &stored_player[i];
3630 /* set number of special actions for bored and sleeping animation */
3631 player->num_special_action_bored =
3632 get_num_special_action(player->artwork_element,
3633 ACTION_BORING_1, ACTION_BORING_LAST);
3634 player->num_special_action_sleeping =
3635 get_num_special_action(player->artwork_element,
3636 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3639 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3640 emulate_sb ? EMU_SOKOBAN :
3641 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3643 /* initialize type of slippery elements */
3644 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3646 if (!IS_CUSTOM_ELEMENT(i))
3648 /* default: elements slip down either to the left or right randomly */
3649 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3651 /* SP style elements prefer to slip down on the left side */
3652 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3653 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3655 /* BD style elements prefer to slip down on the left side */
3656 if (game.emulation == EMU_BOULDERDASH)
3657 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3661 /* initialize explosion and ignition delay */
3662 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3664 if (!IS_CUSTOM_ELEMENT(i))
3667 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3668 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3669 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3670 int last_phase = (num_phase + 1) * delay;
3671 int half_phase = (num_phase / 2) * delay;
3673 element_info[i].explosion_delay = last_phase - 1;
3674 element_info[i].ignition_delay = half_phase;
3676 if (i == EL_BLACK_ORB)
3677 element_info[i].ignition_delay = 1;
3681 /* correct non-moving belts to start moving left */
3682 for (i = 0; i < NUM_BELTS; i++)
3683 if (game.belt_dir[i] == MV_NONE)
3684 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3686 #if USE_NEW_PLAYER_ASSIGNMENTS
3687 for (i = 0; i < MAX_PLAYERS; i++)
3689 stored_player[i].connected = FALSE;
3691 /* in network game mode, the local player might not be the first player */
3692 if (stored_player[i].connected_locally)
3693 local_player = &stored_player[i];
3696 if (!options.network)
3697 local_player->connected = TRUE;
3701 for (i = 0; i < MAX_PLAYERS; i++)
3702 stored_player[i].connected = tape.player_participates[i];
3704 else if (options.network)
3706 /* add team mode players connected over the network (needed for correct
3707 assignment of player figures from level to locally playing players) */
3709 for (i = 0; i < MAX_PLAYERS; i++)
3710 if (stored_player[i].connected_network)
3711 stored_player[i].connected = TRUE;
3713 else if (game.team_mode)
3715 /* try to guess locally connected team mode players (needed for correct
3716 assignment of player figures from level to locally playing players) */
3718 for (i = 0; i < MAX_PLAYERS; i++)
3719 if (setup.input[i].use_joystick ||
3720 setup.input[i].key.left != KSYM_UNDEFINED)
3721 stored_player[i].connected = TRUE;
3724 #if DEBUG_INIT_PLAYER
3727 printf("Player status after level initialization:\n");
3729 for (i = 0; i < MAX_PLAYERS; i++)
3731 struct PlayerInfo *player = &stored_player[i];
3733 printf("- player %d: present == %d, connected == %d, active == %d",
3739 if (local_player == player)
3740 printf(" (local player)");
3747 #if DEBUG_INIT_PLAYER
3749 printf("Reassigning players ...\n");
3752 /* check if any connected player was not found in playfield */
3753 for (i = 0; i < MAX_PLAYERS; i++)
3755 struct PlayerInfo *player = &stored_player[i];
3757 if (player->connected && !player->present)
3759 struct PlayerInfo *field_player = NULL;
3761 #if DEBUG_INIT_PLAYER
3763 printf("- looking for field player for player %d ...\n", i + 1);
3766 /* assign first free player found that is present in the playfield */
3768 /* first try: look for unmapped playfield player that is not connected */
3769 for (j = 0; j < MAX_PLAYERS; j++)
3770 if (field_player == NULL &&
3771 stored_player[j].present &&
3772 !stored_player[j].mapped &&
3773 !stored_player[j].connected)
3774 field_player = &stored_player[j];
3776 /* second try: look for *any* unmapped playfield player */
3777 for (j = 0; j < MAX_PLAYERS; j++)
3778 if (field_player == NULL &&
3779 stored_player[j].present &&
3780 !stored_player[j].mapped)
3781 field_player = &stored_player[j];
3783 if (field_player != NULL)
3785 int jx = field_player->jx, jy = field_player->jy;
3787 #if DEBUG_INIT_PLAYER
3789 printf("- found player %d\n", field_player->index_nr + 1);
3792 player->present = FALSE;
3793 player->active = FALSE;
3795 field_player->present = TRUE;
3796 field_player->active = TRUE;
3799 player->initial_element = field_player->initial_element;
3800 player->artwork_element = field_player->artwork_element;
3802 player->block_last_field = field_player->block_last_field;
3803 player->block_delay_adjustment = field_player->block_delay_adjustment;
3806 StorePlayer[jx][jy] = field_player->element_nr;
3808 field_player->jx = field_player->last_jx = jx;
3809 field_player->jy = field_player->last_jy = jy;
3811 if (local_player == player)
3812 local_player = field_player;
3814 map_player_action[field_player->index_nr] = i;
3816 field_player->mapped = TRUE;
3818 #if DEBUG_INIT_PLAYER
3820 printf("- map_player_action[%d] == %d\n",
3821 field_player->index_nr + 1, i + 1);
3826 if (player->connected && player->present)
3827 player->mapped = TRUE;
3830 #if DEBUG_INIT_PLAYER
3833 printf("Player status after player assignment (first stage):\n");
3835 for (i = 0; i < MAX_PLAYERS; i++)
3837 struct PlayerInfo *player = &stored_player[i];
3839 printf("- player %d: present == %d, connected == %d, active == %d",
3845 if (local_player == player)
3846 printf(" (local player)");
3855 /* check if any connected player was not found in playfield */
3856 for (i = 0; i < MAX_PLAYERS; i++)
3858 struct PlayerInfo *player = &stored_player[i];
3860 if (player->connected && !player->present)
3862 for (j = 0; j < MAX_PLAYERS; j++)
3864 struct PlayerInfo *field_player = &stored_player[j];
3865 int jx = field_player->jx, jy = field_player->jy;
3867 /* assign first free player found that is present in the playfield */
3868 if (field_player->present && !field_player->connected)
3870 player->present = TRUE;
3871 player->active = TRUE;
3873 field_player->present = FALSE;
3874 field_player->active = FALSE;
3876 player->initial_element = field_player->initial_element;
3877 player->artwork_element = field_player->artwork_element;
3879 player->block_last_field = field_player->block_last_field;
3880 player->block_delay_adjustment = field_player->block_delay_adjustment;
3882 StorePlayer[jx][jy] = player->element_nr;
3884 player->jx = player->last_jx = jx;
3885 player->jy = player->last_jy = jy;
3895 printf("::: local_player->present == %d\n", local_player->present);
3900 /* when playing a tape, eliminate all players who do not participate */
3902 #if USE_NEW_PLAYER_ASSIGNMENTS
3904 if (!game.team_mode)
3906 for (i = 0; i < MAX_PLAYERS; i++)
3908 if (stored_player[i].active &&
3909 !tape.player_participates[map_player_action[i]])
3911 struct PlayerInfo *player = &stored_player[i];
3912 int jx = player->jx, jy = player->jy;
3914 #if DEBUG_INIT_PLAYER
3916 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3919 player->active = FALSE;
3920 StorePlayer[jx][jy] = 0;
3921 Feld[jx][jy] = EL_EMPTY;
3928 for (i = 0; i < MAX_PLAYERS; i++)
3930 if (stored_player[i].active &&
3931 !tape.player_participates[i])
3933 struct PlayerInfo *player = &stored_player[i];
3934 int jx = player->jx, jy = player->jy;
3936 player->active = FALSE;
3937 StorePlayer[jx][jy] = 0;
3938 Feld[jx][jy] = EL_EMPTY;
3943 else if (!options.network && !game.team_mode) /* && !tape.playing */
3945 /* when in single player mode, eliminate all but the first active player */
3947 for (i = 0; i < MAX_PLAYERS; i++)
3949 if (stored_player[i].active)
3951 for (j = i + 1; j < MAX_PLAYERS; j++)
3953 if (stored_player[j].active)
3955 struct PlayerInfo *player = &stored_player[j];
3956 int jx = player->jx, jy = player->jy;
3958 player->active = FALSE;
3959 player->present = FALSE;
3961 StorePlayer[jx][jy] = 0;
3962 Feld[jx][jy] = EL_EMPTY;
3969 /* when recording the game, store which players take part in the game */
3972 #if USE_NEW_PLAYER_ASSIGNMENTS
3973 for (i = 0; i < MAX_PLAYERS; i++)
3974 if (stored_player[i].connected)
3975 tape.player_participates[i] = TRUE;
3977 for (i = 0; i < MAX_PLAYERS; i++)
3978 if (stored_player[i].active)
3979 tape.player_participates[i] = TRUE;
3983 #if DEBUG_INIT_PLAYER
3986 printf("Player status after player assignment (final stage):\n");
3988 for (i = 0; i < MAX_PLAYERS; i++)
3990 struct PlayerInfo *player = &stored_player[i];
3992 printf("- player %d: present == %d, connected == %d, active == %d",
3998 if (local_player == player)
3999 printf(" (local player)");
4006 if (BorderElement == EL_EMPTY)
4009 SBX_Right = lev_fieldx - SCR_FIELDX;
4011 SBY_Lower = lev_fieldy - SCR_FIELDY;
4016 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4018 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4021 if (full_lev_fieldx <= SCR_FIELDX)
4022 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4023 if (full_lev_fieldy <= SCR_FIELDY)
4024 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4026 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4028 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4031 /* if local player not found, look for custom element that might create
4032 the player (make some assumptions about the right custom element) */
4033 if (!local_player->present)
4035 int start_x = 0, start_y = 0;
4036 int found_rating = 0;
4037 int found_element = EL_UNDEFINED;
4038 int player_nr = local_player->index_nr;
4040 SCAN_PLAYFIELD(x, y)
4042 int element = Feld[x][y];
4047 if (level.use_start_element[player_nr] &&
4048 level.start_element[player_nr] == element &&
4055 found_element = element;
4058 if (!IS_CUSTOM_ELEMENT(element))
4061 if (CAN_CHANGE(element))
4063 for (i = 0; i < element_info[element].num_change_pages; i++)
4065 /* check for player created from custom element as single target */
4066 content = element_info[element].change_page[i].target_element;
4067 is_player = ELEM_IS_PLAYER(content);
4069 if (is_player && (found_rating < 3 ||
4070 (found_rating == 3 && element < found_element)))
4076 found_element = element;
4081 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4083 /* check for player created from custom element as explosion content */
4084 content = element_info[element].content.e[xx][yy];
4085 is_player = ELEM_IS_PLAYER(content);
4087 if (is_player && (found_rating < 2 ||
4088 (found_rating == 2 && element < found_element)))
4090 start_x = x + xx - 1;
4091 start_y = y + yy - 1;
4094 found_element = element;
4097 if (!CAN_CHANGE(element))
4100 for (i = 0; i < element_info[element].num_change_pages; i++)
4102 /* check for player created from custom element as extended target */
4104 element_info[element].change_page[i].target_content.e[xx][yy];
4106 is_player = ELEM_IS_PLAYER(content);
4108 if (is_player && (found_rating < 1 ||
4109 (found_rating == 1 && element < found_element)))
4111 start_x = x + xx - 1;
4112 start_y = y + yy - 1;
4115 found_element = element;
4121 scroll_x = SCROLL_POSITION_X(start_x);
4122 scroll_y = SCROLL_POSITION_Y(start_y);
4126 scroll_x = SCROLL_POSITION_X(local_player->jx);
4127 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4130 /* !!! FIX THIS (START) !!! */
4131 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4133 InitGameEngine_EM();
4135 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4137 InitGameEngine_SP();
4139 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4141 InitGameEngine_MM();
4145 DrawLevel(REDRAW_FIELD);
4148 /* after drawing the level, correct some elements */
4149 if (game.timegate_time_left == 0)
4150 CloseAllOpenTimegates();
4153 /* blit playfield from scroll buffer to normal back buffer for fading in */
4154 BlitScreenToBitmap(backbuffer);
4155 /* !!! FIX THIS (END) !!! */
4157 DrawMaskedBorder(fade_mask);
4162 // full screen redraw is required at this point in the following cases:
4163 // - special editor door undrawn when game was started from level editor
4164 // - drawing area (playfield) was changed and has to be removed completely
4165 redraw_mask = REDRAW_ALL;
4169 if (!game.restart_level)
4171 /* copy default game door content to main double buffer */
4173 /* !!! CHECK AGAIN !!! */
4174 SetPanelBackground();
4175 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4176 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4179 SetPanelBackground();
4180 SetDrawBackgroundMask(REDRAW_DOOR_1);
4182 UpdateAndDisplayGameControlValues();
4184 if (!game.restart_level)
4190 CreateGameButtons();
4195 /* copy actual game door content to door double buffer for OpenDoor() */
4196 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4198 OpenDoor(DOOR_OPEN_ALL);
4200 KeyboardAutoRepeatOffUnlessAutoplay();
4202 #if DEBUG_INIT_PLAYER
4205 printf("Player status (final):\n");
4207 for (i = 0; i < MAX_PLAYERS; i++)
4209 struct PlayerInfo *player = &stored_player[i];
4211 printf("- player %d: present == %d, connected == %d, active == %d",
4217 if (local_player == player)
4218 printf(" (local player)");
4231 if (!game.restart_level && !tape.playing)
4233 LevelStats_incPlayed(level_nr);
4235 SaveLevelSetup_SeriesInfo();
4238 game.restart_level = FALSE;
4239 game.restart_game_message = NULL;
4241 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4242 InitGameActions_MM();
4244 SaveEngineSnapshotToListInitial();
4246 if (!game.restart_level)
4248 PlaySound(SND_GAME_STARTING);
4250 if (setup.sound_music)
4255 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4256 int actual_player_x, int actual_player_y)
4258 /* this is used for non-R'n'D game engines to update certain engine values */
4260 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4262 actual_player_x = correctLevelPosX_EM(actual_player_x);
4263 actual_player_y = correctLevelPosY_EM(actual_player_y);
4266 /* needed to determine if sounds are played within the visible screen area */
4267 scroll_x = actual_scroll_x;
4268 scroll_y = actual_scroll_y;
4270 /* needed to get player position for "follow finger" playing input method */
4271 local_player->jx = actual_player_x;
4272 local_player->jy = actual_player_y;
4275 void InitMovDir(int x, int y)
4277 int i, element = Feld[x][y];
4278 static int xy[4][2] =
4285 static int direction[3][4] =
4287 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4288 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4289 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4298 Feld[x][y] = EL_BUG;
4299 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4302 case EL_SPACESHIP_RIGHT:
4303 case EL_SPACESHIP_UP:
4304 case EL_SPACESHIP_LEFT:
4305 case EL_SPACESHIP_DOWN:
4306 Feld[x][y] = EL_SPACESHIP;
4307 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4310 case EL_BD_BUTTERFLY_RIGHT:
4311 case EL_BD_BUTTERFLY_UP:
4312 case EL_BD_BUTTERFLY_LEFT:
4313 case EL_BD_BUTTERFLY_DOWN:
4314 Feld[x][y] = EL_BD_BUTTERFLY;
4315 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4318 case EL_BD_FIREFLY_RIGHT:
4319 case EL_BD_FIREFLY_UP:
4320 case EL_BD_FIREFLY_LEFT:
4321 case EL_BD_FIREFLY_DOWN:
4322 Feld[x][y] = EL_BD_FIREFLY;
4323 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4326 case EL_PACMAN_RIGHT:
4328 case EL_PACMAN_LEFT:
4329 case EL_PACMAN_DOWN:
4330 Feld[x][y] = EL_PACMAN;
4331 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4334 case EL_YAMYAM_LEFT:
4335 case EL_YAMYAM_RIGHT:
4337 case EL_YAMYAM_DOWN:
4338 Feld[x][y] = EL_YAMYAM;
4339 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4342 case EL_SP_SNIKSNAK:
4343 MovDir[x][y] = MV_UP;
4346 case EL_SP_ELECTRON:
4347 MovDir[x][y] = MV_LEFT;
4354 Feld[x][y] = EL_MOLE;
4355 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4359 if (IS_CUSTOM_ELEMENT(element))
4361 struct ElementInfo *ei = &element_info[element];
4362 int move_direction_initial = ei->move_direction_initial;
4363 int move_pattern = ei->move_pattern;
4365 if (move_direction_initial == MV_START_PREVIOUS)
4367 if (MovDir[x][y] != MV_NONE)
4370 move_direction_initial = MV_START_AUTOMATIC;
4373 if (move_direction_initial == MV_START_RANDOM)
4374 MovDir[x][y] = 1 << RND(4);
4375 else if (move_direction_initial & MV_ANY_DIRECTION)
4376 MovDir[x][y] = move_direction_initial;
4377 else if (move_pattern == MV_ALL_DIRECTIONS ||
4378 move_pattern == MV_TURNING_LEFT ||
4379 move_pattern == MV_TURNING_RIGHT ||
4380 move_pattern == MV_TURNING_LEFT_RIGHT ||
4381 move_pattern == MV_TURNING_RIGHT_LEFT ||
4382 move_pattern == MV_TURNING_RANDOM)
4383 MovDir[x][y] = 1 << RND(4);
4384 else if (move_pattern == MV_HORIZONTAL)
4385 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4386 else if (move_pattern == MV_VERTICAL)
4387 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4388 else if (move_pattern & MV_ANY_DIRECTION)
4389 MovDir[x][y] = element_info[element].move_pattern;
4390 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4391 move_pattern == MV_ALONG_RIGHT_SIDE)
4393 /* use random direction as default start direction */
4394 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4395 MovDir[x][y] = 1 << RND(4);
4397 for (i = 0; i < NUM_DIRECTIONS; i++)
4399 int x1 = x + xy[i][0];
4400 int y1 = y + xy[i][1];
4402 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4404 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4405 MovDir[x][y] = direction[0][i];
4407 MovDir[x][y] = direction[1][i];
4416 MovDir[x][y] = 1 << RND(4);
4418 if (element != EL_BUG &&
4419 element != EL_SPACESHIP &&
4420 element != EL_BD_BUTTERFLY &&
4421 element != EL_BD_FIREFLY)
4424 for (i = 0; i < NUM_DIRECTIONS; i++)
4426 int x1 = x + xy[i][0];
4427 int y1 = y + xy[i][1];
4429 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4431 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4433 MovDir[x][y] = direction[0][i];
4436 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4437 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4439 MovDir[x][y] = direction[1][i];
4448 GfxDir[x][y] = MovDir[x][y];
4451 void InitAmoebaNr(int x, int y)
4454 int group_nr = AmoebeNachbarNr(x, y);
4458 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4460 if (AmoebaCnt[i] == 0)
4468 AmoebaNr[x][y] = group_nr;
4469 AmoebaCnt[group_nr]++;
4470 AmoebaCnt2[group_nr]++;
4473 static void PlayerWins(struct PlayerInfo *player)
4475 player->LevelSolved = TRUE;
4476 player->GameOver = TRUE;
4478 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4479 level.native_em_level->lev->score :
4480 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4483 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4484 MM_HEALTH(game_mm.laser_overload_value) :
4487 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4489 player->LevelSolved_CountingScore = player->score_final;
4490 player->LevelSolved_CountingHealth = player->health_final;
4495 static int time_count_steps;
4496 static int time, time_final;
4497 static int score, score_final;
4498 static int health, health_final;
4499 static int game_over_delay_1 = 0;
4500 static int game_over_delay_2 = 0;
4501 static int game_over_delay_3 = 0;
4502 int game_over_delay_value_1 = 50;
4503 int game_over_delay_value_2 = 25;
4504 int game_over_delay_value_3 = 50;
4506 if (!local_player->LevelSolved_GameWon)
4510 /* do not start end game actions before the player stops moving (to exit) */
4511 if (local_player->MovPos)
4514 local_player->LevelSolved_GameWon = TRUE;
4515 local_player->LevelSolved_SaveTape = tape.recording;
4516 local_player->LevelSolved_SaveScore = !tape.playing;
4520 LevelStats_incSolved(level_nr);
4522 SaveLevelSetup_SeriesInfo();
4525 if (tape.auto_play) /* tape might already be stopped here */
4526 tape.auto_play_level_solved = TRUE;
4530 game_over_delay_1 = 0;
4531 game_over_delay_2 = 0;
4532 game_over_delay_3 = game_over_delay_value_3;
4534 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4535 score = score_final = local_player->score_final;
4536 health = health_final = local_player->health_final;
4538 if (level.score[SC_TIME_BONUS] > 0)
4543 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4545 else if (game.no_time_limit && TimePlayed < 999)
4548 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4551 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4553 game_over_delay_1 = game_over_delay_value_1;
4555 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4558 score_final += health * level.score[SC_TIME_BONUS];
4560 game_over_delay_2 = game_over_delay_value_2;
4563 local_player->score_final = score_final;
4564 local_player->health_final = health_final;
4567 if (level_editor_test_game)
4570 score = score_final;
4572 local_player->LevelSolved_CountingTime = time;
4573 local_player->LevelSolved_CountingScore = score;
4575 game_panel_controls[GAME_PANEL_TIME].value = time;
4576 game_panel_controls[GAME_PANEL_SCORE].value = score;
4578 DisplayGameControlValues();
4581 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4583 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4585 /* close exit door after last player */
4586 if ((AllPlayersGone &&
4587 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4588 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4589 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4590 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4591 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4593 int element = Feld[ExitX][ExitY];
4595 Feld[ExitX][ExitY] =
4596 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4597 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4598 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4599 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4600 EL_EM_STEEL_EXIT_CLOSING);
4602 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4605 /* player disappears */
4606 DrawLevelField(ExitX, ExitY);
4609 for (i = 0; i < MAX_PLAYERS; i++)
4611 struct PlayerInfo *player = &stored_player[i];
4613 if (player->present)
4615 RemovePlayer(player);
4617 /* player disappears */
4618 DrawLevelField(player->jx, player->jy);
4623 PlaySound(SND_GAME_WINNING);
4626 if (game_over_delay_1 > 0)
4628 game_over_delay_1--;
4633 if (time != time_final)
4635 int time_to_go = ABS(time_final - time);
4636 int time_count_dir = (time < time_final ? +1 : -1);
4638 if (time_to_go < time_count_steps)
4639 time_count_steps = 1;
4641 time += time_count_steps * time_count_dir;
4642 score += time_count_steps * level.score[SC_TIME_BONUS];
4644 local_player->LevelSolved_CountingTime = time;
4645 local_player->LevelSolved_CountingScore = score;
4647 game_panel_controls[GAME_PANEL_TIME].value = time;
4648 game_panel_controls[GAME_PANEL_SCORE].value = score;
4650 DisplayGameControlValues();
4652 if (time == time_final)
4653 StopSound(SND_GAME_LEVELTIME_BONUS);
4654 else if (setup.sound_loops)
4655 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4657 PlaySound(SND_GAME_LEVELTIME_BONUS);
4662 if (game_over_delay_2 > 0)
4664 game_over_delay_2--;
4669 if (health != health_final)
4671 int health_count_dir = (health < health_final ? +1 : -1);
4673 health += health_count_dir;
4674 score += level.score[SC_TIME_BONUS];
4676 local_player->LevelSolved_CountingHealth = health;
4677 local_player->LevelSolved_CountingScore = score;
4679 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4680 game_panel_controls[GAME_PANEL_SCORE].value = score;
4682 DisplayGameControlValues();
4684 if (health == health_final)
4685 StopSound(SND_GAME_LEVELTIME_BONUS);
4686 else if (setup.sound_loops)
4687 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4689 PlaySound(SND_GAME_LEVELTIME_BONUS);
4694 local_player->LevelSolved_PanelOff = TRUE;
4696 if (game_over_delay_3 > 0)
4698 game_over_delay_3--;
4709 boolean raise_level = FALSE;
4711 local_player->LevelSolved_GameEnd = TRUE;
4713 if (local_player->LevelSolved_SaveTape)
4715 /* make sure that request dialog to save tape does not open door again */
4716 if (!global.use_envelope_request)
4717 CloseDoor(DOOR_CLOSE_1);
4719 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4722 /* if no tape is to be saved, close both doors simultaneously */
4723 CloseDoor(DOOR_CLOSE_ALL);
4725 if (level_editor_test_game)
4727 SetGameStatus(GAME_MODE_MAIN);
4734 if (!local_player->LevelSolved_SaveScore)
4736 SetGameStatus(GAME_MODE_MAIN);
4743 if (level_nr == leveldir_current->handicap_level)
4745 leveldir_current->handicap_level++;
4747 SaveLevelSetup_SeriesInfo();
4750 if (setup.increment_levels &&
4751 level_nr < leveldir_current->last_level)
4752 raise_level = TRUE; /* advance to next level */
4754 if ((hi_pos = NewHiScore()) >= 0)
4756 SetGameStatus(GAME_MODE_SCORES);
4758 DrawHallOfFame(hi_pos);
4768 SetGameStatus(GAME_MODE_MAIN);
4784 boolean one_score_entry_per_name = !program.many_scores_per_name;
4786 LoadScore(level_nr);
4788 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4789 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4792 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4794 if (local_player->score_final > highscore[k].Score)
4796 /* player has made it to the hall of fame */
4798 if (k < MAX_SCORE_ENTRIES - 1)
4800 int m = MAX_SCORE_ENTRIES - 1;
4802 if (one_score_entry_per_name)
4804 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4805 if (strEqual(setup.player_name, highscore[l].Name))
4808 if (m == k) /* player's new highscore overwrites his old one */
4812 for (l = m; l > k; l--)
4814 strcpy(highscore[l].Name, highscore[l - 1].Name);
4815 highscore[l].Score = highscore[l - 1].Score;
4821 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4822 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4823 highscore[k].Score = local_player->score_final;
4828 else if (one_score_entry_per_name &&
4829 !strncmp(setup.player_name, highscore[k].Name,
4830 MAX_PLAYER_NAME_LEN))
4831 break; /* player already there with a higher score */
4835 SaveScore(level_nr);
4840 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4842 int element = Feld[x][y];
4843 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4844 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4845 int horiz_move = (dx != 0);
4846 int sign = (horiz_move ? dx : dy);
4847 int step = sign * element_info[element].move_stepsize;
4849 /* special values for move stepsize for spring and things on conveyor belt */
4852 if (CAN_FALL(element) &&
4853 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4854 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4855 else if (element == EL_SPRING)
4856 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4862 inline static int getElementMoveStepsize(int x, int y)
4864 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4867 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4869 if (player->GfxAction != action || player->GfxDir != dir)
4871 player->GfxAction = action;
4872 player->GfxDir = dir;
4874 player->StepFrame = 0;
4878 static void ResetGfxFrame(int x, int y)
4880 // profiling showed that "autotest" spends 10~20% of its time in this function
4881 if (DrawingDeactivatedField())
4884 int element = Feld[x][y];
4885 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4887 if (graphic_info[graphic].anim_global_sync)
4888 GfxFrame[x][y] = FrameCounter;
4889 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4890 GfxFrame[x][y] = CustomValue[x][y];
4891 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4892 GfxFrame[x][y] = element_info[element].collect_score;
4893 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4894 GfxFrame[x][y] = ChangeDelay[x][y];
4897 static void ResetGfxAnimation(int x, int y)
4899 GfxAction[x][y] = ACTION_DEFAULT;
4900 GfxDir[x][y] = MovDir[x][y];
4903 ResetGfxFrame(x, y);
4906 static void ResetRandomAnimationValue(int x, int y)
4908 GfxRandom[x][y] = INIT_GFX_RANDOM();
4911 void InitMovingField(int x, int y, int direction)
4913 int element = Feld[x][y];
4914 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4915 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4918 boolean is_moving_before, is_moving_after;
4920 /* check if element was/is moving or being moved before/after mode change */
4921 is_moving_before = (WasJustMoving[x][y] != 0);
4922 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4924 /* reset animation only for moving elements which change direction of moving
4925 or which just started or stopped moving
4926 (else CEs with property "can move" / "not moving" are reset each frame) */
4927 if (is_moving_before != is_moving_after ||
4928 direction != MovDir[x][y])
4929 ResetGfxAnimation(x, y);
4931 MovDir[x][y] = direction;
4932 GfxDir[x][y] = direction;
4934 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4935 direction == MV_DOWN && CAN_FALL(element) ?
4936 ACTION_FALLING : ACTION_MOVING);
4938 /* this is needed for CEs with property "can move" / "not moving" */
4940 if (is_moving_after)
4942 if (Feld[newx][newy] == EL_EMPTY)
4943 Feld[newx][newy] = EL_BLOCKED;
4945 MovDir[newx][newy] = MovDir[x][y];
4947 CustomValue[newx][newy] = CustomValue[x][y];
4949 GfxFrame[newx][newy] = GfxFrame[x][y];
4950 GfxRandom[newx][newy] = GfxRandom[x][y];
4951 GfxAction[newx][newy] = GfxAction[x][y];
4952 GfxDir[newx][newy] = GfxDir[x][y];
4956 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4958 int direction = MovDir[x][y];
4959 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4960 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4966 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4968 int oldx = x, oldy = y;
4969 int direction = MovDir[x][y];
4971 if (direction == MV_LEFT)
4973 else if (direction == MV_RIGHT)
4975 else if (direction == MV_UP)
4977 else if (direction == MV_DOWN)
4980 *comes_from_x = oldx;
4981 *comes_from_y = oldy;
4984 int MovingOrBlocked2Element(int x, int y)
4986 int element = Feld[x][y];
4988 if (element == EL_BLOCKED)
4992 Blocked2Moving(x, y, &oldx, &oldy);
4993 return Feld[oldx][oldy];
4999 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5001 /* like MovingOrBlocked2Element(), but if element is moving
5002 and (x,y) is the field the moving element is just leaving,
5003 return EL_BLOCKED instead of the element value */
5004 int element = Feld[x][y];
5006 if (IS_MOVING(x, y))
5008 if (element == EL_BLOCKED)
5012 Blocked2Moving(x, y, &oldx, &oldy);
5013 return Feld[oldx][oldy];
5022 static void RemoveField(int x, int y)
5024 Feld[x][y] = EL_EMPTY;
5030 CustomValue[x][y] = 0;
5033 ChangeDelay[x][y] = 0;
5034 ChangePage[x][y] = -1;
5035 Pushed[x][y] = FALSE;
5037 GfxElement[x][y] = EL_UNDEFINED;
5038 GfxAction[x][y] = ACTION_DEFAULT;
5039 GfxDir[x][y] = MV_NONE;
5042 void RemoveMovingField(int x, int y)
5044 int oldx = x, oldy = y, newx = x, newy = y;
5045 int element = Feld[x][y];
5046 int next_element = EL_UNDEFINED;
5048 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5051 if (IS_MOVING(x, y))
5053 Moving2Blocked(x, y, &newx, &newy);
5055 if (Feld[newx][newy] != EL_BLOCKED)
5057 /* element is moving, but target field is not free (blocked), but
5058 already occupied by something different (example: acid pool);
5059 in this case, only remove the moving field, but not the target */
5061 RemoveField(oldx, oldy);
5063 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5065 TEST_DrawLevelField(oldx, oldy);
5070 else if (element == EL_BLOCKED)
5072 Blocked2Moving(x, y, &oldx, &oldy);
5073 if (!IS_MOVING(oldx, oldy))
5077 if (element == EL_BLOCKED &&
5078 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5079 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5080 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5081 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5082 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5083 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5084 next_element = get_next_element(Feld[oldx][oldy]);
5086 RemoveField(oldx, oldy);
5087 RemoveField(newx, newy);
5089 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5091 if (next_element != EL_UNDEFINED)
5092 Feld[oldx][oldy] = next_element;
5094 TEST_DrawLevelField(oldx, oldy);
5095 TEST_DrawLevelField(newx, newy);
5098 void DrawDynamite(int x, int y)
5100 int sx = SCREENX(x), sy = SCREENY(y);
5101 int graphic = el2img(Feld[x][y]);
5104 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5107 if (IS_WALKABLE_INSIDE(Back[x][y]))
5111 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5112 else if (Store[x][y])
5113 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5115 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5117 if (Back[x][y] || Store[x][y])
5118 DrawGraphicThruMask(sx, sy, graphic, frame);
5120 DrawGraphic(sx, sy, graphic, frame);
5123 void CheckDynamite(int x, int y)
5125 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5129 if (MovDelay[x][y] != 0)
5132 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5138 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5143 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5145 boolean num_checked_players = 0;
5148 for (i = 0; i < MAX_PLAYERS; i++)
5150 if (stored_player[i].active)
5152 int sx = stored_player[i].jx;
5153 int sy = stored_player[i].jy;
5155 if (num_checked_players == 0)
5162 *sx1 = MIN(*sx1, sx);
5163 *sy1 = MIN(*sy1, sy);
5164 *sx2 = MAX(*sx2, sx);
5165 *sy2 = MAX(*sy2, sy);
5168 num_checked_players++;
5173 static boolean checkIfAllPlayersFitToScreen_RND()
5175 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5177 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5179 return (sx2 - sx1 < SCR_FIELDX &&
5180 sy2 - sy1 < SCR_FIELDY);
5183 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5185 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5187 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5189 *sx = (sx1 + sx2) / 2;
5190 *sy = (sy1 + sy2) / 2;
5193 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5194 boolean center_screen, boolean quick_relocation)
5196 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5197 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5198 boolean no_delay = (tape.warp_forward);
5199 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5200 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5201 int new_scroll_x, new_scroll_y;
5203 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5205 /* case 1: quick relocation inside visible screen (without scrolling) */
5212 if (!level.shifted_relocation || center_screen)
5214 /* relocation _with_ centering of screen */
5216 new_scroll_x = SCROLL_POSITION_X(x);
5217 new_scroll_y = SCROLL_POSITION_Y(y);
5221 /* relocation _without_ centering of screen */
5223 int center_scroll_x = SCROLL_POSITION_X(old_x);
5224 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5225 int offset_x = x + (scroll_x - center_scroll_x);
5226 int offset_y = y + (scroll_y - center_scroll_y);
5228 /* for new screen position, apply previous offset to center position */
5229 new_scroll_x = SCROLL_POSITION_X(offset_x);
5230 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5233 if (quick_relocation)
5235 /* case 2: quick relocation (redraw without visible scrolling) */
5237 scroll_x = new_scroll_x;
5238 scroll_y = new_scroll_y;
5245 /* case 3: visible relocation (with scrolling to new position) */
5247 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5249 SetVideoFrameDelay(wait_delay_value);
5251 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5254 int fx = FX, fy = FY;
5256 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5257 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5259 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5265 fx += dx * TILEX / 2;
5266 fy += dy * TILEY / 2;
5268 ScrollLevel(dx, dy);
5271 /* scroll in two steps of half tile size to make things smoother */
5272 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5274 /* scroll second step to align at full tile size */
5275 BlitScreenToBitmap(window);
5281 SetVideoFrameDelay(frame_delay_value_old);
5284 void RelocatePlayer(int jx, int jy, int el_player_raw)
5286 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5287 int player_nr = GET_PLAYER_NR(el_player);
5288 struct PlayerInfo *player = &stored_player[player_nr];
5289 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5290 boolean no_delay = (tape.warp_forward);
5291 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5292 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5293 int old_jx = player->jx;
5294 int old_jy = player->jy;
5295 int old_element = Feld[old_jx][old_jy];
5296 int element = Feld[jx][jy];
5297 boolean player_relocated = (old_jx != jx || old_jy != jy);
5299 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5300 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5301 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5302 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5303 int leave_side_horiz = move_dir_horiz;
5304 int leave_side_vert = move_dir_vert;
5305 int enter_side = enter_side_horiz | enter_side_vert;
5306 int leave_side = leave_side_horiz | leave_side_vert;
5308 if (player->GameOver) /* do not reanimate dead player */
5311 if (!player_relocated) /* no need to relocate the player */
5314 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5316 RemoveField(jx, jy); /* temporarily remove newly placed player */
5317 DrawLevelField(jx, jy);
5320 if (player->present)
5322 while (player->MovPos)
5324 ScrollPlayer(player, SCROLL_GO_ON);
5325 ScrollScreen(NULL, SCROLL_GO_ON);
5327 AdvanceFrameAndPlayerCounters(player->index_nr);
5331 BackToFront_WithFrameDelay(wait_delay_value);
5334 DrawPlayer(player); /* needed here only to cleanup last field */
5335 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5337 player->is_moving = FALSE;
5340 if (IS_CUSTOM_ELEMENT(old_element))
5341 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5343 player->index_bit, leave_side);
5345 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5347 player->index_bit, leave_side);
5349 Feld[jx][jy] = el_player;
5350 InitPlayerField(jx, jy, el_player, TRUE);
5352 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5353 possible that the relocation target field did not contain a player element,
5354 but a walkable element, to which the new player was relocated -- in this
5355 case, restore that (already initialized!) element on the player field */
5356 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5358 Feld[jx][jy] = element; /* restore previously existing element */
5361 /* only visually relocate centered player */
5362 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5363 FALSE, level.instant_relocation);
5365 TestIfPlayerTouchesBadThing(jx, jy);
5366 TestIfPlayerTouchesCustomElement(jx, jy);
5368 if (IS_CUSTOM_ELEMENT(element))
5369 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5370 player->index_bit, enter_side);
5372 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5373 player->index_bit, enter_side);
5375 if (player->is_switching)
5377 /* ensure that relocation while still switching an element does not cause
5378 a new element to be treated as also switched directly after relocation
5379 (this is important for teleporter switches that teleport the player to
5380 a place where another teleporter switch is in the same direction, which
5381 would then incorrectly be treated as immediately switched before the
5382 direction key that caused the switch was released) */
5384 player->switch_x += jx - old_jx;
5385 player->switch_y += jy - old_jy;
5389 void Explode(int ex, int ey, int phase, int mode)
5395 /* !!! eliminate this variable !!! */
5396 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5398 if (game.explosions_delayed)
5400 ExplodeField[ex][ey] = mode;
5404 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5406 int center_element = Feld[ex][ey];
5407 int artwork_element, explosion_element; /* set these values later */
5409 /* remove things displayed in background while burning dynamite */
5410 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5413 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5415 /* put moving element to center field (and let it explode there) */
5416 center_element = MovingOrBlocked2Element(ex, ey);
5417 RemoveMovingField(ex, ey);
5418 Feld[ex][ey] = center_element;
5421 /* now "center_element" is finally determined -- set related values now */
5422 artwork_element = center_element; /* for custom player artwork */
5423 explosion_element = center_element; /* for custom player artwork */
5425 if (IS_PLAYER(ex, ey))
5427 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5429 artwork_element = stored_player[player_nr].artwork_element;
5431 if (level.use_explosion_element[player_nr])
5433 explosion_element = level.explosion_element[player_nr];
5434 artwork_element = explosion_element;
5438 if (mode == EX_TYPE_NORMAL ||
5439 mode == EX_TYPE_CENTER ||
5440 mode == EX_TYPE_CROSS)
5441 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5443 last_phase = element_info[explosion_element].explosion_delay + 1;
5445 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5447 int xx = x - ex + 1;
5448 int yy = y - ey + 1;
5451 if (!IN_LEV_FIELD(x, y) ||
5452 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5453 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5456 element = Feld[x][y];
5458 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5460 element = MovingOrBlocked2Element(x, y);
5462 if (!IS_EXPLOSION_PROOF(element))
5463 RemoveMovingField(x, y);
5466 /* indestructible elements can only explode in center (but not flames) */
5467 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5468 mode == EX_TYPE_BORDER)) ||
5469 element == EL_FLAMES)
5472 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5473 behaviour, for example when touching a yamyam that explodes to rocks
5474 with active deadly shield, a rock is created under the player !!! */
5475 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5477 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5478 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5479 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5481 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5484 if (IS_ACTIVE_BOMB(element))
5486 /* re-activate things under the bomb like gate or penguin */
5487 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5494 /* save walkable background elements while explosion on same tile */
5495 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5496 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5497 Back[x][y] = element;
5499 /* ignite explodable elements reached by other explosion */
5500 if (element == EL_EXPLOSION)
5501 element = Store2[x][y];
5503 if (AmoebaNr[x][y] &&
5504 (element == EL_AMOEBA_FULL ||
5505 element == EL_BD_AMOEBA ||
5506 element == EL_AMOEBA_GROWING))
5508 AmoebaCnt[AmoebaNr[x][y]]--;
5509 AmoebaCnt2[AmoebaNr[x][y]]--;
5514 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5516 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5518 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5520 if (PLAYERINFO(ex, ey)->use_murphy)
5521 Store[x][y] = EL_EMPTY;
5524 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5525 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5526 else if (ELEM_IS_PLAYER(center_element))
5527 Store[x][y] = EL_EMPTY;
5528 else if (center_element == EL_YAMYAM)
5529 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5530 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5531 Store[x][y] = element_info[center_element].content.e[xx][yy];
5533 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5534 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5535 otherwise) -- FIX THIS !!! */
5536 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5537 Store[x][y] = element_info[element].content.e[1][1];
5539 else if (!CAN_EXPLODE(element))
5540 Store[x][y] = element_info[element].content.e[1][1];
5543 Store[x][y] = EL_EMPTY;
5545 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5546 center_element == EL_AMOEBA_TO_DIAMOND)
5547 Store2[x][y] = element;
5549 Feld[x][y] = EL_EXPLOSION;
5550 GfxElement[x][y] = artwork_element;
5552 ExplodePhase[x][y] = 1;
5553 ExplodeDelay[x][y] = last_phase;
5558 if (center_element == EL_YAMYAM)
5559 game.yamyam_content_nr =
5560 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5572 GfxFrame[x][y] = 0; /* restart explosion animation */
5574 last_phase = ExplodeDelay[x][y];
5576 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5578 /* this can happen if the player leaves an explosion just in time */
5579 if (GfxElement[x][y] == EL_UNDEFINED)
5580 GfxElement[x][y] = EL_EMPTY;
5582 border_element = Store2[x][y];
5583 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5584 border_element = StorePlayer[x][y];
5586 if (phase == element_info[border_element].ignition_delay ||
5587 phase == last_phase)
5589 boolean border_explosion = FALSE;
5591 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5592 !PLAYER_EXPLOSION_PROTECTED(x, y))
5594 KillPlayerUnlessExplosionProtected(x, y);
5595 border_explosion = TRUE;
5597 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5599 Feld[x][y] = Store2[x][y];
5602 border_explosion = TRUE;
5604 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5606 AmoebeUmwandeln(x, y);
5608 border_explosion = TRUE;
5611 /* if an element just explodes due to another explosion (chain-reaction),
5612 do not immediately end the new explosion when it was the last frame of
5613 the explosion (as it would be done in the following "if"-statement!) */
5614 if (border_explosion && phase == last_phase)
5618 if (phase == last_phase)
5622 element = Feld[x][y] = Store[x][y];
5623 Store[x][y] = Store2[x][y] = 0;
5624 GfxElement[x][y] = EL_UNDEFINED;
5626 /* player can escape from explosions and might therefore be still alive */
5627 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5628 element <= EL_PLAYER_IS_EXPLODING_4)
5630 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5631 int explosion_element = EL_PLAYER_1 + player_nr;
5632 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5633 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5635 if (level.use_explosion_element[player_nr])
5636 explosion_element = level.explosion_element[player_nr];
5638 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5639 element_info[explosion_element].content.e[xx][yy]);
5642 /* restore probably existing indestructible background element */
5643 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5644 element = Feld[x][y] = Back[x][y];
5647 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5648 GfxDir[x][y] = MV_NONE;
5649 ChangeDelay[x][y] = 0;
5650 ChangePage[x][y] = -1;
5652 CustomValue[x][y] = 0;
5654 InitField_WithBug2(x, y, FALSE);
5656 TEST_DrawLevelField(x, y);
5658 TestIfElementTouchesCustomElement(x, y);
5660 if (GFX_CRUMBLED(element))
5661 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5663 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5664 StorePlayer[x][y] = 0;
5666 if (ELEM_IS_PLAYER(element))
5667 RelocatePlayer(x, y, element);
5669 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5671 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5672 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5675 TEST_DrawLevelFieldCrumbled(x, y);
5677 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5679 DrawLevelElement(x, y, Back[x][y]);
5680 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5682 else if (IS_WALKABLE_UNDER(Back[x][y]))
5684 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5685 DrawLevelElementThruMask(x, y, Back[x][y]);
5687 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5688 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5692 void DynaExplode(int ex, int ey)
5695 int dynabomb_element = Feld[ex][ey];
5696 int dynabomb_size = 1;
5697 boolean dynabomb_xl = FALSE;
5698 struct PlayerInfo *player;
5699 static int xy[4][2] =
5707 if (IS_ACTIVE_BOMB(dynabomb_element))
5709 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5710 dynabomb_size = player->dynabomb_size;
5711 dynabomb_xl = player->dynabomb_xl;
5712 player->dynabombs_left++;
5715 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5717 for (i = 0; i < NUM_DIRECTIONS; i++)
5719 for (j = 1; j <= dynabomb_size; j++)
5721 int x = ex + j * xy[i][0];
5722 int y = ey + j * xy[i][1];
5725 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5728 element = Feld[x][y];
5730 /* do not restart explosions of fields with active bombs */
5731 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5734 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5736 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5737 !IS_DIGGABLE(element) && !dynabomb_xl)
5743 void Bang(int x, int y)
5745 int element = MovingOrBlocked2Element(x, y);
5746 int explosion_type = EX_TYPE_NORMAL;
5748 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5750 struct PlayerInfo *player = PLAYERINFO(x, y);
5752 element = Feld[x][y] = player->initial_element;
5754 if (level.use_explosion_element[player->index_nr])
5756 int explosion_element = level.explosion_element[player->index_nr];
5758 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5759 explosion_type = EX_TYPE_CROSS;
5760 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5761 explosion_type = EX_TYPE_CENTER;
5769 case EL_BD_BUTTERFLY:
5772 case EL_DARK_YAMYAM:
5776 RaiseScoreElement(element);
5779 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5780 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5781 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5782 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5783 case EL_DYNABOMB_INCREASE_NUMBER:
5784 case EL_DYNABOMB_INCREASE_SIZE:
5785 case EL_DYNABOMB_INCREASE_POWER:
5786 explosion_type = EX_TYPE_DYNA;
5789 case EL_DC_LANDMINE:
5790 explosion_type = EX_TYPE_CENTER;
5795 case EL_LAMP_ACTIVE:
5796 case EL_AMOEBA_TO_DIAMOND:
5797 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5798 explosion_type = EX_TYPE_CENTER;
5802 if (element_info[element].explosion_type == EXPLODES_CROSS)
5803 explosion_type = EX_TYPE_CROSS;
5804 else if (element_info[element].explosion_type == EXPLODES_1X1)
5805 explosion_type = EX_TYPE_CENTER;
5809 if (explosion_type == EX_TYPE_DYNA)
5812 Explode(x, y, EX_PHASE_START, explosion_type);
5814 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5817 void SplashAcid(int x, int y)
5819 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5820 (!IN_LEV_FIELD(x - 1, y - 2) ||
5821 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5822 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5824 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5825 (!IN_LEV_FIELD(x + 1, y - 2) ||
5826 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5827 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5829 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5832 static void InitBeltMovement()
5834 static int belt_base_element[4] =
5836 EL_CONVEYOR_BELT_1_LEFT,
5837 EL_CONVEYOR_BELT_2_LEFT,
5838 EL_CONVEYOR_BELT_3_LEFT,
5839 EL_CONVEYOR_BELT_4_LEFT
5841 static int belt_base_active_element[4] =
5843 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5844 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5845 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5846 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5851 /* set frame order for belt animation graphic according to belt direction */
5852 for (i = 0; i < NUM_BELTS; i++)
5856 for (j = 0; j < NUM_BELT_PARTS; j++)
5858 int element = belt_base_active_element[belt_nr] + j;
5859 int graphic_1 = el2img(element);
5860 int graphic_2 = el2panelimg(element);
5862 if (game.belt_dir[i] == MV_LEFT)
5864 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5865 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5869 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5870 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5875 SCAN_PLAYFIELD(x, y)
5877 int element = Feld[x][y];
5879 for (i = 0; i < NUM_BELTS; i++)
5881 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5883 int e_belt_nr = getBeltNrFromBeltElement(element);
5886 if (e_belt_nr == belt_nr)
5888 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5890 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5897 static void ToggleBeltSwitch(int x, int y)
5899 static int belt_base_element[4] =
5901 EL_CONVEYOR_BELT_1_LEFT,
5902 EL_CONVEYOR_BELT_2_LEFT,
5903 EL_CONVEYOR_BELT_3_LEFT,
5904 EL_CONVEYOR_BELT_4_LEFT
5906 static int belt_base_active_element[4] =
5908 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5909 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5910 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5911 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5913 static int belt_base_switch_element[4] =
5915 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5916 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5917 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5918 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5920 static int belt_move_dir[4] =
5928 int element = Feld[x][y];
5929 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5930 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5931 int belt_dir = belt_move_dir[belt_dir_nr];
5934 if (!IS_BELT_SWITCH(element))
5937 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5938 game.belt_dir[belt_nr] = belt_dir;
5940 if (belt_dir_nr == 3)
5943 /* set frame order for belt animation graphic according to belt direction */
5944 for (i = 0; i < NUM_BELT_PARTS; i++)
5946 int element = belt_base_active_element[belt_nr] + i;
5947 int graphic_1 = el2img(element);
5948 int graphic_2 = el2panelimg(element);
5950 if (belt_dir == MV_LEFT)
5952 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5953 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5957 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5958 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5962 SCAN_PLAYFIELD(xx, yy)
5964 int element = Feld[xx][yy];
5966 if (IS_BELT_SWITCH(element))
5968 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5970 if (e_belt_nr == belt_nr)
5972 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5973 TEST_DrawLevelField(xx, yy);
5976 else if (IS_BELT(element) && belt_dir != MV_NONE)
5978 int e_belt_nr = getBeltNrFromBeltElement(element);
5980 if (e_belt_nr == belt_nr)
5982 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5984 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5985 TEST_DrawLevelField(xx, yy);
5988 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5990 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5992 if (e_belt_nr == belt_nr)
5994 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5996 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5997 TEST_DrawLevelField(xx, yy);
6003 static void ToggleSwitchgateSwitch(int x, int y)
6007 game.switchgate_pos = !game.switchgate_pos;
6009 SCAN_PLAYFIELD(xx, yy)
6011 int element = Feld[xx][yy];
6013 if (element == EL_SWITCHGATE_SWITCH_UP)
6015 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6016 TEST_DrawLevelField(xx, yy);
6018 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6020 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6021 TEST_DrawLevelField(xx, yy);
6023 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6025 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6026 TEST_DrawLevelField(xx, yy);
6028 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6030 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6031 TEST_DrawLevelField(xx, yy);
6033 else if (element == EL_SWITCHGATE_OPEN ||
6034 element == EL_SWITCHGATE_OPENING)
6036 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6038 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6040 else if (element == EL_SWITCHGATE_CLOSED ||
6041 element == EL_SWITCHGATE_CLOSING)
6043 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6045 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6050 static int getInvisibleActiveFromInvisibleElement(int element)
6052 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6053 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6054 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6058 static int getInvisibleFromInvisibleActiveElement(int element)
6060 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6061 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6062 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6066 static void RedrawAllLightSwitchesAndInvisibleElements()
6070 SCAN_PLAYFIELD(x, y)
6072 int element = Feld[x][y];
6074 if (element == EL_LIGHT_SWITCH &&
6075 game.light_time_left > 0)
6077 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6078 TEST_DrawLevelField(x, y);
6080 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6081 game.light_time_left == 0)
6083 Feld[x][y] = EL_LIGHT_SWITCH;
6084 TEST_DrawLevelField(x, y);
6086 else if (element == EL_EMC_DRIPPER &&
6087 game.light_time_left > 0)
6089 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6090 TEST_DrawLevelField(x, y);
6092 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6093 game.light_time_left == 0)
6095 Feld[x][y] = EL_EMC_DRIPPER;
6096 TEST_DrawLevelField(x, y);
6098 else if (element == EL_INVISIBLE_STEELWALL ||
6099 element == EL_INVISIBLE_WALL ||
6100 element == EL_INVISIBLE_SAND)
6102 if (game.light_time_left > 0)
6103 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6105 TEST_DrawLevelField(x, y);
6107 /* uncrumble neighbour fields, if needed */
6108 if (element == EL_INVISIBLE_SAND)
6109 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6111 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6112 element == EL_INVISIBLE_WALL_ACTIVE ||
6113 element == EL_INVISIBLE_SAND_ACTIVE)
6115 if (game.light_time_left == 0)
6116 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6118 TEST_DrawLevelField(x, y);
6120 /* re-crumble neighbour fields, if needed */
6121 if (element == EL_INVISIBLE_SAND)
6122 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6127 static void RedrawAllInvisibleElementsForLenses()
6131 SCAN_PLAYFIELD(x, y)
6133 int element = Feld[x][y];
6135 if (element == EL_EMC_DRIPPER &&
6136 game.lenses_time_left > 0)
6138 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6139 TEST_DrawLevelField(x, y);
6141 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6142 game.lenses_time_left == 0)
6144 Feld[x][y] = EL_EMC_DRIPPER;
6145 TEST_DrawLevelField(x, y);
6147 else if (element == EL_INVISIBLE_STEELWALL ||
6148 element == EL_INVISIBLE_WALL ||
6149 element == EL_INVISIBLE_SAND)
6151 if (game.lenses_time_left > 0)
6152 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6154 TEST_DrawLevelField(x, y);
6156 /* uncrumble neighbour fields, if needed */
6157 if (element == EL_INVISIBLE_SAND)
6158 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6160 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6161 element == EL_INVISIBLE_WALL_ACTIVE ||
6162 element == EL_INVISIBLE_SAND_ACTIVE)
6164 if (game.lenses_time_left == 0)
6165 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6167 TEST_DrawLevelField(x, y);
6169 /* re-crumble neighbour fields, if needed */
6170 if (element == EL_INVISIBLE_SAND)
6171 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6176 static void RedrawAllInvisibleElementsForMagnifier()
6180 SCAN_PLAYFIELD(x, y)
6182 int element = Feld[x][y];
6184 if (element == EL_EMC_FAKE_GRASS &&
6185 game.magnify_time_left > 0)
6187 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6188 TEST_DrawLevelField(x, y);
6190 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6191 game.magnify_time_left == 0)
6193 Feld[x][y] = EL_EMC_FAKE_GRASS;
6194 TEST_DrawLevelField(x, y);
6196 else if (IS_GATE_GRAY(element) &&
6197 game.magnify_time_left > 0)
6199 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6200 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6201 IS_EM_GATE_GRAY(element) ?
6202 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6203 IS_EMC_GATE_GRAY(element) ?
6204 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6205 IS_DC_GATE_GRAY(element) ?
6206 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6208 TEST_DrawLevelField(x, y);
6210 else if (IS_GATE_GRAY_ACTIVE(element) &&
6211 game.magnify_time_left == 0)
6213 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6214 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6215 IS_EM_GATE_GRAY_ACTIVE(element) ?
6216 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6217 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6218 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6219 IS_DC_GATE_GRAY_ACTIVE(element) ?
6220 EL_DC_GATE_WHITE_GRAY :
6222 TEST_DrawLevelField(x, y);
6227 static void ToggleLightSwitch(int x, int y)
6229 int element = Feld[x][y];
6231 game.light_time_left =
6232 (element == EL_LIGHT_SWITCH ?
6233 level.time_light * FRAMES_PER_SECOND : 0);
6235 RedrawAllLightSwitchesAndInvisibleElements();
6238 static void ActivateTimegateSwitch(int x, int y)
6242 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6244 SCAN_PLAYFIELD(xx, yy)
6246 int element = Feld[xx][yy];
6248 if (element == EL_TIMEGATE_CLOSED ||
6249 element == EL_TIMEGATE_CLOSING)
6251 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6252 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6256 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6258 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6259 TEST_DrawLevelField(xx, yy);
6265 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6266 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6269 void Impact(int x, int y)
6271 boolean last_line = (y == lev_fieldy - 1);
6272 boolean object_hit = FALSE;
6273 boolean impact = (last_line || object_hit);
6274 int element = Feld[x][y];
6275 int smashed = EL_STEELWALL;
6277 if (!last_line) /* check if element below was hit */
6279 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6282 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6283 MovDir[x][y + 1] != MV_DOWN ||
6284 MovPos[x][y + 1] <= TILEY / 2));
6286 /* do not smash moving elements that left the smashed field in time */
6287 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6288 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6291 #if USE_QUICKSAND_IMPACT_BUGFIX
6292 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6294 RemoveMovingField(x, y + 1);
6295 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6296 Feld[x][y + 2] = EL_ROCK;
6297 TEST_DrawLevelField(x, y + 2);
6302 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6304 RemoveMovingField(x, y + 1);
6305 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6306 Feld[x][y + 2] = EL_ROCK;
6307 TEST_DrawLevelField(x, y + 2);
6314 smashed = MovingOrBlocked2Element(x, y + 1);
6316 impact = (last_line || object_hit);
6319 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6321 SplashAcid(x, y + 1);
6325 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6326 /* only reset graphic animation if graphic really changes after impact */
6328 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6330 ResetGfxAnimation(x, y);
6331 TEST_DrawLevelField(x, y);
6334 if (impact && CAN_EXPLODE_IMPACT(element))
6339 else if (impact && element == EL_PEARL &&
6340 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6342 ResetGfxAnimation(x, y);
6344 Feld[x][y] = EL_PEARL_BREAKING;
6345 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6348 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6350 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6355 if (impact && element == EL_AMOEBA_DROP)
6357 if (object_hit && IS_PLAYER(x, y + 1))
6358 KillPlayerUnlessEnemyProtected(x, y + 1);
6359 else if (object_hit && smashed == EL_PENGUIN)
6363 Feld[x][y] = EL_AMOEBA_GROWING;
6364 Store[x][y] = EL_AMOEBA_WET;
6366 ResetRandomAnimationValue(x, y);
6371 if (object_hit) /* check which object was hit */
6373 if ((CAN_PASS_MAGIC_WALL(element) &&
6374 (smashed == EL_MAGIC_WALL ||
6375 smashed == EL_BD_MAGIC_WALL)) ||
6376 (CAN_PASS_DC_MAGIC_WALL(element) &&
6377 smashed == EL_DC_MAGIC_WALL))
6380 int activated_magic_wall =
6381 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6382 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6383 EL_DC_MAGIC_WALL_ACTIVE);
6385 /* activate magic wall / mill */
6386 SCAN_PLAYFIELD(xx, yy)
6388 if (Feld[xx][yy] == smashed)
6389 Feld[xx][yy] = activated_magic_wall;
6392 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6393 game.magic_wall_active = TRUE;
6395 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6396 SND_MAGIC_WALL_ACTIVATING :
6397 smashed == EL_BD_MAGIC_WALL ?
6398 SND_BD_MAGIC_WALL_ACTIVATING :
6399 SND_DC_MAGIC_WALL_ACTIVATING));
6402 if (IS_PLAYER(x, y + 1))
6404 if (CAN_SMASH_PLAYER(element))
6406 KillPlayerUnlessEnemyProtected(x, y + 1);
6410 else if (smashed == EL_PENGUIN)
6412 if (CAN_SMASH_PLAYER(element))
6418 else if (element == EL_BD_DIAMOND)
6420 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6426 else if (((element == EL_SP_INFOTRON ||
6427 element == EL_SP_ZONK) &&
6428 (smashed == EL_SP_SNIKSNAK ||
6429 smashed == EL_SP_ELECTRON ||
6430 smashed == EL_SP_DISK_ORANGE)) ||
6431 (element == EL_SP_INFOTRON &&
6432 smashed == EL_SP_DISK_YELLOW))
6437 else if (CAN_SMASH_EVERYTHING(element))
6439 if (IS_CLASSIC_ENEMY(smashed) ||
6440 CAN_EXPLODE_SMASHED(smashed))
6445 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6447 if (smashed == EL_LAMP ||
6448 smashed == EL_LAMP_ACTIVE)
6453 else if (smashed == EL_NUT)
6455 Feld[x][y + 1] = EL_NUT_BREAKING;
6456 PlayLevelSound(x, y, SND_NUT_BREAKING);
6457 RaiseScoreElement(EL_NUT);
6460 else if (smashed == EL_PEARL)
6462 ResetGfxAnimation(x, y);
6464 Feld[x][y + 1] = EL_PEARL_BREAKING;
6465 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6468 else if (smashed == EL_DIAMOND)
6470 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6471 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6474 else if (IS_BELT_SWITCH(smashed))
6476 ToggleBeltSwitch(x, y + 1);
6478 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6479 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6480 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6481 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6483 ToggleSwitchgateSwitch(x, y + 1);
6485 else if (smashed == EL_LIGHT_SWITCH ||
6486 smashed == EL_LIGHT_SWITCH_ACTIVE)
6488 ToggleLightSwitch(x, y + 1);
6492 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6494 CheckElementChangeBySide(x, y + 1, smashed, element,
6495 CE_SWITCHED, CH_SIDE_TOP);
6496 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6502 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6507 /* play sound of magic wall / mill */
6509 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6510 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6511 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6513 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6514 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6515 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6516 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6517 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6518 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6523 /* play sound of object that hits the ground */
6524 if (last_line || object_hit)
6525 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6528 inline static void TurnRoundExt(int x, int y)
6540 { 0, 0 }, { 0, 0 }, { 0, 0 },
6545 int left, right, back;
6549 { MV_DOWN, MV_UP, MV_RIGHT },
6550 { MV_UP, MV_DOWN, MV_LEFT },
6552 { MV_LEFT, MV_RIGHT, MV_DOWN },
6556 { MV_RIGHT, MV_LEFT, MV_UP }
6559 int element = Feld[x][y];
6560 int move_pattern = element_info[element].move_pattern;
6562 int old_move_dir = MovDir[x][y];
6563 int left_dir = turn[old_move_dir].left;
6564 int right_dir = turn[old_move_dir].right;
6565 int back_dir = turn[old_move_dir].back;
6567 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6568 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6569 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6570 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6572 int left_x = x + left_dx, left_y = y + left_dy;
6573 int right_x = x + right_dx, right_y = y + right_dy;
6574 int move_x = x + move_dx, move_y = y + move_dy;
6578 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6580 TestIfBadThingTouchesOtherBadThing(x, y);
6582 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6583 MovDir[x][y] = right_dir;
6584 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6585 MovDir[x][y] = left_dir;
6587 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6589 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6592 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6594 TestIfBadThingTouchesOtherBadThing(x, y);
6596 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6597 MovDir[x][y] = left_dir;
6598 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6599 MovDir[x][y] = right_dir;
6601 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6603 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6606 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6608 TestIfBadThingTouchesOtherBadThing(x, y);
6610 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6611 MovDir[x][y] = left_dir;
6612 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6613 MovDir[x][y] = right_dir;
6615 if (MovDir[x][y] != old_move_dir)
6618 else if (element == EL_YAMYAM)
6620 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6621 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6623 if (can_turn_left && can_turn_right)
6624 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6625 else if (can_turn_left)
6626 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6627 else if (can_turn_right)
6628 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6630 MovDir[x][y] = back_dir;
6632 MovDelay[x][y] = 16 + 16 * RND(3);
6634 else if (element == EL_DARK_YAMYAM)
6636 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6638 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6641 if (can_turn_left && can_turn_right)
6642 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6643 else if (can_turn_left)
6644 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6645 else if (can_turn_right)
6646 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6648 MovDir[x][y] = back_dir;
6650 MovDelay[x][y] = 16 + 16 * RND(3);
6652 else if (element == EL_PACMAN)
6654 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6655 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6657 if (can_turn_left && can_turn_right)
6658 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6659 else if (can_turn_left)
6660 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6661 else if (can_turn_right)
6662 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6664 MovDir[x][y] = back_dir;
6666 MovDelay[x][y] = 6 + RND(40);
6668 else if (element == EL_PIG)
6670 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6671 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6672 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6673 boolean should_turn_left, should_turn_right, should_move_on;
6675 int rnd = RND(rnd_value);
6677 should_turn_left = (can_turn_left &&
6679 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6680 y + back_dy + left_dy)));
6681 should_turn_right = (can_turn_right &&
6683 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6684 y + back_dy + right_dy)));
6685 should_move_on = (can_move_on &&
6688 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6689 y + move_dy + left_dy) ||
6690 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6691 y + move_dy + right_dy)));
6693 if (should_turn_left || should_turn_right || should_move_on)
6695 if (should_turn_left && should_turn_right && should_move_on)
6696 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6697 rnd < 2 * rnd_value / 3 ? right_dir :
6699 else if (should_turn_left && should_turn_right)
6700 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6701 else if (should_turn_left && should_move_on)
6702 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6703 else if (should_turn_right && should_move_on)
6704 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6705 else if (should_turn_left)
6706 MovDir[x][y] = left_dir;
6707 else if (should_turn_right)
6708 MovDir[x][y] = right_dir;
6709 else if (should_move_on)
6710 MovDir[x][y] = old_move_dir;
6712 else if (can_move_on && rnd > rnd_value / 8)
6713 MovDir[x][y] = old_move_dir;
6714 else if (can_turn_left && can_turn_right)
6715 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6716 else if (can_turn_left && rnd > rnd_value / 8)
6717 MovDir[x][y] = left_dir;
6718 else if (can_turn_right && rnd > rnd_value/8)
6719 MovDir[x][y] = right_dir;
6721 MovDir[x][y] = back_dir;
6723 xx = x + move_xy[MovDir[x][y]].dx;
6724 yy = y + move_xy[MovDir[x][y]].dy;
6726 if (!IN_LEV_FIELD(xx, yy) ||
6727 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6728 MovDir[x][y] = old_move_dir;
6732 else if (element == EL_DRAGON)
6734 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6735 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6736 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6738 int rnd = RND(rnd_value);
6740 if (can_move_on && rnd > rnd_value / 8)
6741 MovDir[x][y] = old_move_dir;
6742 else if (can_turn_left && can_turn_right)
6743 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6744 else if (can_turn_left && rnd > rnd_value / 8)
6745 MovDir[x][y] = left_dir;
6746 else if (can_turn_right && rnd > rnd_value / 8)
6747 MovDir[x][y] = right_dir;
6749 MovDir[x][y] = back_dir;
6751 xx = x + move_xy[MovDir[x][y]].dx;
6752 yy = y + move_xy[MovDir[x][y]].dy;
6754 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6755 MovDir[x][y] = old_move_dir;
6759 else if (element == EL_MOLE)
6761 boolean can_move_on =
6762 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6763 IS_AMOEBOID(Feld[move_x][move_y]) ||
6764 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6767 boolean can_turn_left =
6768 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6769 IS_AMOEBOID(Feld[left_x][left_y])));
6771 boolean can_turn_right =
6772 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6773 IS_AMOEBOID(Feld[right_x][right_y])));
6775 if (can_turn_left && can_turn_right)
6776 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6777 else if (can_turn_left)
6778 MovDir[x][y] = left_dir;
6780 MovDir[x][y] = right_dir;
6783 if (MovDir[x][y] != old_move_dir)
6786 else if (element == EL_BALLOON)
6788 MovDir[x][y] = game.wind_direction;
6791 else if (element == EL_SPRING)
6793 if (MovDir[x][y] & MV_HORIZONTAL)
6795 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6796 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6798 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6799 ResetGfxAnimation(move_x, move_y);
6800 TEST_DrawLevelField(move_x, move_y);
6802 MovDir[x][y] = back_dir;
6804 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6805 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6806 MovDir[x][y] = MV_NONE;
6811 else if (element == EL_ROBOT ||
6812 element == EL_SATELLITE ||
6813 element == EL_PENGUIN ||
6814 element == EL_EMC_ANDROID)
6816 int attr_x = -1, attr_y = -1;
6827 for (i = 0; i < MAX_PLAYERS; i++)
6829 struct PlayerInfo *player = &stored_player[i];
6830 int jx = player->jx, jy = player->jy;
6832 if (!player->active)
6836 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6844 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6845 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6846 game.engine_version < VERSION_IDENT(3,1,0,0)))
6852 if (element == EL_PENGUIN)
6855 static int xy[4][2] =
6863 for (i = 0; i < NUM_DIRECTIONS; i++)
6865 int ex = x + xy[i][0];
6866 int ey = y + xy[i][1];
6868 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6869 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6870 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6871 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6880 MovDir[x][y] = MV_NONE;
6882 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6883 else if (attr_x > x)
6884 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6886 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6887 else if (attr_y > y)
6888 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6890 if (element == EL_ROBOT)
6894 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6895 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6896 Moving2Blocked(x, y, &newx, &newy);
6898 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6899 MovDelay[x][y] = 8 + 8 * !RND(3);
6901 MovDelay[x][y] = 16;
6903 else if (element == EL_PENGUIN)
6909 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6911 boolean first_horiz = RND(2);
6912 int new_move_dir = MovDir[x][y];
6915 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6916 Moving2Blocked(x, y, &newx, &newy);
6918 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6922 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6923 Moving2Blocked(x, y, &newx, &newy);
6925 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6928 MovDir[x][y] = old_move_dir;
6932 else if (element == EL_SATELLITE)
6938 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6940 boolean first_horiz = RND(2);
6941 int new_move_dir = MovDir[x][y];
6944 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6945 Moving2Blocked(x, y, &newx, &newy);
6947 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6951 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6952 Moving2Blocked(x, y, &newx, &newy);
6954 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6957 MovDir[x][y] = old_move_dir;
6961 else if (element == EL_EMC_ANDROID)
6963 static int check_pos[16] =
6965 -1, /* 0 => (invalid) */
6966 7, /* 1 => MV_LEFT */
6967 3, /* 2 => MV_RIGHT */
6968 -1, /* 3 => (invalid) */
6970 0, /* 5 => MV_LEFT | MV_UP */
6971 2, /* 6 => MV_RIGHT | MV_UP */
6972 -1, /* 7 => (invalid) */
6973 5, /* 8 => MV_DOWN */
6974 6, /* 9 => MV_LEFT | MV_DOWN */
6975 4, /* 10 => MV_RIGHT | MV_DOWN */
6976 -1, /* 11 => (invalid) */
6977 -1, /* 12 => (invalid) */
6978 -1, /* 13 => (invalid) */
6979 -1, /* 14 => (invalid) */
6980 -1, /* 15 => (invalid) */
6988 { -1, -1, MV_LEFT | MV_UP },
6990 { +1, -1, MV_RIGHT | MV_UP },
6991 { +1, 0, MV_RIGHT },
6992 { +1, +1, MV_RIGHT | MV_DOWN },
6994 { -1, +1, MV_LEFT | MV_DOWN },
6997 int start_pos, check_order;
6998 boolean can_clone = FALSE;
7001 /* check if there is any free field around current position */
7002 for (i = 0; i < 8; i++)
7004 int newx = x + check_xy[i].dx;
7005 int newy = y + check_xy[i].dy;
7007 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7015 if (can_clone) /* randomly find an element to clone */
7019 start_pos = check_pos[RND(8)];
7020 check_order = (RND(2) ? -1 : +1);
7022 for (i = 0; i < 8; i++)
7024 int pos_raw = start_pos + i * check_order;
7025 int pos = (pos_raw + 8) % 8;
7026 int newx = x + check_xy[pos].dx;
7027 int newy = y + check_xy[pos].dy;
7029 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7031 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7032 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7034 Store[x][y] = Feld[newx][newy];
7043 if (can_clone) /* randomly find a direction to move */
7047 start_pos = check_pos[RND(8)];
7048 check_order = (RND(2) ? -1 : +1);
7050 for (i = 0; i < 8; i++)
7052 int pos_raw = start_pos + i * check_order;
7053 int pos = (pos_raw + 8) % 8;
7054 int newx = x + check_xy[pos].dx;
7055 int newy = y + check_xy[pos].dy;
7056 int new_move_dir = check_xy[pos].dir;
7058 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7060 MovDir[x][y] = new_move_dir;
7061 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7070 if (can_clone) /* cloning and moving successful */
7073 /* cannot clone -- try to move towards player */
7075 start_pos = check_pos[MovDir[x][y] & 0x0f];
7076 check_order = (RND(2) ? -1 : +1);
7078 for (i = 0; i < 3; i++)
7080 /* first check start_pos, then previous/next or (next/previous) pos */
7081 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7082 int pos = (pos_raw + 8) % 8;
7083 int newx = x + check_xy[pos].dx;
7084 int newy = y + check_xy[pos].dy;
7085 int new_move_dir = check_xy[pos].dir;
7087 if (IS_PLAYER(newx, newy))
7090 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7092 MovDir[x][y] = new_move_dir;
7093 MovDelay[x][y] = level.android_move_time * 8 + 1;
7100 else if (move_pattern == MV_TURNING_LEFT ||
7101 move_pattern == MV_TURNING_RIGHT ||
7102 move_pattern == MV_TURNING_LEFT_RIGHT ||
7103 move_pattern == MV_TURNING_RIGHT_LEFT ||
7104 move_pattern == MV_TURNING_RANDOM ||
7105 move_pattern == MV_ALL_DIRECTIONS)
7107 boolean can_turn_left =
7108 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7109 boolean can_turn_right =
7110 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7112 if (element_info[element].move_stepsize == 0) /* "not moving" */
7115 if (move_pattern == MV_TURNING_LEFT)
7116 MovDir[x][y] = left_dir;
7117 else if (move_pattern == MV_TURNING_RIGHT)
7118 MovDir[x][y] = right_dir;
7119 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7120 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7121 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7122 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7123 else if (move_pattern == MV_TURNING_RANDOM)
7124 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7125 can_turn_right && !can_turn_left ? right_dir :
7126 RND(2) ? left_dir : right_dir);
7127 else if (can_turn_left && can_turn_right)
7128 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7129 else if (can_turn_left)
7130 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7131 else if (can_turn_right)
7132 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7134 MovDir[x][y] = back_dir;
7136 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7138 else if (move_pattern == MV_HORIZONTAL ||
7139 move_pattern == MV_VERTICAL)
7141 if (move_pattern & old_move_dir)
7142 MovDir[x][y] = back_dir;
7143 else if (move_pattern == MV_HORIZONTAL)
7144 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7145 else if (move_pattern == MV_VERTICAL)
7146 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7148 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7150 else if (move_pattern & MV_ANY_DIRECTION)
7152 MovDir[x][y] = move_pattern;
7153 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7155 else if (move_pattern & MV_WIND_DIRECTION)
7157 MovDir[x][y] = game.wind_direction;
7158 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7160 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7162 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7163 MovDir[x][y] = left_dir;
7164 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7165 MovDir[x][y] = right_dir;
7167 if (MovDir[x][y] != old_move_dir)
7168 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7170 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7172 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7173 MovDir[x][y] = right_dir;
7174 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7175 MovDir[x][y] = left_dir;
7177 if (MovDir[x][y] != old_move_dir)
7178 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7180 else if (move_pattern == MV_TOWARDS_PLAYER ||
7181 move_pattern == MV_AWAY_FROM_PLAYER)
7183 int attr_x = -1, attr_y = -1;
7185 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7196 for (i = 0; i < MAX_PLAYERS; i++)
7198 struct PlayerInfo *player = &stored_player[i];
7199 int jx = player->jx, jy = player->jy;
7201 if (!player->active)
7205 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7213 MovDir[x][y] = MV_NONE;
7215 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7216 else if (attr_x > x)
7217 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7219 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7220 else if (attr_y > y)
7221 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7223 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7225 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7227 boolean first_horiz = RND(2);
7228 int new_move_dir = MovDir[x][y];
7230 if (element_info[element].move_stepsize == 0) /* "not moving" */
7232 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7233 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7239 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7240 Moving2Blocked(x, y, &newx, &newy);
7242 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7246 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7247 Moving2Blocked(x, y, &newx, &newy);
7249 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7252 MovDir[x][y] = old_move_dir;
7255 else if (move_pattern == MV_WHEN_PUSHED ||
7256 move_pattern == MV_WHEN_DROPPED)
7258 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7259 MovDir[x][y] = MV_NONE;
7263 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7265 static int test_xy[7][2] =
7275 static int test_dir[7] =
7285 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7286 int move_preference = -1000000; /* start with very low preference */
7287 int new_move_dir = MV_NONE;
7288 int start_test = RND(4);
7291 for (i = 0; i < NUM_DIRECTIONS; i++)
7293 int move_dir = test_dir[start_test + i];
7294 int move_dir_preference;
7296 xx = x + test_xy[start_test + i][0];
7297 yy = y + test_xy[start_test + i][1];
7299 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7300 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7302 new_move_dir = move_dir;
7307 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7310 move_dir_preference = -1 * RunnerVisit[xx][yy];
7311 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7312 move_dir_preference = PlayerVisit[xx][yy];
7314 if (move_dir_preference > move_preference)
7316 /* prefer field that has not been visited for the longest time */
7317 move_preference = move_dir_preference;
7318 new_move_dir = move_dir;
7320 else if (move_dir_preference == move_preference &&
7321 move_dir == old_move_dir)
7323 /* prefer last direction when all directions are preferred equally */
7324 move_preference = move_dir_preference;
7325 new_move_dir = move_dir;
7329 MovDir[x][y] = new_move_dir;
7330 if (old_move_dir != new_move_dir)
7331 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7335 static void TurnRound(int x, int y)
7337 int direction = MovDir[x][y];
7341 GfxDir[x][y] = MovDir[x][y];
7343 if (direction != MovDir[x][y])
7347 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7349 ResetGfxFrame(x, y);
7352 static boolean JustBeingPushed(int x, int y)
7356 for (i = 0; i < MAX_PLAYERS; i++)
7358 struct PlayerInfo *player = &stored_player[i];
7360 if (player->active && player->is_pushing && player->MovPos)
7362 int next_jx = player->jx + (player->jx - player->last_jx);
7363 int next_jy = player->jy + (player->jy - player->last_jy);
7365 if (x == next_jx && y == next_jy)
7373 void StartMoving(int x, int y)
7375 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7376 int element = Feld[x][y];
7381 if (MovDelay[x][y] == 0)
7382 GfxAction[x][y] = ACTION_DEFAULT;
7384 if (CAN_FALL(element) && y < lev_fieldy - 1)
7386 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7387 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7388 if (JustBeingPushed(x, y))
7391 if (element == EL_QUICKSAND_FULL)
7393 if (IS_FREE(x, y + 1))
7395 InitMovingField(x, y, MV_DOWN);
7396 started_moving = TRUE;
7398 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7399 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7400 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7401 Store[x][y] = EL_ROCK;
7403 Store[x][y] = EL_ROCK;
7406 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7408 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7410 if (!MovDelay[x][y])
7412 MovDelay[x][y] = TILEY + 1;
7414 ResetGfxAnimation(x, y);
7415 ResetGfxAnimation(x, y + 1);
7420 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7421 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7428 Feld[x][y] = EL_QUICKSAND_EMPTY;
7429 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7430 Store[x][y + 1] = Store[x][y];
7433 PlayLevelSoundAction(x, y, ACTION_FILLING);
7435 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7437 if (!MovDelay[x][y])
7439 MovDelay[x][y] = TILEY + 1;
7441 ResetGfxAnimation(x, y);
7442 ResetGfxAnimation(x, y + 1);
7447 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7448 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7455 Feld[x][y] = EL_QUICKSAND_EMPTY;
7456 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7457 Store[x][y + 1] = Store[x][y];
7460 PlayLevelSoundAction(x, y, ACTION_FILLING);
7463 else if (element == EL_QUICKSAND_FAST_FULL)
7465 if (IS_FREE(x, y + 1))
7467 InitMovingField(x, y, MV_DOWN);
7468 started_moving = TRUE;
7470 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7471 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7472 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7473 Store[x][y] = EL_ROCK;
7475 Store[x][y] = EL_ROCK;
7478 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7480 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7482 if (!MovDelay[x][y])
7484 MovDelay[x][y] = TILEY + 1;
7486 ResetGfxAnimation(x, y);
7487 ResetGfxAnimation(x, y + 1);
7492 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7493 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7500 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7501 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7502 Store[x][y + 1] = Store[x][y];
7505 PlayLevelSoundAction(x, y, ACTION_FILLING);
7507 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7509 if (!MovDelay[x][y])
7511 MovDelay[x][y] = TILEY + 1;
7513 ResetGfxAnimation(x, y);
7514 ResetGfxAnimation(x, y + 1);
7519 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7520 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7527 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7528 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7529 Store[x][y + 1] = Store[x][y];
7532 PlayLevelSoundAction(x, y, ACTION_FILLING);
7535 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7536 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7538 InitMovingField(x, y, MV_DOWN);
7539 started_moving = TRUE;
7541 Feld[x][y] = EL_QUICKSAND_FILLING;
7542 Store[x][y] = element;
7544 PlayLevelSoundAction(x, y, ACTION_FILLING);
7546 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7547 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7549 InitMovingField(x, y, MV_DOWN);
7550 started_moving = TRUE;
7552 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7553 Store[x][y] = element;
7555 PlayLevelSoundAction(x, y, ACTION_FILLING);
7557 else if (element == EL_MAGIC_WALL_FULL)
7559 if (IS_FREE(x, y + 1))
7561 InitMovingField(x, y, MV_DOWN);
7562 started_moving = TRUE;
7564 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7565 Store[x][y] = EL_CHANGED(Store[x][y]);
7567 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7569 if (!MovDelay[x][y])
7570 MovDelay[x][y] = TILEY / 4 + 1;
7579 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7580 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7581 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7585 else if (element == EL_BD_MAGIC_WALL_FULL)
7587 if (IS_FREE(x, y + 1))
7589 InitMovingField(x, y, MV_DOWN);
7590 started_moving = TRUE;
7592 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7593 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7595 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7597 if (!MovDelay[x][y])
7598 MovDelay[x][y] = TILEY / 4 + 1;
7607 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7608 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7609 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7613 else if (element == EL_DC_MAGIC_WALL_FULL)
7615 if (IS_FREE(x, y + 1))
7617 InitMovingField(x, y, MV_DOWN);
7618 started_moving = TRUE;
7620 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7621 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7623 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7625 if (!MovDelay[x][y])
7626 MovDelay[x][y] = TILEY / 4 + 1;
7635 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7636 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7637 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7641 else if ((CAN_PASS_MAGIC_WALL(element) &&
7642 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7643 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7644 (CAN_PASS_DC_MAGIC_WALL(element) &&
7645 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7648 InitMovingField(x, y, MV_DOWN);
7649 started_moving = TRUE;
7652 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7653 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7654 EL_DC_MAGIC_WALL_FILLING);
7655 Store[x][y] = element;
7657 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7659 SplashAcid(x, y + 1);
7661 InitMovingField(x, y, MV_DOWN);
7662 started_moving = TRUE;
7664 Store[x][y] = EL_ACID;
7667 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7668 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7669 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7670 CAN_FALL(element) && WasJustFalling[x][y] &&
7671 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7673 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7674 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7675 (Feld[x][y + 1] == EL_BLOCKED)))
7677 /* this is needed for a special case not covered by calling "Impact()"
7678 from "ContinueMoving()": if an element moves to a tile directly below
7679 another element which was just falling on that tile (which was empty
7680 in the previous frame), the falling element above would just stop
7681 instead of smashing the element below (in previous version, the above
7682 element was just checked for "moving" instead of "falling", resulting
7683 in incorrect smashes caused by horizontal movement of the above
7684 element; also, the case of the player being the element to smash was
7685 simply not covered here... :-/ ) */
7687 CheckCollision[x][y] = 0;
7688 CheckImpact[x][y] = 0;
7692 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7694 if (MovDir[x][y] == MV_NONE)
7696 InitMovingField(x, y, MV_DOWN);
7697 started_moving = TRUE;
7700 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7702 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7703 MovDir[x][y] = MV_DOWN;
7705 InitMovingField(x, y, MV_DOWN);
7706 started_moving = TRUE;
7708 else if (element == EL_AMOEBA_DROP)
7710 Feld[x][y] = EL_AMOEBA_GROWING;
7711 Store[x][y] = EL_AMOEBA_WET;
7713 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7714 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7715 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7716 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7718 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7719 (IS_FREE(x - 1, y + 1) ||
7720 Feld[x - 1][y + 1] == EL_ACID));
7721 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7722 (IS_FREE(x + 1, y + 1) ||
7723 Feld[x + 1][y + 1] == EL_ACID));
7724 boolean can_fall_any = (can_fall_left || can_fall_right);
7725 boolean can_fall_both = (can_fall_left && can_fall_right);
7726 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7728 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7730 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7731 can_fall_right = FALSE;
7732 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7733 can_fall_left = FALSE;
7734 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7735 can_fall_right = FALSE;
7736 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7737 can_fall_left = FALSE;
7739 can_fall_any = (can_fall_left || can_fall_right);
7740 can_fall_both = FALSE;
7745 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7746 can_fall_right = FALSE; /* slip down on left side */
7748 can_fall_left = !(can_fall_right = RND(2));
7750 can_fall_both = FALSE;
7755 /* if not determined otherwise, prefer left side for slipping down */
7756 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7757 started_moving = TRUE;
7760 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7762 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7763 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7764 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7765 int belt_dir = game.belt_dir[belt_nr];
7767 if ((belt_dir == MV_LEFT && left_is_free) ||
7768 (belt_dir == MV_RIGHT && right_is_free))
7770 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7772 InitMovingField(x, y, belt_dir);
7773 started_moving = TRUE;
7775 Pushed[x][y] = TRUE;
7776 Pushed[nextx][y] = TRUE;
7778 GfxAction[x][y] = ACTION_DEFAULT;
7782 MovDir[x][y] = 0; /* if element was moving, stop it */
7787 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7788 if (CAN_MOVE(element) && !started_moving)
7790 int move_pattern = element_info[element].move_pattern;
7793 Moving2Blocked(x, y, &newx, &newy);
7795 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7798 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7799 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7801 WasJustMoving[x][y] = 0;
7802 CheckCollision[x][y] = 0;
7804 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7806 if (Feld[x][y] != element) /* element has changed */
7810 if (!MovDelay[x][y]) /* start new movement phase */
7812 /* all objects that can change their move direction after each step
7813 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7815 if (element != EL_YAMYAM &&
7816 element != EL_DARK_YAMYAM &&
7817 element != EL_PACMAN &&
7818 !(move_pattern & MV_ANY_DIRECTION) &&
7819 move_pattern != MV_TURNING_LEFT &&
7820 move_pattern != MV_TURNING_RIGHT &&
7821 move_pattern != MV_TURNING_LEFT_RIGHT &&
7822 move_pattern != MV_TURNING_RIGHT_LEFT &&
7823 move_pattern != MV_TURNING_RANDOM)
7827 if (MovDelay[x][y] && (element == EL_BUG ||
7828 element == EL_SPACESHIP ||
7829 element == EL_SP_SNIKSNAK ||
7830 element == EL_SP_ELECTRON ||
7831 element == EL_MOLE))
7832 TEST_DrawLevelField(x, y);
7836 if (MovDelay[x][y]) /* wait some time before next movement */
7840 if (element == EL_ROBOT ||
7841 element == EL_YAMYAM ||
7842 element == EL_DARK_YAMYAM)
7844 DrawLevelElementAnimationIfNeeded(x, y, element);
7845 PlayLevelSoundAction(x, y, ACTION_WAITING);
7847 else if (element == EL_SP_ELECTRON)
7848 DrawLevelElementAnimationIfNeeded(x, y, element);
7849 else if (element == EL_DRAGON)
7852 int dir = MovDir[x][y];
7853 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7854 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7855 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7856 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7857 dir == MV_UP ? IMG_FLAMES_1_UP :
7858 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7859 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7861 GfxAction[x][y] = ACTION_ATTACKING;
7863 if (IS_PLAYER(x, y))
7864 DrawPlayerField(x, y);
7866 TEST_DrawLevelField(x, y);
7868 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7870 for (i = 1; i <= 3; i++)
7872 int xx = x + i * dx;
7873 int yy = y + i * dy;
7874 int sx = SCREENX(xx);
7875 int sy = SCREENY(yy);
7876 int flame_graphic = graphic + (i - 1);
7878 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7883 int flamed = MovingOrBlocked2Element(xx, yy);
7885 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7888 RemoveMovingField(xx, yy);
7890 ChangeDelay[xx][yy] = 0;
7892 Feld[xx][yy] = EL_FLAMES;
7894 if (IN_SCR_FIELD(sx, sy))
7896 TEST_DrawLevelFieldCrumbled(xx, yy);
7897 DrawGraphic(sx, sy, flame_graphic, frame);
7902 if (Feld[xx][yy] == EL_FLAMES)
7903 Feld[xx][yy] = EL_EMPTY;
7904 TEST_DrawLevelField(xx, yy);
7909 if (MovDelay[x][y]) /* element still has to wait some time */
7911 PlayLevelSoundAction(x, y, ACTION_WAITING);
7917 /* now make next step */
7919 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7921 if (DONT_COLLIDE_WITH(element) &&
7922 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7923 !PLAYER_ENEMY_PROTECTED(newx, newy))
7925 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7930 else if (CAN_MOVE_INTO_ACID(element) &&
7931 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7932 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7933 (MovDir[x][y] == MV_DOWN ||
7934 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7936 SplashAcid(newx, newy);
7937 Store[x][y] = EL_ACID;
7939 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7941 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7942 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7943 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7944 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7947 TEST_DrawLevelField(x, y);
7949 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7950 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7951 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7953 local_player->friends_still_needed--;
7954 if (!local_player->friends_still_needed &&
7955 !local_player->GameOver && AllPlayersGone)
7956 PlayerWins(local_player);
7960 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7962 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7963 TEST_DrawLevelField(newx, newy);
7965 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7967 else if (!IS_FREE(newx, newy))
7969 GfxAction[x][y] = ACTION_WAITING;
7971 if (IS_PLAYER(x, y))
7972 DrawPlayerField(x, y);
7974 TEST_DrawLevelField(x, y);
7979 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7981 if (IS_FOOD_PIG(Feld[newx][newy]))
7983 if (IS_MOVING(newx, newy))
7984 RemoveMovingField(newx, newy);
7987 Feld[newx][newy] = EL_EMPTY;
7988 TEST_DrawLevelField(newx, newy);
7991 PlayLevelSound(x, y, SND_PIG_DIGGING);
7993 else if (!IS_FREE(newx, newy))
7995 if (IS_PLAYER(x, y))
7996 DrawPlayerField(x, y);
7998 TEST_DrawLevelField(x, y);
8003 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8005 if (Store[x][y] != EL_EMPTY)
8007 boolean can_clone = FALSE;
8010 /* check if element to clone is still there */
8011 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8013 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8021 /* cannot clone or target field not free anymore -- do not clone */
8022 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8023 Store[x][y] = EL_EMPTY;
8026 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8028 if (IS_MV_DIAGONAL(MovDir[x][y]))
8030 int diagonal_move_dir = MovDir[x][y];
8031 int stored = Store[x][y];
8032 int change_delay = 8;
8035 /* android is moving diagonally */
8037 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8039 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8040 GfxElement[x][y] = EL_EMC_ANDROID;
8041 GfxAction[x][y] = ACTION_SHRINKING;
8042 GfxDir[x][y] = diagonal_move_dir;
8043 ChangeDelay[x][y] = change_delay;
8045 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8048 DrawLevelGraphicAnimation(x, y, graphic);
8049 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8051 if (Feld[newx][newy] == EL_ACID)
8053 SplashAcid(newx, newy);
8058 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8060 Store[newx][newy] = EL_EMC_ANDROID;
8061 GfxElement[newx][newy] = EL_EMC_ANDROID;
8062 GfxAction[newx][newy] = ACTION_GROWING;
8063 GfxDir[newx][newy] = diagonal_move_dir;
8064 ChangeDelay[newx][newy] = change_delay;
8066 graphic = el_act_dir2img(GfxElement[newx][newy],
8067 GfxAction[newx][newy], GfxDir[newx][newy]);
8069 DrawLevelGraphicAnimation(newx, newy, graphic);
8070 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8076 Feld[newx][newy] = EL_EMPTY;
8077 TEST_DrawLevelField(newx, newy);
8079 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8082 else if (!IS_FREE(newx, newy))
8087 else if (IS_CUSTOM_ELEMENT(element) &&
8088 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8090 if (!DigFieldByCE(newx, newy, element))
8093 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8095 RunnerVisit[x][y] = FrameCounter;
8096 PlayerVisit[x][y] /= 8; /* expire player visit path */
8099 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8101 if (!IS_FREE(newx, newy))
8103 if (IS_PLAYER(x, y))
8104 DrawPlayerField(x, y);
8106 TEST_DrawLevelField(x, y);
8112 boolean wanna_flame = !RND(10);
8113 int dx = newx - x, dy = newy - y;
8114 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8115 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8116 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8117 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8118 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8119 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8122 IS_CLASSIC_ENEMY(element1) ||
8123 IS_CLASSIC_ENEMY(element2)) &&
8124 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8125 element1 != EL_FLAMES && element2 != EL_FLAMES)
8127 ResetGfxAnimation(x, y);
8128 GfxAction[x][y] = ACTION_ATTACKING;
8130 if (IS_PLAYER(x, y))
8131 DrawPlayerField(x, y);
8133 TEST_DrawLevelField(x, y);
8135 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8137 MovDelay[x][y] = 50;
8139 Feld[newx][newy] = EL_FLAMES;
8140 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8141 Feld[newx1][newy1] = EL_FLAMES;
8142 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8143 Feld[newx2][newy2] = EL_FLAMES;
8149 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8150 Feld[newx][newy] == EL_DIAMOND)
8152 if (IS_MOVING(newx, newy))
8153 RemoveMovingField(newx, newy);
8156 Feld[newx][newy] = EL_EMPTY;
8157 TEST_DrawLevelField(newx, newy);
8160 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8162 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8163 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8165 if (AmoebaNr[newx][newy])
8167 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8168 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8169 Feld[newx][newy] == EL_BD_AMOEBA)
8170 AmoebaCnt[AmoebaNr[newx][newy]]--;
8173 if (IS_MOVING(newx, newy))
8175 RemoveMovingField(newx, newy);
8179 Feld[newx][newy] = EL_EMPTY;
8180 TEST_DrawLevelField(newx, newy);
8183 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8185 else if ((element == EL_PACMAN || element == EL_MOLE)
8186 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8188 if (AmoebaNr[newx][newy])
8190 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8191 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8192 Feld[newx][newy] == EL_BD_AMOEBA)
8193 AmoebaCnt[AmoebaNr[newx][newy]]--;
8196 if (element == EL_MOLE)
8198 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8199 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8201 ResetGfxAnimation(x, y);
8202 GfxAction[x][y] = ACTION_DIGGING;
8203 TEST_DrawLevelField(x, y);
8205 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8207 return; /* wait for shrinking amoeba */
8209 else /* element == EL_PACMAN */
8211 Feld[newx][newy] = EL_EMPTY;
8212 TEST_DrawLevelField(newx, newy);
8213 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8216 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8217 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8218 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8220 /* wait for shrinking amoeba to completely disappear */
8223 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8225 /* object was running against a wall */
8229 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8230 DrawLevelElementAnimation(x, y, element);
8232 if (DONT_TOUCH(element))
8233 TestIfBadThingTouchesPlayer(x, y);
8238 InitMovingField(x, y, MovDir[x][y]);
8240 PlayLevelSoundAction(x, y, ACTION_MOVING);
8244 ContinueMoving(x, y);
8247 void ContinueMoving(int x, int y)
8249 int element = Feld[x][y];
8250 struct ElementInfo *ei = &element_info[element];
8251 int direction = MovDir[x][y];
8252 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8253 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8254 int newx = x + dx, newy = y + dy;
8255 int stored = Store[x][y];
8256 int stored_new = Store[newx][newy];
8257 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8258 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8259 boolean last_line = (newy == lev_fieldy - 1);
8261 MovPos[x][y] += getElementMoveStepsize(x, y);
8263 if (pushed_by_player) /* special case: moving object pushed by player */
8264 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8266 if (ABS(MovPos[x][y]) < TILEX)
8268 TEST_DrawLevelField(x, y);
8270 return; /* element is still moving */
8273 /* element reached destination field */
8275 Feld[x][y] = EL_EMPTY;
8276 Feld[newx][newy] = element;
8277 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8279 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8281 element = Feld[newx][newy] = EL_ACID;
8283 else if (element == EL_MOLE)
8285 Feld[x][y] = EL_SAND;
8287 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8289 else if (element == EL_QUICKSAND_FILLING)
8291 element = Feld[newx][newy] = get_next_element(element);
8292 Store[newx][newy] = Store[x][y];
8294 else if (element == EL_QUICKSAND_EMPTYING)
8296 Feld[x][y] = get_next_element(element);
8297 element = Feld[newx][newy] = Store[x][y];
8299 else if (element == EL_QUICKSAND_FAST_FILLING)
8301 element = Feld[newx][newy] = get_next_element(element);
8302 Store[newx][newy] = Store[x][y];
8304 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8306 Feld[x][y] = get_next_element(element);
8307 element = Feld[newx][newy] = Store[x][y];
8309 else if (element == EL_MAGIC_WALL_FILLING)
8311 element = Feld[newx][newy] = get_next_element(element);
8312 if (!game.magic_wall_active)
8313 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8314 Store[newx][newy] = Store[x][y];
8316 else if (element == EL_MAGIC_WALL_EMPTYING)
8318 Feld[x][y] = get_next_element(element);
8319 if (!game.magic_wall_active)
8320 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8321 element = Feld[newx][newy] = Store[x][y];
8323 InitField(newx, newy, FALSE);
8325 else if (element == EL_BD_MAGIC_WALL_FILLING)
8327 element = Feld[newx][newy] = get_next_element(element);
8328 if (!game.magic_wall_active)
8329 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8330 Store[newx][newy] = Store[x][y];
8332 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8334 Feld[x][y] = get_next_element(element);
8335 if (!game.magic_wall_active)
8336 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8337 element = Feld[newx][newy] = Store[x][y];
8339 InitField(newx, newy, FALSE);
8341 else if (element == EL_DC_MAGIC_WALL_FILLING)
8343 element = Feld[newx][newy] = get_next_element(element);
8344 if (!game.magic_wall_active)
8345 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8346 Store[newx][newy] = Store[x][y];
8348 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8350 Feld[x][y] = get_next_element(element);
8351 if (!game.magic_wall_active)
8352 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8353 element = Feld[newx][newy] = Store[x][y];
8355 InitField(newx, newy, FALSE);
8357 else if (element == EL_AMOEBA_DROPPING)
8359 Feld[x][y] = get_next_element(element);
8360 element = Feld[newx][newy] = Store[x][y];
8362 else if (element == EL_SOKOBAN_OBJECT)
8365 Feld[x][y] = Back[x][y];
8367 if (Back[newx][newy])
8368 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8370 Back[x][y] = Back[newx][newy] = 0;
8373 Store[x][y] = EL_EMPTY;
8378 MovDelay[newx][newy] = 0;
8380 if (CAN_CHANGE_OR_HAS_ACTION(element))
8382 /* copy element change control values to new field */
8383 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8384 ChangePage[newx][newy] = ChangePage[x][y];
8385 ChangeCount[newx][newy] = ChangeCount[x][y];
8386 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8389 CustomValue[newx][newy] = CustomValue[x][y];
8391 ChangeDelay[x][y] = 0;
8392 ChangePage[x][y] = -1;
8393 ChangeCount[x][y] = 0;
8394 ChangeEvent[x][y] = -1;
8396 CustomValue[x][y] = 0;
8398 /* copy animation control values to new field */
8399 GfxFrame[newx][newy] = GfxFrame[x][y];
8400 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8401 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8402 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8404 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8406 /* some elements can leave other elements behind after moving */
8407 if (ei->move_leave_element != EL_EMPTY &&
8408 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8409 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8411 int move_leave_element = ei->move_leave_element;
8413 /* this makes it possible to leave the removed element again */
8414 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8415 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8417 Feld[x][y] = move_leave_element;
8419 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8420 MovDir[x][y] = direction;
8422 InitField(x, y, FALSE);
8424 if (GFX_CRUMBLED(Feld[x][y]))
8425 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8427 if (ELEM_IS_PLAYER(move_leave_element))
8428 RelocatePlayer(x, y, move_leave_element);
8431 /* do this after checking for left-behind element */
8432 ResetGfxAnimation(x, y); /* reset animation values for old field */
8434 if (!CAN_MOVE(element) ||
8435 (CAN_FALL(element) && direction == MV_DOWN &&
8436 (element == EL_SPRING ||
8437 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8438 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8439 GfxDir[x][y] = MovDir[newx][newy] = 0;
8441 TEST_DrawLevelField(x, y);
8442 TEST_DrawLevelField(newx, newy);
8444 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8446 /* prevent pushed element from moving on in pushed direction */
8447 if (pushed_by_player && CAN_MOVE(element) &&
8448 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8449 !(element_info[element].move_pattern & direction))
8450 TurnRound(newx, newy);
8452 /* prevent elements on conveyor belt from moving on in last direction */
8453 if (pushed_by_conveyor && CAN_FALL(element) &&
8454 direction & MV_HORIZONTAL)
8455 MovDir[newx][newy] = 0;
8457 if (!pushed_by_player)
8459 int nextx = newx + dx, nexty = newy + dy;
8460 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8462 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8464 if (CAN_FALL(element) && direction == MV_DOWN)
8465 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8467 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8468 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8470 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8471 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8474 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8476 TestIfBadThingTouchesPlayer(newx, newy);
8477 TestIfBadThingTouchesFriend(newx, newy);
8479 if (!IS_CUSTOM_ELEMENT(element))
8480 TestIfBadThingTouchesOtherBadThing(newx, newy);
8482 else if (element == EL_PENGUIN)
8483 TestIfFriendTouchesBadThing(newx, newy);
8485 if (DONT_GET_HIT_BY(element))
8487 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8490 /* give the player one last chance (one more frame) to move away */
8491 if (CAN_FALL(element) && direction == MV_DOWN &&
8492 (last_line || (!IS_FREE(x, newy + 1) &&
8493 (!IS_PLAYER(x, newy + 1) ||
8494 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8497 if (pushed_by_player && !game.use_change_when_pushing_bug)
8499 int push_side = MV_DIR_OPPOSITE(direction);
8500 struct PlayerInfo *player = PLAYERINFO(x, y);
8502 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8503 player->index_bit, push_side);
8504 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8505 player->index_bit, push_side);
8508 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8509 MovDelay[newx][newy] = 1;
8511 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8513 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8514 TestIfElementHitsCustomElement(newx, newy, direction);
8515 TestIfPlayerTouchesCustomElement(newx, newy);
8516 TestIfElementTouchesCustomElement(newx, newy);
8518 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8519 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8520 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8521 MV_DIR_OPPOSITE(direction));
8524 int AmoebeNachbarNr(int ax, int ay)
8527 int element = Feld[ax][ay];
8529 static int xy[4][2] =
8537 for (i = 0; i < NUM_DIRECTIONS; i++)
8539 int x = ax + xy[i][0];
8540 int y = ay + xy[i][1];
8542 if (!IN_LEV_FIELD(x, y))
8545 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8546 group_nr = AmoebaNr[x][y];
8552 void AmoebenVereinigen(int ax, int ay)
8554 int i, x, y, xx, yy;
8555 int new_group_nr = AmoebaNr[ax][ay];
8556 static int xy[4][2] =
8564 if (new_group_nr == 0)
8567 for (i = 0; i < NUM_DIRECTIONS; i++)
8572 if (!IN_LEV_FIELD(x, y))
8575 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8576 Feld[x][y] == EL_BD_AMOEBA ||
8577 Feld[x][y] == EL_AMOEBA_DEAD) &&
8578 AmoebaNr[x][y] != new_group_nr)
8580 int old_group_nr = AmoebaNr[x][y];
8582 if (old_group_nr == 0)
8585 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8586 AmoebaCnt[old_group_nr] = 0;
8587 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8588 AmoebaCnt2[old_group_nr] = 0;
8590 SCAN_PLAYFIELD(xx, yy)
8592 if (AmoebaNr[xx][yy] == old_group_nr)
8593 AmoebaNr[xx][yy] = new_group_nr;
8599 void AmoebeUmwandeln(int ax, int ay)
8603 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8605 int group_nr = AmoebaNr[ax][ay];
8610 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8611 printf("AmoebeUmwandeln(): This should never happen!\n");
8616 SCAN_PLAYFIELD(x, y)
8618 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8621 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8625 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8626 SND_AMOEBA_TURNING_TO_GEM :
8627 SND_AMOEBA_TURNING_TO_ROCK));
8632 static int xy[4][2] =
8640 for (i = 0; i < NUM_DIRECTIONS; i++)
8645 if (!IN_LEV_FIELD(x, y))
8648 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8650 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8651 SND_AMOEBA_TURNING_TO_GEM :
8652 SND_AMOEBA_TURNING_TO_ROCK));
8659 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8662 int group_nr = AmoebaNr[ax][ay];
8663 boolean done = FALSE;
8668 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8669 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8674 SCAN_PLAYFIELD(x, y)
8676 if (AmoebaNr[x][y] == group_nr &&
8677 (Feld[x][y] == EL_AMOEBA_DEAD ||
8678 Feld[x][y] == EL_BD_AMOEBA ||
8679 Feld[x][y] == EL_AMOEBA_GROWING))
8682 Feld[x][y] = new_element;
8683 InitField(x, y, FALSE);
8684 TEST_DrawLevelField(x, y);
8690 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8691 SND_BD_AMOEBA_TURNING_TO_ROCK :
8692 SND_BD_AMOEBA_TURNING_TO_GEM));
8695 void AmoebeWaechst(int x, int y)
8697 static unsigned int sound_delay = 0;
8698 static unsigned int sound_delay_value = 0;
8700 if (!MovDelay[x][y]) /* start new growing cycle */
8704 if (DelayReached(&sound_delay, sound_delay_value))
8706 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8707 sound_delay_value = 30;
8711 if (MovDelay[x][y]) /* wait some time before growing bigger */
8714 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8716 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8717 6 - MovDelay[x][y]);
8719 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8722 if (!MovDelay[x][y])
8724 Feld[x][y] = Store[x][y];
8726 TEST_DrawLevelField(x, y);
8731 void AmoebaDisappearing(int x, int y)
8733 static unsigned int sound_delay = 0;
8734 static unsigned int sound_delay_value = 0;
8736 if (!MovDelay[x][y]) /* start new shrinking cycle */
8740 if (DelayReached(&sound_delay, sound_delay_value))
8741 sound_delay_value = 30;
8744 if (MovDelay[x][y]) /* wait some time before shrinking */
8747 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8749 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8750 6 - MovDelay[x][y]);
8752 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8755 if (!MovDelay[x][y])
8757 Feld[x][y] = EL_EMPTY;
8758 TEST_DrawLevelField(x, y);
8760 /* don't let mole enter this field in this cycle;
8761 (give priority to objects falling to this field from above) */
8767 void AmoebeAbleger(int ax, int ay)
8770 int element = Feld[ax][ay];
8771 int graphic = el2img(element);
8772 int newax = ax, neway = ay;
8773 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8774 static int xy[4][2] =
8782 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8784 Feld[ax][ay] = EL_AMOEBA_DEAD;
8785 TEST_DrawLevelField(ax, ay);
8789 if (IS_ANIMATED(graphic))
8790 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8792 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8793 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8795 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8798 if (MovDelay[ax][ay])
8802 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8805 int x = ax + xy[start][0];
8806 int y = ay + xy[start][1];
8808 if (!IN_LEV_FIELD(x, y))
8811 if (IS_FREE(x, y) ||
8812 CAN_GROW_INTO(Feld[x][y]) ||
8813 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8814 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8820 if (newax == ax && neway == ay)
8823 else /* normal or "filled" (BD style) amoeba */
8826 boolean waiting_for_player = FALSE;
8828 for (i = 0; i < NUM_DIRECTIONS; i++)
8830 int j = (start + i) % 4;
8831 int x = ax + xy[j][0];
8832 int y = ay + xy[j][1];
8834 if (!IN_LEV_FIELD(x, y))
8837 if (IS_FREE(x, y) ||
8838 CAN_GROW_INTO(Feld[x][y]) ||
8839 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8840 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8846 else if (IS_PLAYER(x, y))
8847 waiting_for_player = TRUE;
8850 if (newax == ax && neway == ay) /* amoeba cannot grow */
8852 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8854 Feld[ax][ay] = EL_AMOEBA_DEAD;
8855 TEST_DrawLevelField(ax, ay);
8856 AmoebaCnt[AmoebaNr[ax][ay]]--;
8858 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8860 if (element == EL_AMOEBA_FULL)
8861 AmoebeUmwandeln(ax, ay);
8862 else if (element == EL_BD_AMOEBA)
8863 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8868 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8870 /* amoeba gets larger by growing in some direction */
8872 int new_group_nr = AmoebaNr[ax][ay];
8875 if (new_group_nr == 0)
8877 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8878 printf("AmoebeAbleger(): This should never happen!\n");
8883 AmoebaNr[newax][neway] = new_group_nr;
8884 AmoebaCnt[new_group_nr]++;
8885 AmoebaCnt2[new_group_nr]++;
8887 /* if amoeba touches other amoeba(s) after growing, unify them */
8888 AmoebenVereinigen(newax, neway);
8890 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8892 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8898 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8899 (neway == lev_fieldy - 1 && newax != ax))
8901 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8902 Store[newax][neway] = element;
8904 else if (neway == ay || element == EL_EMC_DRIPPER)
8906 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8908 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8912 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8913 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8914 Store[ax][ay] = EL_AMOEBA_DROP;
8915 ContinueMoving(ax, ay);
8919 TEST_DrawLevelField(newax, neway);
8922 void Life(int ax, int ay)
8926 int element = Feld[ax][ay];
8927 int graphic = el2img(element);
8928 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8930 boolean changed = FALSE;
8932 if (IS_ANIMATED(graphic))
8933 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8938 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8939 MovDelay[ax][ay] = life_time;
8941 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8944 if (MovDelay[ax][ay])
8948 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8950 int xx = ax+x1, yy = ay+y1;
8953 if (!IN_LEV_FIELD(xx, yy))
8956 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8958 int x = xx+x2, y = yy+y2;
8960 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8963 if (((Feld[x][y] == element ||
8964 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8966 (IS_FREE(x, y) && Stop[x][y]))
8970 if (xx == ax && yy == ay) /* field in the middle */
8972 if (nachbarn < life_parameter[0] ||
8973 nachbarn > life_parameter[1])
8975 Feld[xx][yy] = EL_EMPTY;
8977 TEST_DrawLevelField(xx, yy);
8978 Stop[xx][yy] = TRUE;
8982 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8983 { /* free border field */
8984 if (nachbarn >= life_parameter[2] &&
8985 nachbarn <= life_parameter[3])
8987 Feld[xx][yy] = element;
8988 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8990 TEST_DrawLevelField(xx, yy);
8991 Stop[xx][yy] = TRUE;
8998 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8999 SND_GAME_OF_LIFE_GROWING);
9002 static void InitRobotWheel(int x, int y)
9004 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9007 static void RunRobotWheel(int x, int y)
9009 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9012 static void StopRobotWheel(int x, int y)
9014 if (ZX == x && ZY == y)
9018 game.robot_wheel_active = FALSE;
9022 static void InitTimegateWheel(int x, int y)
9024 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9027 static void RunTimegateWheel(int x, int y)
9029 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9032 static void InitMagicBallDelay(int x, int y)
9034 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9037 static void ActivateMagicBall(int bx, int by)
9041 if (level.ball_random)
9043 int pos_border = RND(8); /* select one of the eight border elements */
9044 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9045 int xx = pos_content % 3;
9046 int yy = pos_content / 3;
9051 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9052 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9056 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9058 int xx = x - bx + 1;
9059 int yy = y - by + 1;
9061 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9062 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9066 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9069 void CheckExit(int x, int y)
9071 if (local_player->gems_still_needed > 0 ||
9072 local_player->sokobanfields_still_needed > 0 ||
9073 local_player->lights_still_needed > 0)
9075 int element = Feld[x][y];
9076 int graphic = el2img(element);
9078 if (IS_ANIMATED(graphic))
9079 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9084 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9087 Feld[x][y] = EL_EXIT_OPENING;
9089 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9092 void CheckExitEM(int x, int y)
9094 if (local_player->gems_still_needed > 0 ||
9095 local_player->sokobanfields_still_needed > 0 ||
9096 local_player->lights_still_needed > 0)
9098 int element = Feld[x][y];
9099 int graphic = el2img(element);
9101 if (IS_ANIMATED(graphic))
9102 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9107 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9110 Feld[x][y] = EL_EM_EXIT_OPENING;
9112 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9115 void CheckExitSteel(int x, int y)
9117 if (local_player->gems_still_needed > 0 ||
9118 local_player->sokobanfields_still_needed > 0 ||
9119 local_player->lights_still_needed > 0)
9121 int element = Feld[x][y];
9122 int graphic = el2img(element);
9124 if (IS_ANIMATED(graphic))
9125 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9130 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9133 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9135 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9138 void CheckExitSteelEM(int x, int y)
9140 if (local_player->gems_still_needed > 0 ||
9141 local_player->sokobanfields_still_needed > 0 ||
9142 local_player->lights_still_needed > 0)
9144 int element = Feld[x][y];
9145 int graphic = el2img(element);
9147 if (IS_ANIMATED(graphic))
9148 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9153 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9156 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9158 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9161 void CheckExitSP(int x, int y)
9163 if (local_player->gems_still_needed > 0)
9165 int element = Feld[x][y];
9166 int graphic = el2img(element);
9168 if (IS_ANIMATED(graphic))
9169 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9174 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9177 Feld[x][y] = EL_SP_EXIT_OPENING;
9179 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9182 static void CloseAllOpenTimegates()
9186 SCAN_PLAYFIELD(x, y)
9188 int element = Feld[x][y];
9190 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9192 Feld[x][y] = EL_TIMEGATE_CLOSING;
9194 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9199 void DrawTwinkleOnField(int x, int y)
9201 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9204 if (Feld[x][y] == EL_BD_DIAMOND)
9207 if (MovDelay[x][y] == 0) /* next animation frame */
9208 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9210 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9214 DrawLevelElementAnimation(x, y, Feld[x][y]);
9216 if (MovDelay[x][y] != 0)
9218 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9219 10 - MovDelay[x][y]);
9221 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9226 void MauerWaechst(int x, int y)
9230 if (!MovDelay[x][y]) /* next animation frame */
9231 MovDelay[x][y] = 3 * delay;
9233 if (MovDelay[x][y]) /* wait some time before next frame */
9237 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9239 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9240 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9242 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9245 if (!MovDelay[x][y])
9247 if (MovDir[x][y] == MV_LEFT)
9249 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9250 TEST_DrawLevelField(x - 1, y);
9252 else if (MovDir[x][y] == MV_RIGHT)
9254 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9255 TEST_DrawLevelField(x + 1, y);
9257 else if (MovDir[x][y] == MV_UP)
9259 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9260 TEST_DrawLevelField(x, y - 1);
9264 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9265 TEST_DrawLevelField(x, y + 1);
9268 Feld[x][y] = Store[x][y];
9270 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9271 TEST_DrawLevelField(x, y);
9276 void MauerAbleger(int ax, int ay)
9278 int element = Feld[ax][ay];
9279 int graphic = el2img(element);
9280 boolean oben_frei = FALSE, unten_frei = FALSE;
9281 boolean links_frei = FALSE, rechts_frei = FALSE;
9282 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9283 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9284 boolean new_wall = FALSE;
9286 if (IS_ANIMATED(graphic))
9287 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9289 if (!MovDelay[ax][ay]) /* start building new wall */
9290 MovDelay[ax][ay] = 6;
9292 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9295 if (MovDelay[ax][ay])
9299 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9301 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9303 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9305 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9308 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9309 element == EL_EXPANDABLE_WALL_ANY)
9313 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9314 Store[ax][ay-1] = element;
9315 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9316 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9317 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9318 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9323 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9324 Store[ax][ay+1] = element;
9325 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9326 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9327 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9328 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9333 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9334 element == EL_EXPANDABLE_WALL_ANY ||
9335 element == EL_EXPANDABLE_WALL ||
9336 element == EL_BD_EXPANDABLE_WALL)
9340 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9341 Store[ax-1][ay] = element;
9342 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9343 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9344 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9345 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9351 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9352 Store[ax+1][ay] = element;
9353 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9354 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9355 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9356 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9361 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9362 TEST_DrawLevelField(ax, ay);
9364 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9366 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9367 unten_massiv = TRUE;
9368 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9369 links_massiv = TRUE;
9370 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9371 rechts_massiv = TRUE;
9373 if (((oben_massiv && unten_massiv) ||
9374 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9375 element == EL_EXPANDABLE_WALL) &&
9376 ((links_massiv && rechts_massiv) ||
9377 element == EL_EXPANDABLE_WALL_VERTICAL))
9378 Feld[ax][ay] = EL_WALL;
9381 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9384 void MauerAblegerStahl(int ax, int ay)
9386 int element = Feld[ax][ay];
9387 int graphic = el2img(element);
9388 boolean oben_frei = FALSE, unten_frei = FALSE;
9389 boolean links_frei = FALSE, rechts_frei = FALSE;
9390 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9391 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9392 boolean new_wall = FALSE;
9394 if (IS_ANIMATED(graphic))
9395 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9397 if (!MovDelay[ax][ay]) /* start building new wall */
9398 MovDelay[ax][ay] = 6;
9400 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9403 if (MovDelay[ax][ay])
9407 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9409 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9411 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9413 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9416 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9417 element == EL_EXPANDABLE_STEELWALL_ANY)
9421 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9422 Store[ax][ay-1] = element;
9423 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9424 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9425 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9426 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9431 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9432 Store[ax][ay+1] = element;
9433 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9434 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9435 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9436 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9441 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9442 element == EL_EXPANDABLE_STEELWALL_ANY)
9446 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9447 Store[ax-1][ay] = element;
9448 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9449 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9450 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9451 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9457 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9458 Store[ax+1][ay] = element;
9459 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9460 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9461 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9462 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9467 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9469 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9470 unten_massiv = TRUE;
9471 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9472 links_massiv = TRUE;
9473 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9474 rechts_massiv = TRUE;
9476 if (((oben_massiv && unten_massiv) ||
9477 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9478 ((links_massiv && rechts_massiv) ||
9479 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9480 Feld[ax][ay] = EL_STEELWALL;
9483 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9486 void CheckForDragon(int x, int y)
9489 boolean dragon_found = FALSE;
9490 static int xy[4][2] =
9498 for (i = 0; i < NUM_DIRECTIONS; i++)
9500 for (j = 0; j < 4; j++)
9502 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9504 if (IN_LEV_FIELD(xx, yy) &&
9505 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9507 if (Feld[xx][yy] == EL_DRAGON)
9508 dragon_found = TRUE;
9517 for (i = 0; i < NUM_DIRECTIONS; i++)
9519 for (j = 0; j < 3; j++)
9521 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9523 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9525 Feld[xx][yy] = EL_EMPTY;
9526 TEST_DrawLevelField(xx, yy);
9535 static void InitBuggyBase(int x, int y)
9537 int element = Feld[x][y];
9538 int activating_delay = FRAMES_PER_SECOND / 4;
9541 (element == EL_SP_BUGGY_BASE ?
9542 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9543 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9545 element == EL_SP_BUGGY_BASE_ACTIVE ?
9546 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9549 static void WarnBuggyBase(int x, int y)
9552 static int xy[4][2] =
9560 for (i = 0; i < NUM_DIRECTIONS; i++)
9562 int xx = x + xy[i][0];
9563 int yy = y + xy[i][1];
9565 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9567 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9574 static void InitTrap(int x, int y)
9576 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9579 static void ActivateTrap(int x, int y)
9581 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9584 static void ChangeActiveTrap(int x, int y)
9586 int graphic = IMG_TRAP_ACTIVE;
9588 /* if new animation frame was drawn, correct crumbled sand border */
9589 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9590 TEST_DrawLevelFieldCrumbled(x, y);
9593 static int getSpecialActionElement(int element, int number, int base_element)
9595 return (element != EL_EMPTY ? element :
9596 number != -1 ? base_element + number - 1 :
9600 static int getModifiedActionNumber(int value_old, int operator, int operand,
9601 int value_min, int value_max)
9603 int value_new = (operator == CA_MODE_SET ? operand :
9604 operator == CA_MODE_ADD ? value_old + operand :
9605 operator == CA_MODE_SUBTRACT ? value_old - operand :
9606 operator == CA_MODE_MULTIPLY ? value_old * operand :
9607 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9608 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9611 return (value_new < value_min ? value_min :
9612 value_new > value_max ? value_max :
9616 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9618 struct ElementInfo *ei = &element_info[element];
9619 struct ElementChangeInfo *change = &ei->change_page[page];
9620 int target_element = change->target_element;
9621 int action_type = change->action_type;
9622 int action_mode = change->action_mode;
9623 int action_arg = change->action_arg;
9624 int action_element = change->action_element;
9627 if (!change->has_action)
9630 /* ---------- determine action paramater values -------------------------- */
9632 int level_time_value =
9633 (level.time > 0 ? TimeLeft :
9636 int action_arg_element_raw =
9637 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9638 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9639 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9640 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9641 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9642 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9643 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9645 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9647 int action_arg_direction =
9648 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9649 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9650 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9651 change->actual_trigger_side :
9652 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9653 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9656 int action_arg_number_min =
9657 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9660 int action_arg_number_max =
9661 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9662 action_type == CA_SET_LEVEL_GEMS ? 999 :
9663 action_type == CA_SET_LEVEL_TIME ? 9999 :
9664 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9665 action_type == CA_SET_CE_VALUE ? 9999 :
9666 action_type == CA_SET_CE_SCORE ? 9999 :
9669 int action_arg_number_reset =
9670 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9671 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9672 action_type == CA_SET_LEVEL_TIME ? level.time :
9673 action_type == CA_SET_LEVEL_SCORE ? 0 :
9674 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9675 action_type == CA_SET_CE_SCORE ? 0 :
9678 int action_arg_number =
9679 (action_arg <= CA_ARG_MAX ? action_arg :
9680 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9681 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9682 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9683 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9684 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9685 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9686 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9687 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9688 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9689 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9690 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9691 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9692 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9693 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9694 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9695 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9696 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9697 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9698 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9699 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9700 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9703 int action_arg_number_old =
9704 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9705 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9706 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9707 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9708 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9711 int action_arg_number_new =
9712 getModifiedActionNumber(action_arg_number_old,
9713 action_mode, action_arg_number,
9714 action_arg_number_min, action_arg_number_max);
9716 int trigger_player_bits =
9717 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9718 change->actual_trigger_player_bits : change->trigger_player);
9720 int action_arg_player_bits =
9721 (action_arg >= CA_ARG_PLAYER_1 &&
9722 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9723 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9724 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9727 /* ---------- execute action -------------------------------------------- */
9729 switch (action_type)
9736 /* ---------- level actions ------------------------------------------- */
9738 case CA_RESTART_LEVEL:
9740 game.restart_level = TRUE;
9745 case CA_SHOW_ENVELOPE:
9747 int element = getSpecialActionElement(action_arg_element,
9748 action_arg_number, EL_ENVELOPE_1);
9750 if (IS_ENVELOPE(element))
9751 local_player->show_envelope = element;
9756 case CA_SET_LEVEL_TIME:
9758 if (level.time > 0) /* only modify limited time value */
9760 TimeLeft = action_arg_number_new;
9762 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9764 DisplayGameControlValues();
9766 if (!TimeLeft && setup.time_limit)
9767 for (i = 0; i < MAX_PLAYERS; i++)
9768 KillPlayer(&stored_player[i]);
9774 case CA_SET_LEVEL_SCORE:
9776 local_player->score = action_arg_number_new;
9778 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9780 DisplayGameControlValues();
9785 case CA_SET_LEVEL_GEMS:
9787 local_player->gems_still_needed = action_arg_number_new;
9789 game.snapshot.collected_item = TRUE;
9791 game_panel_controls[GAME_PANEL_GEMS].value =
9792 local_player->gems_still_needed;
9794 DisplayGameControlValues();
9799 case CA_SET_LEVEL_WIND:
9801 game.wind_direction = action_arg_direction;
9806 case CA_SET_LEVEL_RANDOM_SEED:
9808 /* ensure that setting a new random seed while playing is predictable */
9809 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9814 /* ---------- player actions ------------------------------------------ */
9816 case CA_MOVE_PLAYER:
9818 /* automatically move to the next field in specified direction */
9819 for (i = 0; i < MAX_PLAYERS; i++)
9820 if (trigger_player_bits & (1 << i))
9821 stored_player[i].programmed_action = action_arg_direction;
9826 case CA_EXIT_PLAYER:
9828 for (i = 0; i < MAX_PLAYERS; i++)
9829 if (action_arg_player_bits & (1 << i))
9830 PlayerWins(&stored_player[i]);
9835 case CA_KILL_PLAYER:
9837 for (i = 0; i < MAX_PLAYERS; i++)
9838 if (action_arg_player_bits & (1 << i))
9839 KillPlayer(&stored_player[i]);
9844 case CA_SET_PLAYER_KEYS:
9846 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9847 int element = getSpecialActionElement(action_arg_element,
9848 action_arg_number, EL_KEY_1);
9850 if (IS_KEY(element))
9852 for (i = 0; i < MAX_PLAYERS; i++)
9854 if (trigger_player_bits & (1 << i))
9856 stored_player[i].key[KEY_NR(element)] = key_state;
9858 DrawGameDoorValues();
9866 case CA_SET_PLAYER_SPEED:
9868 for (i = 0; i < MAX_PLAYERS; i++)
9870 if (trigger_player_bits & (1 << i))
9872 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9874 if (action_arg == CA_ARG_SPEED_FASTER &&
9875 stored_player[i].cannot_move)
9877 action_arg_number = STEPSIZE_VERY_SLOW;
9879 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9880 action_arg == CA_ARG_SPEED_FASTER)
9882 action_arg_number = 2;
9883 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9886 else if (action_arg == CA_ARG_NUMBER_RESET)
9888 action_arg_number = level.initial_player_stepsize[i];
9892 getModifiedActionNumber(move_stepsize,
9895 action_arg_number_min,
9896 action_arg_number_max);
9898 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9905 case CA_SET_PLAYER_SHIELD:
9907 for (i = 0; i < MAX_PLAYERS; i++)
9909 if (trigger_player_bits & (1 << i))
9911 if (action_arg == CA_ARG_SHIELD_OFF)
9913 stored_player[i].shield_normal_time_left = 0;
9914 stored_player[i].shield_deadly_time_left = 0;
9916 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9918 stored_player[i].shield_normal_time_left = 999999;
9920 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9922 stored_player[i].shield_normal_time_left = 999999;
9923 stored_player[i].shield_deadly_time_left = 999999;
9931 case CA_SET_PLAYER_GRAVITY:
9933 for (i = 0; i < MAX_PLAYERS; i++)
9935 if (trigger_player_bits & (1 << i))
9937 stored_player[i].gravity =
9938 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9939 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9940 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9941 stored_player[i].gravity);
9948 case CA_SET_PLAYER_ARTWORK:
9950 for (i = 0; i < MAX_PLAYERS; i++)
9952 if (trigger_player_bits & (1 << i))
9954 int artwork_element = action_arg_element;
9956 if (action_arg == CA_ARG_ELEMENT_RESET)
9958 (level.use_artwork_element[i] ? level.artwork_element[i] :
9959 stored_player[i].element_nr);
9961 if (stored_player[i].artwork_element != artwork_element)
9962 stored_player[i].Frame = 0;
9964 stored_player[i].artwork_element = artwork_element;
9966 SetPlayerWaiting(&stored_player[i], FALSE);
9968 /* set number of special actions for bored and sleeping animation */
9969 stored_player[i].num_special_action_bored =
9970 get_num_special_action(artwork_element,
9971 ACTION_BORING_1, ACTION_BORING_LAST);
9972 stored_player[i].num_special_action_sleeping =
9973 get_num_special_action(artwork_element,
9974 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9981 case CA_SET_PLAYER_INVENTORY:
9983 for (i = 0; i < MAX_PLAYERS; i++)
9985 struct PlayerInfo *player = &stored_player[i];
9988 if (trigger_player_bits & (1 << i))
9990 int inventory_element = action_arg_element;
9992 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9993 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9994 action_arg == CA_ARG_ELEMENT_ACTION)
9996 int element = inventory_element;
9997 int collect_count = element_info[element].collect_count_initial;
9999 if (!IS_CUSTOM_ELEMENT(element))
10002 if (collect_count == 0)
10003 player->inventory_infinite_element = element;
10005 for (k = 0; k < collect_count; k++)
10006 if (player->inventory_size < MAX_INVENTORY_SIZE)
10007 player->inventory_element[player->inventory_size++] =
10010 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10011 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10012 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10014 if (player->inventory_infinite_element != EL_UNDEFINED &&
10015 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10016 action_arg_element_raw))
10017 player->inventory_infinite_element = EL_UNDEFINED;
10019 for (k = 0, j = 0; j < player->inventory_size; j++)
10021 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10022 action_arg_element_raw))
10023 player->inventory_element[k++] = player->inventory_element[j];
10026 player->inventory_size = k;
10028 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10030 if (player->inventory_size > 0)
10032 for (j = 0; j < player->inventory_size - 1; j++)
10033 player->inventory_element[j] = player->inventory_element[j + 1];
10035 player->inventory_size--;
10038 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10040 if (player->inventory_size > 0)
10041 player->inventory_size--;
10043 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10045 player->inventory_infinite_element = EL_UNDEFINED;
10046 player->inventory_size = 0;
10048 else if (action_arg == CA_ARG_INVENTORY_RESET)
10050 player->inventory_infinite_element = EL_UNDEFINED;
10051 player->inventory_size = 0;
10053 if (level.use_initial_inventory[i])
10055 for (j = 0; j < level.initial_inventory_size[i]; j++)
10057 int element = level.initial_inventory_content[i][j];
10058 int collect_count = element_info[element].collect_count_initial;
10060 if (!IS_CUSTOM_ELEMENT(element))
10063 if (collect_count == 0)
10064 player->inventory_infinite_element = element;
10066 for (k = 0; k < collect_count; k++)
10067 if (player->inventory_size < MAX_INVENTORY_SIZE)
10068 player->inventory_element[player->inventory_size++] =
10079 /* ---------- CE actions ---------------------------------------------- */
10081 case CA_SET_CE_VALUE:
10083 int last_ce_value = CustomValue[x][y];
10085 CustomValue[x][y] = action_arg_number_new;
10087 if (CustomValue[x][y] != last_ce_value)
10089 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10090 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10092 if (CustomValue[x][y] == 0)
10094 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10095 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10102 case CA_SET_CE_SCORE:
10104 int last_ce_score = ei->collect_score;
10106 ei->collect_score = action_arg_number_new;
10108 if (ei->collect_score != last_ce_score)
10110 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10111 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10113 if (ei->collect_score == 0)
10117 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10118 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10121 This is a very special case that seems to be a mixture between
10122 CheckElementChange() and CheckTriggeredElementChange(): while
10123 the first one only affects single elements that are triggered
10124 directly, the second one affects multiple elements in the playfield
10125 that are triggered indirectly by another element. This is a third
10126 case: Changing the CE score always affects multiple identical CEs,
10127 so every affected CE must be checked, not only the single CE for
10128 which the CE score was changed in the first place (as every instance
10129 of that CE shares the same CE score, and therefore also can change)!
10131 SCAN_PLAYFIELD(xx, yy)
10133 if (Feld[xx][yy] == element)
10134 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10135 CE_SCORE_GETS_ZERO);
10143 case CA_SET_CE_ARTWORK:
10145 int artwork_element = action_arg_element;
10146 boolean reset_frame = FALSE;
10149 if (action_arg == CA_ARG_ELEMENT_RESET)
10150 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10153 if (ei->gfx_element != artwork_element)
10154 reset_frame = TRUE;
10156 ei->gfx_element = artwork_element;
10158 SCAN_PLAYFIELD(xx, yy)
10160 if (Feld[xx][yy] == element)
10164 ResetGfxAnimation(xx, yy);
10165 ResetRandomAnimationValue(xx, yy);
10168 TEST_DrawLevelField(xx, yy);
10175 /* ---------- engine actions ------------------------------------------ */
10177 case CA_SET_ENGINE_SCAN_MODE:
10179 InitPlayfieldScanMode(action_arg);
10189 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10191 int old_element = Feld[x][y];
10192 int new_element = GetElementFromGroupElement(element);
10193 int previous_move_direction = MovDir[x][y];
10194 int last_ce_value = CustomValue[x][y];
10195 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10196 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10197 boolean add_player_onto_element = (new_element_is_player &&
10198 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10199 IS_WALKABLE(old_element));
10201 if (!add_player_onto_element)
10203 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10204 RemoveMovingField(x, y);
10208 Feld[x][y] = new_element;
10210 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10211 MovDir[x][y] = previous_move_direction;
10213 if (element_info[new_element].use_last_ce_value)
10214 CustomValue[x][y] = last_ce_value;
10216 InitField_WithBug1(x, y, FALSE);
10218 new_element = Feld[x][y]; /* element may have changed */
10220 ResetGfxAnimation(x, y);
10221 ResetRandomAnimationValue(x, y);
10223 TEST_DrawLevelField(x, y);
10225 if (GFX_CRUMBLED(new_element))
10226 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10229 /* check if element under the player changes from accessible to unaccessible
10230 (needed for special case of dropping element which then changes) */
10231 /* (must be checked after creating new element for walkable group elements) */
10232 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10233 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10240 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10241 if (new_element_is_player)
10242 RelocatePlayer(x, y, new_element);
10245 ChangeCount[x][y]++; /* count number of changes in the same frame */
10247 TestIfBadThingTouchesPlayer(x, y);
10248 TestIfPlayerTouchesCustomElement(x, y);
10249 TestIfElementTouchesCustomElement(x, y);
10252 static void CreateField(int x, int y, int element)
10254 CreateFieldExt(x, y, element, FALSE);
10257 static void CreateElementFromChange(int x, int y, int element)
10259 element = GET_VALID_RUNTIME_ELEMENT(element);
10261 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10263 int old_element = Feld[x][y];
10265 /* prevent changed element from moving in same engine frame
10266 unless both old and new element can either fall or move */
10267 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10268 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10272 CreateFieldExt(x, y, element, TRUE);
10275 static boolean ChangeElement(int x, int y, int element, int page)
10277 struct ElementInfo *ei = &element_info[element];
10278 struct ElementChangeInfo *change = &ei->change_page[page];
10279 int ce_value = CustomValue[x][y];
10280 int ce_score = ei->collect_score;
10281 int target_element;
10282 int old_element = Feld[x][y];
10284 /* always use default change event to prevent running into a loop */
10285 if (ChangeEvent[x][y] == -1)
10286 ChangeEvent[x][y] = CE_DELAY;
10288 if (ChangeEvent[x][y] == CE_DELAY)
10290 /* reset actual trigger element, trigger player and action element */
10291 change->actual_trigger_element = EL_EMPTY;
10292 change->actual_trigger_player = EL_EMPTY;
10293 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10294 change->actual_trigger_side = CH_SIDE_NONE;
10295 change->actual_trigger_ce_value = 0;
10296 change->actual_trigger_ce_score = 0;
10299 /* do not change elements more than a specified maximum number of changes */
10300 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10303 ChangeCount[x][y]++; /* count number of changes in the same frame */
10305 if (change->explode)
10312 if (change->use_target_content)
10314 boolean complete_replace = TRUE;
10315 boolean can_replace[3][3];
10318 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10321 boolean is_walkable;
10322 boolean is_diggable;
10323 boolean is_collectible;
10324 boolean is_removable;
10325 boolean is_destructible;
10326 int ex = x + xx - 1;
10327 int ey = y + yy - 1;
10328 int content_element = change->target_content.e[xx][yy];
10331 can_replace[xx][yy] = TRUE;
10333 if (ex == x && ey == y) /* do not check changing element itself */
10336 if (content_element == EL_EMPTY_SPACE)
10338 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10343 if (!IN_LEV_FIELD(ex, ey))
10345 can_replace[xx][yy] = FALSE;
10346 complete_replace = FALSE;
10353 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10354 e = MovingOrBlocked2Element(ex, ey);
10356 is_empty = (IS_FREE(ex, ey) ||
10357 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10359 is_walkable = (is_empty || IS_WALKABLE(e));
10360 is_diggable = (is_empty || IS_DIGGABLE(e));
10361 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10362 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10363 is_removable = (is_diggable || is_collectible);
10365 can_replace[xx][yy] =
10366 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10367 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10368 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10369 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10370 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10371 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10372 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10374 if (!can_replace[xx][yy])
10375 complete_replace = FALSE;
10378 if (!change->only_if_complete || complete_replace)
10380 boolean something_has_changed = FALSE;
10382 if (change->only_if_complete && change->use_random_replace &&
10383 RND(100) < change->random_percentage)
10386 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10388 int ex = x + xx - 1;
10389 int ey = y + yy - 1;
10390 int content_element;
10392 if (can_replace[xx][yy] && (!change->use_random_replace ||
10393 RND(100) < change->random_percentage))
10395 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10396 RemoveMovingField(ex, ey);
10398 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10400 content_element = change->target_content.e[xx][yy];
10401 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10402 ce_value, ce_score);
10404 CreateElementFromChange(ex, ey, target_element);
10406 something_has_changed = TRUE;
10408 /* for symmetry reasons, freeze newly created border elements */
10409 if (ex != x || ey != y)
10410 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10414 if (something_has_changed)
10416 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10417 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10423 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10424 ce_value, ce_score);
10426 if (element == EL_DIAGONAL_GROWING ||
10427 element == EL_DIAGONAL_SHRINKING)
10429 target_element = Store[x][y];
10431 Store[x][y] = EL_EMPTY;
10434 CreateElementFromChange(x, y, target_element);
10436 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10437 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10440 /* this uses direct change before indirect change */
10441 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10446 static void HandleElementChange(int x, int y, int page)
10448 int element = MovingOrBlocked2Element(x, y);
10449 struct ElementInfo *ei = &element_info[element];
10450 struct ElementChangeInfo *change = &ei->change_page[page];
10451 boolean handle_action_before_change = FALSE;
10454 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10455 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10458 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10459 x, y, element, element_info[element].token_name);
10460 printf("HandleElementChange(): This should never happen!\n");
10465 /* this can happen with classic bombs on walkable, changing elements */
10466 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10471 if (ChangeDelay[x][y] == 0) /* initialize element change */
10473 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10475 if (change->can_change)
10477 /* !!! not clear why graphic animation should be reset at all here !!! */
10478 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10479 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10482 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10484 When using an animation frame delay of 1 (this only happens with
10485 "sp_zonk.moving.left/right" in the classic graphics), the default
10486 (non-moving) animation shows wrong animation frames (while the
10487 moving animation, like "sp_zonk.moving.left/right", is correct,
10488 so this graphical bug never shows up with the classic graphics).
10489 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10490 be drawn instead of the correct frames 0,1,2,3. This is caused by
10491 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10492 an element change: First when the change delay ("ChangeDelay[][]")
10493 counter has reached zero after decrementing, then a second time in
10494 the next frame (after "GfxFrame[][]" was already incremented) when
10495 "ChangeDelay[][]" is reset to the initial delay value again.
10497 This causes frame 0 to be drawn twice, while the last frame won't
10498 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10500 As some animations may already be cleverly designed around this bug
10501 (at least the "Snake Bite" snake tail animation does this), it cannot
10502 simply be fixed here without breaking such existing animations.
10503 Unfortunately, it cannot easily be detected if a graphics set was
10504 designed "before" or "after" the bug was fixed. As a workaround,
10505 a new graphics set option "game.graphics_engine_version" was added
10506 to be able to specify the game's major release version for which the
10507 graphics set was designed, which can then be used to decide if the
10508 bugfix should be used (version 4 and above) or not (version 3 or
10509 below, or if no version was specified at all, as with old sets).
10511 (The wrong/fixed animation frames can be tested with the test level set
10512 "test_gfxframe" and level "000", which contains a specially prepared
10513 custom element at level position (x/y) == (11/9) which uses the zonk
10514 animation mentioned above. Using "game.graphics_engine_version: 4"
10515 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10516 This can also be seen from the debug output for this test element.)
10519 /* when a custom element is about to change (for example by change delay),
10520 do not reset graphic animation when the custom element is moving */
10521 if (game.graphics_engine_version < 4 &&
10524 ResetGfxAnimation(x, y);
10525 ResetRandomAnimationValue(x, y);
10528 if (change->pre_change_function)
10529 change->pre_change_function(x, y);
10533 ChangeDelay[x][y]--;
10535 if (ChangeDelay[x][y] != 0) /* continue element change */
10537 if (change->can_change)
10539 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10541 if (IS_ANIMATED(graphic))
10542 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10544 if (change->change_function)
10545 change->change_function(x, y);
10548 else /* finish element change */
10550 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10552 page = ChangePage[x][y];
10553 ChangePage[x][y] = -1;
10555 change = &ei->change_page[page];
10558 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10560 ChangeDelay[x][y] = 1; /* try change after next move step */
10561 ChangePage[x][y] = page; /* remember page to use for change */
10566 /* special case: set new level random seed before changing element */
10567 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10568 handle_action_before_change = TRUE;
10570 if (change->has_action && handle_action_before_change)
10571 ExecuteCustomElementAction(x, y, element, page);
10573 if (change->can_change)
10575 if (ChangeElement(x, y, element, page))
10577 if (change->post_change_function)
10578 change->post_change_function(x, y);
10582 if (change->has_action && !handle_action_before_change)
10583 ExecuteCustomElementAction(x, y, element, page);
10587 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10588 int trigger_element,
10590 int trigger_player,
10594 boolean change_done_any = FALSE;
10595 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10598 if (!(trigger_events[trigger_element][trigger_event]))
10601 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10603 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10605 int element = EL_CUSTOM_START + i;
10606 boolean change_done = FALSE;
10609 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10610 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10613 for (p = 0; p < element_info[element].num_change_pages; p++)
10615 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10617 if (change->can_change_or_has_action &&
10618 change->has_event[trigger_event] &&
10619 change->trigger_side & trigger_side &&
10620 change->trigger_player & trigger_player &&
10621 change->trigger_page & trigger_page_bits &&
10622 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10624 change->actual_trigger_element = trigger_element;
10625 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10626 change->actual_trigger_player_bits = trigger_player;
10627 change->actual_trigger_side = trigger_side;
10628 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10629 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10631 if ((change->can_change && !change_done) || change->has_action)
10635 SCAN_PLAYFIELD(x, y)
10637 if (Feld[x][y] == element)
10639 if (change->can_change && !change_done)
10641 /* if element already changed in this frame, not only prevent
10642 another element change (checked in ChangeElement()), but
10643 also prevent additional element actions for this element */
10645 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10646 !level.use_action_after_change_bug)
10649 ChangeDelay[x][y] = 1;
10650 ChangeEvent[x][y] = trigger_event;
10652 HandleElementChange(x, y, p);
10654 else if (change->has_action)
10656 /* if element already changed in this frame, not only prevent
10657 another element change (checked in ChangeElement()), but
10658 also prevent additional element actions for this element */
10660 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10661 !level.use_action_after_change_bug)
10664 ExecuteCustomElementAction(x, y, element, p);
10665 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10670 if (change->can_change)
10672 change_done = TRUE;
10673 change_done_any = TRUE;
10680 RECURSION_LOOP_DETECTION_END();
10682 return change_done_any;
10685 static boolean CheckElementChangeExt(int x, int y,
10687 int trigger_element,
10689 int trigger_player,
10692 boolean change_done = FALSE;
10695 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10696 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10699 if (Feld[x][y] == EL_BLOCKED)
10701 Blocked2Moving(x, y, &x, &y);
10702 element = Feld[x][y];
10705 /* check if element has already changed or is about to change after moving */
10706 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10707 Feld[x][y] != element) ||
10709 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10710 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10711 ChangePage[x][y] != -1)))
10714 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10716 for (p = 0; p < element_info[element].num_change_pages; p++)
10718 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10720 /* check trigger element for all events where the element that is checked
10721 for changing interacts with a directly adjacent element -- this is
10722 different to element changes that affect other elements to change on the
10723 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10724 boolean check_trigger_element =
10725 (trigger_event == CE_TOUCHING_X ||
10726 trigger_event == CE_HITTING_X ||
10727 trigger_event == CE_HIT_BY_X ||
10728 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10730 if (change->can_change_or_has_action &&
10731 change->has_event[trigger_event] &&
10732 change->trigger_side & trigger_side &&
10733 change->trigger_player & trigger_player &&
10734 (!check_trigger_element ||
10735 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10737 change->actual_trigger_element = trigger_element;
10738 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10739 change->actual_trigger_player_bits = trigger_player;
10740 change->actual_trigger_side = trigger_side;
10741 change->actual_trigger_ce_value = CustomValue[x][y];
10742 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10744 /* special case: trigger element not at (x,y) position for some events */
10745 if (check_trigger_element)
10757 { 0, 0 }, { 0, 0 }, { 0, 0 },
10761 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10762 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10764 change->actual_trigger_ce_value = CustomValue[xx][yy];
10765 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10768 if (change->can_change && !change_done)
10770 ChangeDelay[x][y] = 1;
10771 ChangeEvent[x][y] = trigger_event;
10773 HandleElementChange(x, y, p);
10775 change_done = TRUE;
10777 else if (change->has_action)
10779 ExecuteCustomElementAction(x, y, element, p);
10780 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10785 RECURSION_LOOP_DETECTION_END();
10787 return change_done;
10790 static void PlayPlayerSound(struct PlayerInfo *player)
10792 int jx = player->jx, jy = player->jy;
10793 int sound_element = player->artwork_element;
10794 int last_action = player->last_action_waiting;
10795 int action = player->action_waiting;
10797 if (player->is_waiting)
10799 if (action != last_action)
10800 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10802 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10806 if (action != last_action)
10807 StopSound(element_info[sound_element].sound[last_action]);
10809 if (last_action == ACTION_SLEEPING)
10810 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10814 static void PlayAllPlayersSound()
10818 for (i = 0; i < MAX_PLAYERS; i++)
10819 if (stored_player[i].active)
10820 PlayPlayerSound(&stored_player[i]);
10823 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10825 boolean last_waiting = player->is_waiting;
10826 int move_dir = player->MovDir;
10828 player->dir_waiting = move_dir;
10829 player->last_action_waiting = player->action_waiting;
10833 if (!last_waiting) /* not waiting -> waiting */
10835 player->is_waiting = TRUE;
10837 player->frame_counter_bored =
10839 game.player_boring_delay_fixed +
10840 GetSimpleRandom(game.player_boring_delay_random);
10841 player->frame_counter_sleeping =
10843 game.player_sleeping_delay_fixed +
10844 GetSimpleRandom(game.player_sleeping_delay_random);
10846 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10849 if (game.player_sleeping_delay_fixed +
10850 game.player_sleeping_delay_random > 0 &&
10851 player->anim_delay_counter == 0 &&
10852 player->post_delay_counter == 0 &&
10853 FrameCounter >= player->frame_counter_sleeping)
10854 player->is_sleeping = TRUE;
10855 else if (game.player_boring_delay_fixed +
10856 game.player_boring_delay_random > 0 &&
10857 FrameCounter >= player->frame_counter_bored)
10858 player->is_bored = TRUE;
10860 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10861 player->is_bored ? ACTION_BORING :
10864 if (player->is_sleeping && player->use_murphy)
10866 /* special case for sleeping Murphy when leaning against non-free tile */
10868 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10869 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10870 !IS_MOVING(player->jx - 1, player->jy)))
10871 move_dir = MV_LEFT;
10872 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10873 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10874 !IS_MOVING(player->jx + 1, player->jy)))
10875 move_dir = MV_RIGHT;
10877 player->is_sleeping = FALSE;
10879 player->dir_waiting = move_dir;
10882 if (player->is_sleeping)
10884 if (player->num_special_action_sleeping > 0)
10886 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10888 int last_special_action = player->special_action_sleeping;
10889 int num_special_action = player->num_special_action_sleeping;
10890 int special_action =
10891 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10892 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10893 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10894 last_special_action + 1 : ACTION_SLEEPING);
10895 int special_graphic =
10896 el_act_dir2img(player->artwork_element, special_action, move_dir);
10898 player->anim_delay_counter =
10899 graphic_info[special_graphic].anim_delay_fixed +
10900 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10901 player->post_delay_counter =
10902 graphic_info[special_graphic].post_delay_fixed +
10903 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10905 player->special_action_sleeping = special_action;
10908 if (player->anim_delay_counter > 0)
10910 player->action_waiting = player->special_action_sleeping;
10911 player->anim_delay_counter--;
10913 else if (player->post_delay_counter > 0)
10915 player->post_delay_counter--;
10919 else if (player->is_bored)
10921 if (player->num_special_action_bored > 0)
10923 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10925 int special_action =
10926 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10927 int special_graphic =
10928 el_act_dir2img(player->artwork_element, special_action, move_dir);
10930 player->anim_delay_counter =
10931 graphic_info[special_graphic].anim_delay_fixed +
10932 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10933 player->post_delay_counter =
10934 graphic_info[special_graphic].post_delay_fixed +
10935 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10937 player->special_action_bored = special_action;
10940 if (player->anim_delay_counter > 0)
10942 player->action_waiting = player->special_action_bored;
10943 player->anim_delay_counter--;
10945 else if (player->post_delay_counter > 0)
10947 player->post_delay_counter--;
10952 else if (last_waiting) /* waiting -> not waiting */
10954 player->is_waiting = FALSE;
10955 player->is_bored = FALSE;
10956 player->is_sleeping = FALSE;
10958 player->frame_counter_bored = -1;
10959 player->frame_counter_sleeping = -1;
10961 player->anim_delay_counter = 0;
10962 player->post_delay_counter = 0;
10964 player->dir_waiting = player->MovDir;
10965 player->action_waiting = ACTION_DEFAULT;
10967 player->special_action_bored = ACTION_DEFAULT;
10968 player->special_action_sleeping = ACTION_DEFAULT;
10972 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10974 if ((!player->is_moving && player->was_moving) ||
10975 (player->MovPos == 0 && player->was_moving) ||
10976 (player->is_snapping && !player->was_snapping) ||
10977 (player->is_dropping && !player->was_dropping))
10979 if (!CheckSaveEngineSnapshotToList())
10982 player->was_moving = FALSE;
10983 player->was_snapping = TRUE;
10984 player->was_dropping = TRUE;
10988 if (player->is_moving)
10989 player->was_moving = TRUE;
10991 if (!player->is_snapping)
10992 player->was_snapping = FALSE;
10994 if (!player->is_dropping)
10995 player->was_dropping = FALSE;
10999 static void CheckSingleStepMode(struct PlayerInfo *player)
11001 if (tape.single_step && tape.recording && !tape.pausing)
11003 /* as it is called "single step mode", just return to pause mode when the
11004 player stopped moving after one tile (or never starts moving at all) */
11005 if (!player->is_moving &&
11006 !player->is_pushing &&
11007 !player->is_dropping_pressed)
11009 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11010 SnapField(player, 0, 0); /* stop snapping */
11014 CheckSaveEngineSnapshot(player);
11017 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11019 int left = player_action & JOY_LEFT;
11020 int right = player_action & JOY_RIGHT;
11021 int up = player_action & JOY_UP;
11022 int down = player_action & JOY_DOWN;
11023 int button1 = player_action & JOY_BUTTON_1;
11024 int button2 = player_action & JOY_BUTTON_2;
11025 int dx = (left ? -1 : right ? 1 : 0);
11026 int dy = (up ? -1 : down ? 1 : 0);
11028 if (!player->active || tape.pausing)
11034 SnapField(player, dx, dy);
11038 DropElement(player);
11040 MovePlayer(player, dx, dy);
11043 CheckSingleStepMode(player);
11045 SetPlayerWaiting(player, FALSE);
11047 return player_action;
11051 /* no actions for this player (no input at player's configured device) */
11053 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11054 SnapField(player, 0, 0);
11055 CheckGravityMovementWhenNotMoving(player);
11057 if (player->MovPos == 0)
11058 SetPlayerWaiting(player, TRUE);
11060 if (player->MovPos == 0) /* needed for tape.playing */
11061 player->is_moving = FALSE;
11063 player->is_dropping = FALSE;
11064 player->is_dropping_pressed = FALSE;
11065 player->drop_pressed_delay = 0;
11067 CheckSingleStepMode(player);
11073 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11076 if (!tape.use_mouse)
11079 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11080 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11081 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11084 static void SetTapeActionFromMouseAction(byte *tape_action,
11085 struct MouseActionInfo *mouse_action)
11087 if (!tape.use_mouse)
11090 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11091 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11092 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11095 static void CheckLevelTime()
11099 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11100 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11102 if (level.native_em_level->lev->home == 0) /* all players at home */
11104 PlayerWins(local_player);
11106 AllPlayersGone = TRUE;
11108 level.native_em_level->lev->home = -1;
11111 if (level.native_em_level->ply[0]->alive == 0 &&
11112 level.native_em_level->ply[1]->alive == 0 &&
11113 level.native_em_level->ply[2]->alive == 0 &&
11114 level.native_em_level->ply[3]->alive == 0) /* all dead */
11115 AllPlayersGone = TRUE;
11117 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11119 if (game_sp.LevelSolved &&
11120 !game_sp.GameOver) /* game won */
11122 PlayerWins(local_player);
11124 game_sp.GameOver = TRUE;
11126 AllPlayersGone = TRUE;
11129 if (game_sp.GameOver) /* game lost */
11130 AllPlayersGone = TRUE;
11132 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11134 if (game_mm.level_solved &&
11135 !game_mm.game_over) /* game won */
11137 PlayerWins(local_player);
11139 game_mm.game_over = TRUE;
11141 AllPlayersGone = TRUE;
11144 if (game_mm.game_over) /* game lost */
11145 AllPlayersGone = TRUE;
11148 if (TimeFrames >= FRAMES_PER_SECOND)
11153 for (i = 0; i < MAX_PLAYERS; i++)
11155 struct PlayerInfo *player = &stored_player[i];
11157 if (SHIELD_ON(player))
11159 player->shield_normal_time_left--;
11161 if (player->shield_deadly_time_left > 0)
11162 player->shield_deadly_time_left--;
11166 if (!local_player->LevelSolved && !level.use_step_counter)
11174 if (TimeLeft <= 10 && setup.time_limit)
11175 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11177 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11178 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11180 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11182 if (!TimeLeft && setup.time_limit)
11184 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11185 level.native_em_level->lev->killed_out_of_time = TRUE;
11187 for (i = 0; i < MAX_PLAYERS; i++)
11188 KillPlayer(&stored_player[i]);
11191 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11193 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11196 level.native_em_level->lev->time =
11197 (game.no_time_limit ? TimePlayed : TimeLeft);
11200 if (tape.recording || tape.playing)
11201 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11204 if (tape.recording || tape.playing)
11205 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11207 UpdateAndDisplayGameControlValues();
11210 void AdvanceFrameAndPlayerCounters(int player_nr)
11214 /* advance frame counters (global frame counter and time frame counter) */
11218 /* advance player counters (counters for move delay, move animation etc.) */
11219 for (i = 0; i < MAX_PLAYERS; i++)
11221 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11222 int move_delay_value = stored_player[i].move_delay_value;
11223 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11225 if (!advance_player_counters) /* not all players may be affected */
11228 if (move_frames == 0) /* less than one move per game frame */
11230 int stepsize = TILEX / move_delay_value;
11231 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11232 int count = (stored_player[i].is_moving ?
11233 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11235 if (count % delay == 0)
11239 stored_player[i].Frame += move_frames;
11241 if (stored_player[i].MovPos != 0)
11242 stored_player[i].StepFrame += move_frames;
11244 if (stored_player[i].move_delay > 0)
11245 stored_player[i].move_delay--;
11247 /* due to bugs in previous versions, counter must count up, not down */
11248 if (stored_player[i].push_delay != -1)
11249 stored_player[i].push_delay++;
11251 if (stored_player[i].drop_delay > 0)
11252 stored_player[i].drop_delay--;
11254 if (stored_player[i].is_dropping_pressed)
11255 stored_player[i].drop_pressed_delay++;
11259 void StartGameActions(boolean init_network_game, boolean record_tape,
11262 unsigned int new_random_seed = InitRND(random_seed);
11265 TapeStartRecording(new_random_seed);
11267 #if defined(NETWORK_AVALIABLE)
11268 if (init_network_game)
11270 SendToServer_StartPlaying();
11279 void GameActionsExt()
11282 static unsigned int game_frame_delay = 0;
11284 unsigned int game_frame_delay_value;
11285 byte *recorded_player_action;
11286 byte summarized_player_action = 0;
11287 byte tape_action[MAX_PLAYERS];
11290 /* detect endless loops, caused by custom element programming */
11291 if (recursion_loop_detected && recursion_loop_depth == 0)
11293 char *message = getStringCat3("Internal Error! Element ",
11294 EL_NAME(recursion_loop_element),
11295 " caused endless loop! Quit the game?");
11297 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11298 EL_NAME(recursion_loop_element));
11300 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11302 recursion_loop_detected = FALSE; /* if game should be continued */
11309 if (game.restart_level)
11310 StartGameActions(options.network, setup.autorecord, level.random_seed);
11312 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11313 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11315 if (level.native_em_level->lev->home == 0) /* all players at home */
11317 PlayerWins(local_player);
11319 AllPlayersGone = TRUE;
11321 level.native_em_level->lev->home = -1;
11324 if (level.native_em_level->ply[0]->alive == 0 &&
11325 level.native_em_level->ply[1]->alive == 0 &&
11326 level.native_em_level->ply[2]->alive == 0 &&
11327 level.native_em_level->ply[3]->alive == 0) /* all dead */
11328 AllPlayersGone = TRUE;
11330 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11332 if (game_sp.LevelSolved &&
11333 !game_sp.GameOver) /* game won */
11335 PlayerWins(local_player);
11337 game_sp.GameOver = TRUE;
11339 AllPlayersGone = TRUE;
11342 if (game_sp.GameOver) /* game lost */
11343 AllPlayersGone = TRUE;
11345 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11347 if (game_mm.level_solved &&
11348 !game_mm.game_over) /* game won */
11350 PlayerWins(local_player);
11352 game_mm.game_over = TRUE;
11354 AllPlayersGone = TRUE;
11357 if (game_mm.game_over) /* game lost */
11358 AllPlayersGone = TRUE;
11361 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11364 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11367 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11370 game_frame_delay_value =
11371 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11373 if (tape.playing && tape.warp_forward && !tape.pausing)
11374 game_frame_delay_value = 0;
11376 SetVideoFrameDelay(game_frame_delay_value);
11380 /* ---------- main game synchronization point ---------- */
11382 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11384 printf("::: skip == %d\n", skip);
11387 /* ---------- main game synchronization point ---------- */
11389 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11393 if (network_playing && !network_player_action_received)
11395 /* try to get network player actions in time */
11397 #if defined(NETWORK_AVALIABLE)
11398 /* last chance to get network player actions without main loop delay */
11399 HandleNetworking();
11402 /* game was quit by network peer */
11403 if (game_status != GAME_MODE_PLAYING)
11406 if (!network_player_action_received)
11407 return; /* failed to get network player actions in time */
11409 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11415 /* at this point we know that we really continue executing the game */
11417 network_player_action_received = FALSE;
11419 /* when playing tape, read previously recorded player input from tape data */
11420 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11422 local_player->effective_mouse_action = local_player->mouse_action;
11424 if (recorded_player_action != NULL)
11425 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11426 recorded_player_action);
11428 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11432 if (tape.set_centered_player)
11434 game.centered_player_nr_next = tape.centered_player_nr_next;
11435 game.set_centered_player = TRUE;
11438 for (i = 0; i < MAX_PLAYERS; i++)
11440 summarized_player_action |= stored_player[i].action;
11442 if (!network_playing && (game.team_mode || tape.playing))
11443 stored_player[i].effective_action = stored_player[i].action;
11446 #if defined(NETWORK_AVALIABLE)
11447 if (network_playing)
11448 SendToServer_MovePlayer(summarized_player_action);
11451 // summarize all actions at local players mapped input device position
11452 // (this allows using different input devices in single player mode)
11453 if (!options.network && !game.team_mode)
11454 stored_player[map_player_action[local_player->index_nr]].effective_action =
11455 summarized_player_action;
11457 if (tape.recording &&
11459 setup.input_on_focus &&
11460 game.centered_player_nr != -1)
11462 for (i = 0; i < MAX_PLAYERS; i++)
11463 stored_player[i].effective_action =
11464 (i == game.centered_player_nr ? summarized_player_action : 0);
11467 if (recorded_player_action != NULL)
11468 for (i = 0; i < MAX_PLAYERS; i++)
11469 stored_player[i].effective_action = recorded_player_action[i];
11471 for (i = 0; i < MAX_PLAYERS; i++)
11473 tape_action[i] = stored_player[i].effective_action;
11475 /* (this may happen in the RND game engine if a player was not present on
11476 the playfield on level start, but appeared later from a custom element */
11477 if (setup.team_mode &&
11480 !tape.player_participates[i])
11481 tape.player_participates[i] = TRUE;
11484 SetTapeActionFromMouseAction(tape_action,
11485 &local_player->effective_mouse_action);
11487 /* only record actions from input devices, but not programmed actions */
11488 if (tape.recording)
11489 TapeRecordAction(tape_action);
11491 #if USE_NEW_PLAYER_ASSIGNMENTS
11492 // !!! also map player actions in single player mode !!!
11493 // if (game.team_mode)
11496 byte mapped_action[MAX_PLAYERS];
11498 #if DEBUG_PLAYER_ACTIONS
11500 for (i = 0; i < MAX_PLAYERS; i++)
11501 printf(" %d, ", stored_player[i].effective_action);
11504 for (i = 0; i < MAX_PLAYERS; i++)
11505 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11507 for (i = 0; i < MAX_PLAYERS; i++)
11508 stored_player[i].effective_action = mapped_action[i];
11510 #if DEBUG_PLAYER_ACTIONS
11512 for (i = 0; i < MAX_PLAYERS; i++)
11513 printf(" %d, ", stored_player[i].effective_action);
11517 #if DEBUG_PLAYER_ACTIONS
11521 for (i = 0; i < MAX_PLAYERS; i++)
11522 printf(" %d, ", stored_player[i].effective_action);
11528 for (i = 0; i < MAX_PLAYERS; i++)
11530 // allow engine snapshot in case of changed movement attempt
11531 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11532 (stored_player[i].effective_action & KEY_MOTION))
11533 game.snapshot.changed_action = TRUE;
11535 // allow engine snapshot in case of snapping/dropping attempt
11536 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11537 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11538 game.snapshot.changed_action = TRUE;
11540 game.snapshot.last_action[i] = stored_player[i].effective_action;
11543 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11545 GameActions_EM_Main();
11547 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11549 GameActions_SP_Main();
11551 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11553 GameActions_MM_Main();
11557 GameActions_RND_Main();
11560 BlitScreenToBitmap(backbuffer);
11564 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11566 if (global.show_frames_per_second)
11568 static unsigned int fps_counter = 0;
11569 static int fps_frames = 0;
11570 unsigned int fps_delay_ms = Counter() - fps_counter;
11574 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11576 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11579 fps_counter = Counter();
11581 /* always draw FPS to screen after FPS value was updated */
11582 redraw_mask |= REDRAW_FPS;
11585 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11586 if (GetDrawDeactivationMask() == REDRAW_NONE)
11587 redraw_mask |= REDRAW_FPS;
11591 static void GameActions_CheckSaveEngineSnapshot()
11593 if (!game.snapshot.save_snapshot)
11596 // clear flag for saving snapshot _before_ saving snapshot
11597 game.snapshot.save_snapshot = FALSE;
11599 SaveEngineSnapshotToList();
11606 GameActions_CheckSaveEngineSnapshot();
11609 void GameActions_EM_Main()
11611 byte effective_action[MAX_PLAYERS];
11612 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11615 for (i = 0; i < MAX_PLAYERS; i++)
11616 effective_action[i] = stored_player[i].effective_action;
11618 GameActions_EM(effective_action, warp_mode);
11621 void GameActions_SP_Main()
11623 byte effective_action[MAX_PLAYERS];
11624 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11627 for (i = 0; i < MAX_PLAYERS; i++)
11628 effective_action[i] = stored_player[i].effective_action;
11630 GameActions_SP(effective_action, warp_mode);
11632 for (i = 0; i < MAX_PLAYERS; i++)
11634 if (stored_player[i].force_dropping)
11635 stored_player[i].action |= KEY_BUTTON_DROP;
11637 stored_player[i].force_dropping = FALSE;
11641 void GameActions_MM_Main()
11643 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11645 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11648 void GameActions_RND_Main()
11653 void GameActions_RND()
11655 int magic_wall_x = 0, magic_wall_y = 0;
11656 int i, x, y, element, graphic, last_gfx_frame;
11658 InitPlayfieldScanModeVars();
11660 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11662 SCAN_PLAYFIELD(x, y)
11664 ChangeCount[x][y] = 0;
11665 ChangeEvent[x][y] = -1;
11669 if (game.set_centered_player)
11671 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11673 /* switching to "all players" only possible if all players fit to screen */
11674 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11676 game.centered_player_nr_next = game.centered_player_nr;
11677 game.set_centered_player = FALSE;
11680 /* do not switch focus to non-existing (or non-active) player */
11681 if (game.centered_player_nr_next >= 0 &&
11682 !stored_player[game.centered_player_nr_next].active)
11684 game.centered_player_nr_next = game.centered_player_nr;
11685 game.set_centered_player = FALSE;
11689 if (game.set_centered_player &&
11690 ScreenMovPos == 0) /* screen currently aligned at tile position */
11694 if (game.centered_player_nr_next == -1)
11696 setScreenCenteredToAllPlayers(&sx, &sy);
11700 sx = stored_player[game.centered_player_nr_next].jx;
11701 sy = stored_player[game.centered_player_nr_next].jy;
11704 game.centered_player_nr = game.centered_player_nr_next;
11705 game.set_centered_player = FALSE;
11707 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11708 DrawGameDoorValues();
11711 for (i = 0; i < MAX_PLAYERS; i++)
11713 int actual_player_action = stored_player[i].effective_action;
11716 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11717 - rnd_equinox_tetrachloride 048
11718 - rnd_equinox_tetrachloride_ii 096
11719 - rnd_emanuel_schmieg 002
11720 - doctor_sloan_ww 001, 020
11722 if (stored_player[i].MovPos == 0)
11723 CheckGravityMovement(&stored_player[i]);
11726 /* overwrite programmed action with tape action */
11727 if (stored_player[i].programmed_action)
11728 actual_player_action = stored_player[i].programmed_action;
11730 PlayerActions(&stored_player[i], actual_player_action);
11732 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11735 ScrollScreen(NULL, SCROLL_GO_ON);
11737 /* for backwards compatibility, the following code emulates a fixed bug that
11738 occured when pushing elements (causing elements that just made their last
11739 pushing step to already (if possible) make their first falling step in the
11740 same game frame, which is bad); this code is also needed to use the famous
11741 "spring push bug" which is used in older levels and might be wanted to be
11742 used also in newer levels, but in this case the buggy pushing code is only
11743 affecting the "spring" element and no other elements */
11745 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11747 for (i = 0; i < MAX_PLAYERS; i++)
11749 struct PlayerInfo *player = &stored_player[i];
11750 int x = player->jx;
11751 int y = player->jy;
11753 if (player->active && player->is_pushing && player->is_moving &&
11755 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11756 Feld[x][y] == EL_SPRING))
11758 ContinueMoving(x, y);
11760 /* continue moving after pushing (this is actually a bug) */
11761 if (!IS_MOVING(x, y))
11762 Stop[x][y] = FALSE;
11767 SCAN_PLAYFIELD(x, y)
11769 ChangeCount[x][y] = 0;
11770 ChangeEvent[x][y] = -1;
11772 /* this must be handled before main playfield loop */
11773 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11776 if (MovDelay[x][y] <= 0)
11780 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11783 if (MovDelay[x][y] <= 0)
11786 TEST_DrawLevelField(x, y);
11788 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11793 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11795 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11796 printf("GameActions(): This should never happen!\n");
11798 ChangePage[x][y] = -1;
11802 Stop[x][y] = FALSE;
11803 if (WasJustMoving[x][y] > 0)
11804 WasJustMoving[x][y]--;
11805 if (WasJustFalling[x][y] > 0)
11806 WasJustFalling[x][y]--;
11807 if (CheckCollision[x][y] > 0)
11808 CheckCollision[x][y]--;
11809 if (CheckImpact[x][y] > 0)
11810 CheckImpact[x][y]--;
11814 /* reset finished pushing action (not done in ContinueMoving() to allow
11815 continuous pushing animation for elements with zero push delay) */
11816 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11818 ResetGfxAnimation(x, y);
11819 TEST_DrawLevelField(x, y);
11823 if (IS_BLOCKED(x, y))
11827 Blocked2Moving(x, y, &oldx, &oldy);
11828 if (!IS_MOVING(oldx, oldy))
11830 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11831 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11832 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11833 printf("GameActions(): This should never happen!\n");
11839 SCAN_PLAYFIELD(x, y)
11841 element = Feld[x][y];
11842 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11843 last_gfx_frame = GfxFrame[x][y];
11845 ResetGfxFrame(x, y);
11847 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11848 DrawLevelGraphicAnimation(x, y, graphic);
11850 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11851 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11852 ResetRandomAnimationValue(x, y);
11854 SetRandomAnimationValue(x, y);
11856 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11858 if (IS_INACTIVE(element))
11860 if (IS_ANIMATED(graphic))
11861 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11866 /* this may take place after moving, so 'element' may have changed */
11867 if (IS_CHANGING(x, y) &&
11868 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11870 int page = element_info[element].event_page_nr[CE_DELAY];
11872 HandleElementChange(x, y, page);
11874 element = Feld[x][y];
11875 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11878 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11882 element = Feld[x][y];
11883 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11885 if (IS_ANIMATED(graphic) &&
11886 !IS_MOVING(x, y) &&
11888 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11890 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11891 TEST_DrawTwinkleOnField(x, y);
11893 else if (element == EL_ACID)
11896 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11898 else if ((element == EL_EXIT_OPEN ||
11899 element == EL_EM_EXIT_OPEN ||
11900 element == EL_SP_EXIT_OPEN ||
11901 element == EL_STEEL_EXIT_OPEN ||
11902 element == EL_EM_STEEL_EXIT_OPEN ||
11903 element == EL_SP_TERMINAL ||
11904 element == EL_SP_TERMINAL_ACTIVE ||
11905 element == EL_EXTRA_TIME ||
11906 element == EL_SHIELD_NORMAL ||
11907 element == EL_SHIELD_DEADLY) &&
11908 IS_ANIMATED(graphic))
11909 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11910 else if (IS_MOVING(x, y))
11911 ContinueMoving(x, y);
11912 else if (IS_ACTIVE_BOMB(element))
11913 CheckDynamite(x, y);
11914 else if (element == EL_AMOEBA_GROWING)
11915 AmoebeWaechst(x, y);
11916 else if (element == EL_AMOEBA_SHRINKING)
11917 AmoebaDisappearing(x, y);
11919 #if !USE_NEW_AMOEBA_CODE
11920 else if (IS_AMOEBALIVE(element))
11921 AmoebeAbleger(x, y);
11924 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11926 else if (element == EL_EXIT_CLOSED)
11928 else if (element == EL_EM_EXIT_CLOSED)
11930 else if (element == EL_STEEL_EXIT_CLOSED)
11931 CheckExitSteel(x, y);
11932 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11933 CheckExitSteelEM(x, y);
11934 else if (element == EL_SP_EXIT_CLOSED)
11936 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11937 element == EL_EXPANDABLE_STEELWALL_GROWING)
11938 MauerWaechst(x, y);
11939 else if (element == EL_EXPANDABLE_WALL ||
11940 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11941 element == EL_EXPANDABLE_WALL_VERTICAL ||
11942 element == EL_EXPANDABLE_WALL_ANY ||
11943 element == EL_BD_EXPANDABLE_WALL)
11944 MauerAbleger(x, y);
11945 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11946 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11947 element == EL_EXPANDABLE_STEELWALL_ANY)
11948 MauerAblegerStahl(x, y);
11949 else if (element == EL_FLAMES)
11950 CheckForDragon(x, y);
11951 else if (element == EL_EXPLOSION)
11952 ; /* drawing of correct explosion animation is handled separately */
11953 else if (element == EL_ELEMENT_SNAPPING ||
11954 element == EL_DIAGONAL_SHRINKING ||
11955 element == EL_DIAGONAL_GROWING)
11957 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11959 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11961 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11962 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11964 if (IS_BELT_ACTIVE(element))
11965 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11967 if (game.magic_wall_active)
11969 int jx = local_player->jx, jy = local_player->jy;
11971 /* play the element sound at the position nearest to the player */
11972 if ((element == EL_MAGIC_WALL_FULL ||
11973 element == EL_MAGIC_WALL_ACTIVE ||
11974 element == EL_MAGIC_WALL_EMPTYING ||
11975 element == EL_BD_MAGIC_WALL_FULL ||
11976 element == EL_BD_MAGIC_WALL_ACTIVE ||
11977 element == EL_BD_MAGIC_WALL_EMPTYING ||
11978 element == EL_DC_MAGIC_WALL_FULL ||
11979 element == EL_DC_MAGIC_WALL_ACTIVE ||
11980 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11981 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11989 #if USE_NEW_AMOEBA_CODE
11990 /* new experimental amoeba growth stuff */
11991 if (!(FrameCounter % 8))
11993 static unsigned int random = 1684108901;
11995 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11997 x = RND(lev_fieldx);
11998 y = RND(lev_fieldy);
11999 element = Feld[x][y];
12001 if (!IS_PLAYER(x,y) &&
12002 (element == EL_EMPTY ||
12003 CAN_GROW_INTO(element) ||
12004 element == EL_QUICKSAND_EMPTY ||
12005 element == EL_QUICKSAND_FAST_EMPTY ||
12006 element == EL_ACID_SPLASH_LEFT ||
12007 element == EL_ACID_SPLASH_RIGHT))
12009 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12010 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12011 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12012 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12013 Feld[x][y] = EL_AMOEBA_DROP;
12016 random = random * 129 + 1;
12021 game.explosions_delayed = FALSE;
12023 SCAN_PLAYFIELD(x, y)
12025 element = Feld[x][y];
12027 if (ExplodeField[x][y])
12028 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12029 else if (element == EL_EXPLOSION)
12030 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12032 ExplodeField[x][y] = EX_TYPE_NONE;
12035 game.explosions_delayed = TRUE;
12037 if (game.magic_wall_active)
12039 if (!(game.magic_wall_time_left % 4))
12041 int element = Feld[magic_wall_x][magic_wall_y];
12043 if (element == EL_BD_MAGIC_WALL_FULL ||
12044 element == EL_BD_MAGIC_WALL_ACTIVE ||
12045 element == EL_BD_MAGIC_WALL_EMPTYING)
12046 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12047 else if (element == EL_DC_MAGIC_WALL_FULL ||
12048 element == EL_DC_MAGIC_WALL_ACTIVE ||
12049 element == EL_DC_MAGIC_WALL_EMPTYING)
12050 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12052 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12055 if (game.magic_wall_time_left > 0)
12057 game.magic_wall_time_left--;
12059 if (!game.magic_wall_time_left)
12061 SCAN_PLAYFIELD(x, y)
12063 element = Feld[x][y];
12065 if (element == EL_MAGIC_WALL_ACTIVE ||
12066 element == EL_MAGIC_WALL_FULL)
12068 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12069 TEST_DrawLevelField(x, y);
12071 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12072 element == EL_BD_MAGIC_WALL_FULL)
12074 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12075 TEST_DrawLevelField(x, y);
12077 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12078 element == EL_DC_MAGIC_WALL_FULL)
12080 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12081 TEST_DrawLevelField(x, y);
12085 game.magic_wall_active = FALSE;
12090 if (game.light_time_left > 0)
12092 game.light_time_left--;
12094 if (game.light_time_left == 0)
12095 RedrawAllLightSwitchesAndInvisibleElements();
12098 if (game.timegate_time_left > 0)
12100 game.timegate_time_left--;
12102 if (game.timegate_time_left == 0)
12103 CloseAllOpenTimegates();
12106 if (game.lenses_time_left > 0)
12108 game.lenses_time_left--;
12110 if (game.lenses_time_left == 0)
12111 RedrawAllInvisibleElementsForLenses();
12114 if (game.magnify_time_left > 0)
12116 game.magnify_time_left--;
12118 if (game.magnify_time_left == 0)
12119 RedrawAllInvisibleElementsForMagnifier();
12122 for (i = 0; i < MAX_PLAYERS; i++)
12124 struct PlayerInfo *player = &stored_player[i];
12126 if (SHIELD_ON(player))
12128 if (player->shield_deadly_time_left)
12129 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12130 else if (player->shield_normal_time_left)
12131 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12135 #if USE_DELAYED_GFX_REDRAW
12136 SCAN_PLAYFIELD(x, y)
12138 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12140 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12141 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12143 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12144 DrawLevelField(x, y);
12146 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12147 DrawLevelFieldCrumbled(x, y);
12149 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12150 DrawLevelFieldCrumbledNeighbours(x, y);
12152 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12153 DrawTwinkleOnField(x, y);
12156 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12161 PlayAllPlayersSound();
12163 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12165 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12167 local_player->show_envelope = 0;
12170 /* use random number generator in every frame to make it less predictable */
12171 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12175 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12177 int min_x = x, min_y = y, max_x = x, max_y = y;
12180 for (i = 0; i < MAX_PLAYERS; i++)
12182 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12184 if (!stored_player[i].active || &stored_player[i] == player)
12187 min_x = MIN(min_x, jx);
12188 min_y = MIN(min_y, jy);
12189 max_x = MAX(max_x, jx);
12190 max_y = MAX(max_y, jy);
12193 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12196 static boolean AllPlayersInVisibleScreen()
12200 for (i = 0; i < MAX_PLAYERS; i++)
12202 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12204 if (!stored_player[i].active)
12207 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12214 void ScrollLevel(int dx, int dy)
12216 int scroll_offset = 2 * TILEX_VAR;
12219 BlitBitmap(drawto_field, drawto_field,
12220 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12221 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12222 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12223 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12224 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12225 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12229 x = (dx == 1 ? BX1 : BX2);
12230 for (y = BY1; y <= BY2; y++)
12231 DrawScreenField(x, y);
12236 y = (dy == 1 ? BY1 : BY2);
12237 for (x = BX1; x <= BX2; x++)
12238 DrawScreenField(x, y);
12241 redraw_mask |= REDRAW_FIELD;
12244 static boolean canFallDown(struct PlayerInfo *player)
12246 int jx = player->jx, jy = player->jy;
12248 return (IN_LEV_FIELD(jx, jy + 1) &&
12249 (IS_FREE(jx, jy + 1) ||
12250 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12251 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12252 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12255 static boolean canPassField(int x, int y, int move_dir)
12257 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12258 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12259 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12260 int nextx = x + dx;
12261 int nexty = y + dy;
12262 int element = Feld[x][y];
12264 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12265 !CAN_MOVE(element) &&
12266 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12267 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12268 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12271 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12273 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12274 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12275 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12279 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12280 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12281 (IS_DIGGABLE(Feld[newx][newy]) ||
12282 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12283 canPassField(newx, newy, move_dir)));
12286 static void CheckGravityMovement(struct PlayerInfo *player)
12288 if (player->gravity && !player->programmed_action)
12290 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12291 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12292 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12293 int jx = player->jx, jy = player->jy;
12294 boolean player_is_moving_to_valid_field =
12295 (!player_is_snapping &&
12296 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12297 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12298 boolean player_can_fall_down = canFallDown(player);
12300 if (player_can_fall_down &&
12301 !player_is_moving_to_valid_field)
12302 player->programmed_action = MV_DOWN;
12306 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12308 return CheckGravityMovement(player);
12310 if (player->gravity && !player->programmed_action)
12312 int jx = player->jx, jy = player->jy;
12313 boolean field_under_player_is_free =
12314 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12315 boolean player_is_standing_on_valid_field =
12316 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12317 (IS_WALKABLE(Feld[jx][jy]) &&
12318 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12320 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12321 player->programmed_action = MV_DOWN;
12326 MovePlayerOneStep()
12327 -----------------------------------------------------------------------------
12328 dx, dy: direction (non-diagonal) to try to move the player to
12329 real_dx, real_dy: direction as read from input device (can be diagonal)
12332 boolean MovePlayerOneStep(struct PlayerInfo *player,
12333 int dx, int dy, int real_dx, int real_dy)
12335 int jx = player->jx, jy = player->jy;
12336 int new_jx = jx + dx, new_jy = jy + dy;
12338 boolean player_can_move = !player->cannot_move;
12340 if (!player->active || (!dx && !dy))
12341 return MP_NO_ACTION;
12343 player->MovDir = (dx < 0 ? MV_LEFT :
12344 dx > 0 ? MV_RIGHT :
12346 dy > 0 ? MV_DOWN : MV_NONE);
12348 if (!IN_LEV_FIELD(new_jx, new_jy))
12349 return MP_NO_ACTION;
12351 if (!player_can_move)
12353 if (player->MovPos == 0)
12355 player->is_moving = FALSE;
12356 player->is_digging = FALSE;
12357 player->is_collecting = FALSE;
12358 player->is_snapping = FALSE;
12359 player->is_pushing = FALSE;
12363 if (!options.network && game.centered_player_nr == -1 &&
12364 !AllPlayersInSight(player, new_jx, new_jy))
12365 return MP_NO_ACTION;
12367 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12368 if (can_move != MP_MOVING)
12371 /* check if DigField() has caused relocation of the player */
12372 if (player->jx != jx || player->jy != jy)
12373 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12375 StorePlayer[jx][jy] = 0;
12376 player->last_jx = jx;
12377 player->last_jy = jy;
12378 player->jx = new_jx;
12379 player->jy = new_jy;
12380 StorePlayer[new_jx][new_jy] = player->element_nr;
12382 if (player->move_delay_value_next != -1)
12384 player->move_delay_value = player->move_delay_value_next;
12385 player->move_delay_value_next = -1;
12389 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12391 player->step_counter++;
12393 PlayerVisit[jx][jy] = FrameCounter;
12395 player->is_moving = TRUE;
12398 /* should better be called in MovePlayer(), but this breaks some tapes */
12399 ScrollPlayer(player, SCROLL_INIT);
12405 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12407 int jx = player->jx, jy = player->jy;
12408 int old_jx = jx, old_jy = jy;
12409 int moved = MP_NO_ACTION;
12411 if (!player->active)
12416 if (player->MovPos == 0)
12418 player->is_moving = FALSE;
12419 player->is_digging = FALSE;
12420 player->is_collecting = FALSE;
12421 player->is_snapping = FALSE;
12422 player->is_pushing = FALSE;
12428 if (player->move_delay > 0)
12431 player->move_delay = -1; /* set to "uninitialized" value */
12433 /* store if player is automatically moved to next field */
12434 player->is_auto_moving = (player->programmed_action != MV_NONE);
12436 /* remove the last programmed player action */
12437 player->programmed_action = 0;
12439 if (player->MovPos)
12441 /* should only happen if pre-1.2 tape recordings are played */
12442 /* this is only for backward compatibility */
12444 int original_move_delay_value = player->move_delay_value;
12447 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12451 /* scroll remaining steps with finest movement resolution */
12452 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12454 while (player->MovPos)
12456 ScrollPlayer(player, SCROLL_GO_ON);
12457 ScrollScreen(NULL, SCROLL_GO_ON);
12459 AdvanceFrameAndPlayerCounters(player->index_nr);
12462 BackToFront_WithFrameDelay(0);
12465 player->move_delay_value = original_move_delay_value;
12468 player->is_active = FALSE;
12470 if (player->last_move_dir & MV_HORIZONTAL)
12472 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12473 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12477 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12478 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12481 if (!moved && !player->is_active)
12483 player->is_moving = FALSE;
12484 player->is_digging = FALSE;
12485 player->is_collecting = FALSE;
12486 player->is_snapping = FALSE;
12487 player->is_pushing = FALSE;
12493 if (moved & MP_MOVING && !ScreenMovPos &&
12494 (player->index_nr == game.centered_player_nr ||
12495 game.centered_player_nr == -1))
12497 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12498 int offset = game.scroll_delay_value;
12500 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12502 /* actual player has left the screen -- scroll in that direction */
12503 if (jx != old_jx) /* player has moved horizontally */
12504 scroll_x += (jx - old_jx);
12505 else /* player has moved vertically */
12506 scroll_y += (jy - old_jy);
12510 if (jx != old_jx) /* player has moved horizontally */
12512 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12513 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12514 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12516 /* don't scroll over playfield boundaries */
12517 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12518 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12520 /* don't scroll more than one field at a time */
12521 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12523 /* don't scroll against the player's moving direction */
12524 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12525 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12526 scroll_x = old_scroll_x;
12528 else /* player has moved vertically */
12530 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12531 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12532 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12534 /* don't scroll over playfield boundaries */
12535 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12536 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12538 /* don't scroll more than one field at a time */
12539 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12541 /* don't scroll against the player's moving direction */
12542 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12543 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12544 scroll_y = old_scroll_y;
12548 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12550 if (!options.network && game.centered_player_nr == -1 &&
12551 !AllPlayersInVisibleScreen())
12553 scroll_x = old_scroll_x;
12554 scroll_y = old_scroll_y;
12558 ScrollScreen(player, SCROLL_INIT);
12559 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12564 player->StepFrame = 0;
12566 if (moved & MP_MOVING)
12568 if (old_jx != jx && old_jy == jy)
12569 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12570 else if (old_jx == jx && old_jy != jy)
12571 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12573 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12575 player->last_move_dir = player->MovDir;
12576 player->is_moving = TRUE;
12577 player->is_snapping = FALSE;
12578 player->is_switching = FALSE;
12579 player->is_dropping = FALSE;
12580 player->is_dropping_pressed = FALSE;
12581 player->drop_pressed_delay = 0;
12584 /* should better be called here than above, but this breaks some tapes */
12585 ScrollPlayer(player, SCROLL_INIT);
12590 CheckGravityMovementWhenNotMoving(player);
12592 player->is_moving = FALSE;
12594 /* at this point, the player is allowed to move, but cannot move right now
12595 (e.g. because of something blocking the way) -- ensure that the player
12596 is also allowed to move in the next frame (in old versions before 3.1.1,
12597 the player was forced to wait again for eight frames before next try) */
12599 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12600 player->move_delay = 0; /* allow direct movement in the next frame */
12603 if (player->move_delay == -1) /* not yet initialized by DigField() */
12604 player->move_delay = player->move_delay_value;
12606 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12608 TestIfPlayerTouchesBadThing(jx, jy);
12609 TestIfPlayerTouchesCustomElement(jx, jy);
12612 if (!player->active)
12613 RemovePlayer(player);
12618 void ScrollPlayer(struct PlayerInfo *player, int mode)
12620 int jx = player->jx, jy = player->jy;
12621 int last_jx = player->last_jx, last_jy = player->last_jy;
12622 int move_stepsize = TILEX / player->move_delay_value;
12624 if (!player->active)
12627 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12630 if (mode == SCROLL_INIT)
12632 player->actual_frame_counter = FrameCounter;
12633 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12635 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12636 Feld[last_jx][last_jy] == EL_EMPTY)
12638 int last_field_block_delay = 0; /* start with no blocking at all */
12639 int block_delay_adjustment = player->block_delay_adjustment;
12641 /* if player blocks last field, add delay for exactly one move */
12642 if (player->block_last_field)
12644 last_field_block_delay += player->move_delay_value;
12646 /* when blocking enabled, prevent moving up despite gravity */
12647 if (player->gravity && player->MovDir == MV_UP)
12648 block_delay_adjustment = -1;
12651 /* add block delay adjustment (also possible when not blocking) */
12652 last_field_block_delay += block_delay_adjustment;
12654 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12655 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12658 if (player->MovPos != 0) /* player has not yet reached destination */
12661 else if (!FrameReached(&player->actual_frame_counter, 1))
12664 if (player->MovPos != 0)
12666 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12667 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12669 /* before DrawPlayer() to draw correct player graphic for this case */
12670 if (player->MovPos == 0)
12671 CheckGravityMovement(player);
12674 if (player->MovPos == 0) /* player reached destination field */
12676 if (player->move_delay_reset_counter > 0)
12678 player->move_delay_reset_counter--;
12680 if (player->move_delay_reset_counter == 0)
12682 /* continue with normal speed after quickly moving through gate */
12683 HALVE_PLAYER_SPEED(player);
12685 /* be able to make the next move without delay */
12686 player->move_delay = 0;
12690 player->last_jx = jx;
12691 player->last_jy = jy;
12693 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12694 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12695 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12696 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12697 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12698 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12699 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12700 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12702 DrawPlayer(player); /* needed here only to cleanup last field */
12703 RemovePlayer(player);
12705 if (local_player->friends_still_needed == 0 ||
12706 IS_SP_ELEMENT(Feld[jx][jy]))
12707 PlayerWins(player);
12710 /* this breaks one level: "machine", level 000 */
12712 int move_direction = player->MovDir;
12713 int enter_side = MV_DIR_OPPOSITE(move_direction);
12714 int leave_side = move_direction;
12715 int old_jx = last_jx;
12716 int old_jy = last_jy;
12717 int old_element = Feld[old_jx][old_jy];
12718 int new_element = Feld[jx][jy];
12720 if (IS_CUSTOM_ELEMENT(old_element))
12721 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12723 player->index_bit, leave_side);
12725 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12726 CE_PLAYER_LEAVES_X,
12727 player->index_bit, leave_side);
12729 if (IS_CUSTOM_ELEMENT(new_element))
12730 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12731 player->index_bit, enter_side);
12733 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12734 CE_PLAYER_ENTERS_X,
12735 player->index_bit, enter_side);
12737 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12738 CE_MOVE_OF_X, move_direction);
12741 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12743 TestIfPlayerTouchesBadThing(jx, jy);
12744 TestIfPlayerTouchesCustomElement(jx, jy);
12746 /* needed because pushed element has not yet reached its destination,
12747 so it would trigger a change event at its previous field location */
12748 if (!player->is_pushing)
12749 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12751 if (!player->active)
12752 RemovePlayer(player);
12755 if (!local_player->LevelSolved && level.use_step_counter)
12765 if (TimeLeft <= 10 && setup.time_limit)
12766 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12768 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12770 DisplayGameControlValues();
12772 if (!TimeLeft && setup.time_limit)
12773 for (i = 0; i < MAX_PLAYERS; i++)
12774 KillPlayer(&stored_player[i]);
12776 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12778 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12780 DisplayGameControlValues();
12784 if (tape.single_step && tape.recording && !tape.pausing &&
12785 !player->programmed_action)
12786 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12788 if (!player->programmed_action)
12789 CheckSaveEngineSnapshot(player);
12793 void ScrollScreen(struct PlayerInfo *player, int mode)
12795 static unsigned int screen_frame_counter = 0;
12797 if (mode == SCROLL_INIT)
12799 /* set scrolling step size according to actual player's moving speed */
12800 ScrollStepSize = TILEX / player->move_delay_value;
12802 screen_frame_counter = FrameCounter;
12803 ScreenMovDir = player->MovDir;
12804 ScreenMovPos = player->MovPos;
12805 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12808 else if (!FrameReached(&screen_frame_counter, 1))
12813 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12814 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12815 redraw_mask |= REDRAW_FIELD;
12818 ScreenMovDir = MV_NONE;
12821 void TestIfPlayerTouchesCustomElement(int x, int y)
12823 static int xy[4][2] =
12830 static int trigger_sides[4][2] =
12832 /* center side border side */
12833 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12834 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12835 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12836 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12838 static int touch_dir[4] =
12840 MV_LEFT | MV_RIGHT,
12845 int center_element = Feld[x][y]; /* should always be non-moving! */
12848 for (i = 0; i < NUM_DIRECTIONS; i++)
12850 int xx = x + xy[i][0];
12851 int yy = y + xy[i][1];
12852 int center_side = trigger_sides[i][0];
12853 int border_side = trigger_sides[i][1];
12854 int border_element;
12856 if (!IN_LEV_FIELD(xx, yy))
12859 if (IS_PLAYER(x, y)) /* player found at center element */
12861 struct PlayerInfo *player = PLAYERINFO(x, y);
12863 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12864 border_element = Feld[xx][yy]; /* may be moving! */
12865 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12866 border_element = Feld[xx][yy];
12867 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12868 border_element = MovingOrBlocked2Element(xx, yy);
12870 continue; /* center and border element do not touch */
12872 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12873 player->index_bit, border_side);
12874 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12875 CE_PLAYER_TOUCHES_X,
12876 player->index_bit, border_side);
12879 /* use player element that is initially defined in the level playfield,
12880 not the player element that corresponds to the runtime player number
12881 (example: a level that contains EL_PLAYER_3 as the only player would
12882 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12883 int player_element = PLAYERINFO(x, y)->initial_element;
12885 CheckElementChangeBySide(xx, yy, border_element, player_element,
12886 CE_TOUCHING_X, border_side);
12889 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12891 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12893 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12895 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12896 continue; /* center and border element do not touch */
12899 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12900 player->index_bit, center_side);
12901 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12902 CE_PLAYER_TOUCHES_X,
12903 player->index_bit, center_side);
12906 /* use player element that is initially defined in the level playfield,
12907 not the player element that corresponds to the runtime player number
12908 (example: a level that contains EL_PLAYER_3 as the only player would
12909 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12910 int player_element = PLAYERINFO(xx, yy)->initial_element;
12912 CheckElementChangeBySide(x, y, center_element, player_element,
12913 CE_TOUCHING_X, center_side);
12921 void TestIfElementTouchesCustomElement(int x, int y)
12923 static int xy[4][2] =
12930 static int trigger_sides[4][2] =
12932 /* center side border side */
12933 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12934 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12935 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12936 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12938 static int touch_dir[4] =
12940 MV_LEFT | MV_RIGHT,
12945 boolean change_center_element = FALSE;
12946 int center_element = Feld[x][y]; /* should always be non-moving! */
12947 int border_element_old[NUM_DIRECTIONS];
12950 for (i = 0; i < NUM_DIRECTIONS; i++)
12952 int xx = x + xy[i][0];
12953 int yy = y + xy[i][1];
12954 int border_element;
12956 border_element_old[i] = -1;
12958 if (!IN_LEV_FIELD(xx, yy))
12961 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12962 border_element = Feld[xx][yy]; /* may be moving! */
12963 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12964 border_element = Feld[xx][yy];
12965 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12966 border_element = MovingOrBlocked2Element(xx, yy);
12968 continue; /* center and border element do not touch */
12970 border_element_old[i] = border_element;
12973 for (i = 0; i < NUM_DIRECTIONS; i++)
12975 int xx = x + xy[i][0];
12976 int yy = y + xy[i][1];
12977 int center_side = trigger_sides[i][0];
12978 int border_element = border_element_old[i];
12980 if (border_element == -1)
12983 /* check for change of border element */
12984 CheckElementChangeBySide(xx, yy, border_element, center_element,
12985 CE_TOUCHING_X, center_side);
12987 /* (center element cannot be player, so we dont have to check this here) */
12990 for (i = 0; i < NUM_DIRECTIONS; i++)
12992 int xx = x + xy[i][0];
12993 int yy = y + xy[i][1];
12994 int border_side = trigger_sides[i][1];
12995 int border_element = border_element_old[i];
12997 if (border_element == -1)
13000 /* check for change of center element (but change it only once) */
13001 if (!change_center_element)
13002 change_center_element =
13003 CheckElementChangeBySide(x, y, center_element, border_element,
13004 CE_TOUCHING_X, border_side);
13006 if (IS_PLAYER(xx, yy))
13008 /* use player element that is initially defined in the level playfield,
13009 not the player element that corresponds to the runtime player number
13010 (example: a level that contains EL_PLAYER_3 as the only player would
13011 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13012 int player_element = PLAYERINFO(xx, yy)->initial_element;
13014 CheckElementChangeBySide(x, y, center_element, player_element,
13015 CE_TOUCHING_X, border_side);
13020 void TestIfElementHitsCustomElement(int x, int y, int direction)
13022 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13023 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13024 int hitx = x + dx, hity = y + dy;
13025 int hitting_element = Feld[x][y];
13026 int touched_element;
13028 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13031 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13032 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13034 if (IN_LEV_FIELD(hitx, hity))
13036 int opposite_direction = MV_DIR_OPPOSITE(direction);
13037 int hitting_side = direction;
13038 int touched_side = opposite_direction;
13039 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13040 MovDir[hitx][hity] != direction ||
13041 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13047 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13048 CE_HITTING_X, touched_side);
13050 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13051 CE_HIT_BY_X, hitting_side);
13053 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13054 CE_HIT_BY_SOMETHING, opposite_direction);
13056 if (IS_PLAYER(hitx, hity))
13058 /* use player element that is initially defined in the level playfield,
13059 not the player element that corresponds to the runtime player number
13060 (example: a level that contains EL_PLAYER_3 as the only player would
13061 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13062 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13064 CheckElementChangeBySide(x, y, hitting_element, player_element,
13065 CE_HITTING_X, touched_side);
13070 /* "hitting something" is also true when hitting the playfield border */
13071 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13072 CE_HITTING_SOMETHING, direction);
13075 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13077 int i, kill_x = -1, kill_y = -1;
13079 int bad_element = -1;
13080 static int test_xy[4][2] =
13087 static int test_dir[4] =
13095 for (i = 0; i < NUM_DIRECTIONS; i++)
13097 int test_x, test_y, test_move_dir, test_element;
13099 test_x = good_x + test_xy[i][0];
13100 test_y = good_y + test_xy[i][1];
13102 if (!IN_LEV_FIELD(test_x, test_y))
13106 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13108 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13110 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13111 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13113 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13114 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13118 bad_element = test_element;
13124 if (kill_x != -1 || kill_y != -1)
13126 if (IS_PLAYER(good_x, good_y))
13128 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13130 if (player->shield_deadly_time_left > 0 &&
13131 !IS_INDESTRUCTIBLE(bad_element))
13132 Bang(kill_x, kill_y);
13133 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13134 KillPlayer(player);
13137 Bang(good_x, good_y);
13141 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13143 int i, kill_x = -1, kill_y = -1;
13144 int bad_element = Feld[bad_x][bad_y];
13145 static int test_xy[4][2] =
13152 static int touch_dir[4] =
13154 MV_LEFT | MV_RIGHT,
13159 static int test_dir[4] =
13167 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13170 for (i = 0; i < NUM_DIRECTIONS; i++)
13172 int test_x, test_y, test_move_dir, test_element;
13174 test_x = bad_x + test_xy[i][0];
13175 test_y = bad_y + test_xy[i][1];
13177 if (!IN_LEV_FIELD(test_x, test_y))
13181 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13183 test_element = Feld[test_x][test_y];
13185 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13186 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13188 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13189 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13191 /* good thing is player or penguin that does not move away */
13192 if (IS_PLAYER(test_x, test_y))
13194 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13196 if (bad_element == EL_ROBOT && player->is_moving)
13197 continue; /* robot does not kill player if he is moving */
13199 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13201 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13202 continue; /* center and border element do not touch */
13210 else if (test_element == EL_PENGUIN)
13220 if (kill_x != -1 || kill_y != -1)
13222 if (IS_PLAYER(kill_x, kill_y))
13224 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13226 if (player->shield_deadly_time_left > 0 &&
13227 !IS_INDESTRUCTIBLE(bad_element))
13228 Bang(bad_x, bad_y);
13229 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13230 KillPlayer(player);
13233 Bang(kill_x, kill_y);
13237 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13239 int bad_element = Feld[bad_x][bad_y];
13240 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13241 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13242 int test_x = bad_x + dx, test_y = bad_y + dy;
13243 int test_move_dir, test_element;
13244 int kill_x = -1, kill_y = -1;
13246 if (!IN_LEV_FIELD(test_x, test_y))
13250 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13252 test_element = Feld[test_x][test_y];
13254 if (test_move_dir != bad_move_dir)
13256 /* good thing can be player or penguin that does not move away */
13257 if (IS_PLAYER(test_x, test_y))
13259 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13261 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13262 player as being hit when he is moving towards the bad thing, because
13263 the "get hit by" condition would be lost after the player stops) */
13264 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13265 return; /* player moves away from bad thing */
13270 else if (test_element == EL_PENGUIN)
13277 if (kill_x != -1 || kill_y != -1)
13279 if (IS_PLAYER(kill_x, kill_y))
13281 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13283 if (player->shield_deadly_time_left > 0 &&
13284 !IS_INDESTRUCTIBLE(bad_element))
13285 Bang(bad_x, bad_y);
13286 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13287 KillPlayer(player);
13290 Bang(kill_x, kill_y);
13294 void TestIfPlayerTouchesBadThing(int x, int y)
13296 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13299 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13301 TestIfGoodThingHitsBadThing(x, y, move_dir);
13304 void TestIfBadThingTouchesPlayer(int x, int y)
13306 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13309 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13311 TestIfBadThingHitsGoodThing(x, y, move_dir);
13314 void TestIfFriendTouchesBadThing(int x, int y)
13316 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13319 void TestIfBadThingTouchesFriend(int x, int y)
13321 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13324 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13326 int i, kill_x = bad_x, kill_y = bad_y;
13327 static int xy[4][2] =
13335 for (i = 0; i < NUM_DIRECTIONS; i++)
13339 x = bad_x + xy[i][0];
13340 y = bad_y + xy[i][1];
13341 if (!IN_LEV_FIELD(x, y))
13344 element = Feld[x][y];
13345 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13346 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13354 if (kill_x != bad_x || kill_y != bad_y)
13355 Bang(bad_x, bad_y);
13358 void KillPlayer(struct PlayerInfo *player)
13360 int jx = player->jx, jy = player->jy;
13362 if (!player->active)
13366 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13367 player->killed, player->active, player->reanimated);
13370 /* the following code was introduced to prevent an infinite loop when calling
13372 -> CheckTriggeredElementChangeExt()
13373 -> ExecuteCustomElementAction()
13375 -> (infinitely repeating the above sequence of function calls)
13376 which occurs when killing the player while having a CE with the setting
13377 "kill player X when explosion of <player X>"; the solution using a new
13378 field "player->killed" was chosen for backwards compatibility, although
13379 clever use of the fields "player->active" etc. would probably also work */
13381 if (player->killed)
13385 player->killed = TRUE;
13387 /* remove accessible field at the player's position */
13388 Feld[jx][jy] = EL_EMPTY;
13390 /* deactivate shield (else Bang()/Explode() would not work right) */
13391 player->shield_normal_time_left = 0;
13392 player->shield_deadly_time_left = 0;
13395 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13396 player->killed, player->active, player->reanimated);
13402 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13403 player->killed, player->active, player->reanimated);
13406 if (player->reanimated) /* killed player may have been reanimated */
13407 player->killed = player->reanimated = FALSE;
13409 BuryPlayer(player);
13412 static void KillPlayerUnlessEnemyProtected(int x, int y)
13414 if (!PLAYER_ENEMY_PROTECTED(x, y))
13415 KillPlayer(PLAYERINFO(x, y));
13418 static void KillPlayerUnlessExplosionProtected(int x, int y)
13420 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13421 KillPlayer(PLAYERINFO(x, y));
13424 void BuryPlayer(struct PlayerInfo *player)
13426 int jx = player->jx, jy = player->jy;
13428 if (!player->active)
13431 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13432 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13434 player->GameOver = TRUE;
13435 RemovePlayer(player);
13438 void RemovePlayer(struct PlayerInfo *player)
13440 int jx = player->jx, jy = player->jy;
13441 int i, found = FALSE;
13443 player->present = FALSE;
13444 player->active = FALSE;
13446 if (!ExplodeField[jx][jy])
13447 StorePlayer[jx][jy] = 0;
13449 if (player->is_moving)
13450 TEST_DrawLevelField(player->last_jx, player->last_jy);
13452 for (i = 0; i < MAX_PLAYERS; i++)
13453 if (stored_player[i].active)
13457 AllPlayersGone = TRUE;
13463 static void setFieldForSnapping(int x, int y, int element, int direction)
13465 struct ElementInfo *ei = &element_info[element];
13466 int direction_bit = MV_DIR_TO_BIT(direction);
13467 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13468 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13469 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13471 Feld[x][y] = EL_ELEMENT_SNAPPING;
13472 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13474 ResetGfxAnimation(x, y);
13476 GfxElement[x][y] = element;
13477 GfxAction[x][y] = action;
13478 GfxDir[x][y] = direction;
13479 GfxFrame[x][y] = -1;
13483 =============================================================================
13484 checkDiagonalPushing()
13485 -----------------------------------------------------------------------------
13486 check if diagonal input device direction results in pushing of object
13487 (by checking if the alternative direction is walkable, diggable, ...)
13488 =============================================================================
13491 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13492 int x, int y, int real_dx, int real_dy)
13494 int jx, jy, dx, dy, xx, yy;
13496 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13499 /* diagonal direction: check alternative direction */
13504 xx = jx + (dx == 0 ? real_dx : 0);
13505 yy = jy + (dy == 0 ? real_dy : 0);
13507 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13511 =============================================================================
13513 -----------------------------------------------------------------------------
13514 x, y: field next to player (non-diagonal) to try to dig to
13515 real_dx, real_dy: direction as read from input device (can be diagonal)
13516 =============================================================================
13519 static int DigField(struct PlayerInfo *player,
13520 int oldx, int oldy, int x, int y,
13521 int real_dx, int real_dy, int mode)
13523 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13524 boolean player_was_pushing = player->is_pushing;
13525 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13526 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13527 int jx = oldx, jy = oldy;
13528 int dx = x - jx, dy = y - jy;
13529 int nextx = x + dx, nexty = y + dy;
13530 int move_direction = (dx == -1 ? MV_LEFT :
13531 dx == +1 ? MV_RIGHT :
13533 dy == +1 ? MV_DOWN : MV_NONE);
13534 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13535 int dig_side = MV_DIR_OPPOSITE(move_direction);
13536 int old_element = Feld[jx][jy];
13537 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13540 if (is_player) /* function can also be called by EL_PENGUIN */
13542 if (player->MovPos == 0)
13544 player->is_digging = FALSE;
13545 player->is_collecting = FALSE;
13548 if (player->MovPos == 0) /* last pushing move finished */
13549 player->is_pushing = FALSE;
13551 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13553 player->is_switching = FALSE;
13554 player->push_delay = -1;
13556 return MP_NO_ACTION;
13560 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13561 old_element = Back[jx][jy];
13563 /* in case of element dropped at player position, check background */
13564 else if (Back[jx][jy] != EL_EMPTY &&
13565 game.engine_version >= VERSION_IDENT(2,2,0,0))
13566 old_element = Back[jx][jy];
13568 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13569 return MP_NO_ACTION; /* field has no opening in this direction */
13571 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13572 return MP_NO_ACTION; /* field has no opening in this direction */
13574 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13578 Feld[jx][jy] = player->artwork_element;
13579 InitMovingField(jx, jy, MV_DOWN);
13580 Store[jx][jy] = EL_ACID;
13581 ContinueMoving(jx, jy);
13582 BuryPlayer(player);
13584 return MP_DONT_RUN_INTO;
13587 if (player_can_move && DONT_RUN_INTO(element))
13589 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13591 return MP_DONT_RUN_INTO;
13594 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13595 return MP_NO_ACTION;
13597 collect_count = element_info[element].collect_count_initial;
13599 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13600 return MP_NO_ACTION;
13602 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13603 player_can_move = player_can_move_or_snap;
13605 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13606 game.engine_version >= VERSION_IDENT(2,2,0,0))
13608 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13609 player->index_bit, dig_side);
13610 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13611 player->index_bit, dig_side);
13613 if (element == EL_DC_LANDMINE)
13616 if (Feld[x][y] != element) /* field changed by snapping */
13619 return MP_NO_ACTION;
13622 if (player->gravity && is_player && !player->is_auto_moving &&
13623 canFallDown(player) && move_direction != MV_DOWN &&
13624 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13625 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13627 if (player_can_move &&
13628 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13630 int sound_element = SND_ELEMENT(element);
13631 int sound_action = ACTION_WALKING;
13633 if (IS_RND_GATE(element))
13635 if (!player->key[RND_GATE_NR(element)])
13636 return MP_NO_ACTION;
13638 else if (IS_RND_GATE_GRAY(element))
13640 if (!player->key[RND_GATE_GRAY_NR(element)])
13641 return MP_NO_ACTION;
13643 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13645 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13646 return MP_NO_ACTION;
13648 else if (element == EL_EXIT_OPEN ||
13649 element == EL_EM_EXIT_OPEN ||
13650 element == EL_EM_EXIT_OPENING ||
13651 element == EL_STEEL_EXIT_OPEN ||
13652 element == EL_EM_STEEL_EXIT_OPEN ||
13653 element == EL_EM_STEEL_EXIT_OPENING ||
13654 element == EL_SP_EXIT_OPEN ||
13655 element == EL_SP_EXIT_OPENING)
13657 sound_action = ACTION_PASSING; /* player is passing exit */
13659 else if (element == EL_EMPTY)
13661 sound_action = ACTION_MOVING; /* nothing to walk on */
13664 /* play sound from background or player, whatever is available */
13665 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13666 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13668 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13670 else if (player_can_move &&
13671 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13673 if (!ACCESS_FROM(element, opposite_direction))
13674 return MP_NO_ACTION; /* field not accessible from this direction */
13676 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13677 return MP_NO_ACTION;
13679 if (IS_EM_GATE(element))
13681 if (!player->key[EM_GATE_NR(element)])
13682 return MP_NO_ACTION;
13684 else if (IS_EM_GATE_GRAY(element))
13686 if (!player->key[EM_GATE_GRAY_NR(element)])
13687 return MP_NO_ACTION;
13689 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13691 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13692 return MP_NO_ACTION;
13694 else if (IS_EMC_GATE(element))
13696 if (!player->key[EMC_GATE_NR(element)])
13697 return MP_NO_ACTION;
13699 else if (IS_EMC_GATE_GRAY(element))
13701 if (!player->key[EMC_GATE_GRAY_NR(element)])
13702 return MP_NO_ACTION;
13704 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13706 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13707 return MP_NO_ACTION;
13709 else if (element == EL_DC_GATE_WHITE ||
13710 element == EL_DC_GATE_WHITE_GRAY ||
13711 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13713 if (player->num_white_keys == 0)
13714 return MP_NO_ACTION;
13716 player->num_white_keys--;
13718 else if (IS_SP_PORT(element))
13720 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13721 element == EL_SP_GRAVITY_PORT_RIGHT ||
13722 element == EL_SP_GRAVITY_PORT_UP ||
13723 element == EL_SP_GRAVITY_PORT_DOWN)
13724 player->gravity = !player->gravity;
13725 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13726 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13727 element == EL_SP_GRAVITY_ON_PORT_UP ||
13728 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13729 player->gravity = TRUE;
13730 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13731 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13732 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13733 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13734 player->gravity = FALSE;
13737 /* automatically move to the next field with double speed */
13738 player->programmed_action = move_direction;
13740 if (player->move_delay_reset_counter == 0)
13742 player->move_delay_reset_counter = 2; /* two double speed steps */
13744 DOUBLE_PLAYER_SPEED(player);
13747 PlayLevelSoundAction(x, y, ACTION_PASSING);
13749 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13753 if (mode != DF_SNAP)
13755 GfxElement[x][y] = GFX_ELEMENT(element);
13756 player->is_digging = TRUE;
13759 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13761 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13762 player->index_bit, dig_side);
13764 if (mode == DF_SNAP)
13766 if (level.block_snap_field)
13767 setFieldForSnapping(x, y, element, move_direction);
13769 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13771 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13772 player->index_bit, dig_side);
13775 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13779 if (is_player && mode != DF_SNAP)
13781 GfxElement[x][y] = element;
13782 player->is_collecting = TRUE;
13785 if (element == EL_SPEED_PILL)
13787 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13789 else if (element == EL_EXTRA_TIME && level.time > 0)
13791 TimeLeft += level.extra_time;
13793 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13795 DisplayGameControlValues();
13797 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13799 player->shield_normal_time_left += level.shield_normal_time;
13800 if (element == EL_SHIELD_DEADLY)
13801 player->shield_deadly_time_left += level.shield_deadly_time;
13803 else if (element == EL_DYNAMITE ||
13804 element == EL_EM_DYNAMITE ||
13805 element == EL_SP_DISK_RED)
13807 if (player->inventory_size < MAX_INVENTORY_SIZE)
13808 player->inventory_element[player->inventory_size++] = element;
13810 DrawGameDoorValues();
13812 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13814 player->dynabomb_count++;
13815 player->dynabombs_left++;
13817 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13819 player->dynabomb_size++;
13821 else if (element == EL_DYNABOMB_INCREASE_POWER)
13823 player->dynabomb_xl = TRUE;
13825 else if (IS_KEY(element))
13827 player->key[KEY_NR(element)] = TRUE;
13829 DrawGameDoorValues();
13831 else if (element == EL_DC_KEY_WHITE)
13833 player->num_white_keys++;
13835 /* display white keys? */
13836 /* DrawGameDoorValues(); */
13838 else if (IS_ENVELOPE(element))
13840 player->show_envelope = element;
13842 else if (element == EL_EMC_LENSES)
13844 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13846 RedrawAllInvisibleElementsForLenses();
13848 else if (element == EL_EMC_MAGNIFIER)
13850 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13852 RedrawAllInvisibleElementsForMagnifier();
13854 else if (IS_DROPPABLE(element) ||
13855 IS_THROWABLE(element)) /* can be collected and dropped */
13859 if (collect_count == 0)
13860 player->inventory_infinite_element = element;
13862 for (i = 0; i < collect_count; i++)
13863 if (player->inventory_size < MAX_INVENTORY_SIZE)
13864 player->inventory_element[player->inventory_size++] = element;
13866 DrawGameDoorValues();
13868 else if (collect_count > 0)
13870 local_player->gems_still_needed -= collect_count;
13871 if (local_player->gems_still_needed < 0)
13872 local_player->gems_still_needed = 0;
13874 game.snapshot.collected_item = TRUE;
13876 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13878 DisplayGameControlValues();
13881 RaiseScoreElement(element);
13882 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13885 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13886 player->index_bit, dig_side);
13888 if (mode == DF_SNAP)
13890 if (level.block_snap_field)
13891 setFieldForSnapping(x, y, element, move_direction);
13893 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13895 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13896 player->index_bit, dig_side);
13899 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13901 if (mode == DF_SNAP && element != EL_BD_ROCK)
13902 return MP_NO_ACTION;
13904 if (CAN_FALL(element) && dy)
13905 return MP_NO_ACTION;
13907 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13908 !(element == EL_SPRING && level.use_spring_bug))
13909 return MP_NO_ACTION;
13911 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13912 ((move_direction & MV_VERTICAL &&
13913 ((element_info[element].move_pattern & MV_LEFT &&
13914 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13915 (element_info[element].move_pattern & MV_RIGHT &&
13916 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13917 (move_direction & MV_HORIZONTAL &&
13918 ((element_info[element].move_pattern & MV_UP &&
13919 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13920 (element_info[element].move_pattern & MV_DOWN &&
13921 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13922 return MP_NO_ACTION;
13924 /* do not push elements already moving away faster than player */
13925 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13926 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13927 return MP_NO_ACTION;
13929 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13931 if (player->push_delay_value == -1 || !player_was_pushing)
13932 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13934 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13936 if (player->push_delay_value == -1)
13937 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13939 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13941 if (!player->is_pushing)
13942 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13945 player->is_pushing = TRUE;
13946 player->is_active = TRUE;
13948 if (!(IN_LEV_FIELD(nextx, nexty) &&
13949 (IS_FREE(nextx, nexty) ||
13950 (IS_SB_ELEMENT(element) &&
13951 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13952 (IS_CUSTOM_ELEMENT(element) &&
13953 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13954 return MP_NO_ACTION;
13956 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13957 return MP_NO_ACTION;
13959 if (player->push_delay == -1) /* new pushing; restart delay */
13960 player->push_delay = 0;
13962 if (player->push_delay < player->push_delay_value &&
13963 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13964 element != EL_SPRING && element != EL_BALLOON)
13966 /* make sure that there is no move delay before next try to push */
13967 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13968 player->move_delay = 0;
13970 return MP_NO_ACTION;
13973 if (IS_CUSTOM_ELEMENT(element) &&
13974 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13976 if (!DigFieldByCE(nextx, nexty, element))
13977 return MP_NO_ACTION;
13980 if (IS_SB_ELEMENT(element))
13982 if (element == EL_SOKOBAN_FIELD_FULL)
13984 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13985 local_player->sokobanfields_still_needed++;
13988 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13990 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13991 local_player->sokobanfields_still_needed--;
13994 Feld[x][y] = EL_SOKOBAN_OBJECT;
13996 if (Back[x][y] == Back[nextx][nexty])
13997 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13998 else if (Back[x][y] != 0)
13999 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14002 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14005 if (local_player->sokobanfields_still_needed == 0 &&
14006 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14008 PlayerWins(player);
14010 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14014 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14016 InitMovingField(x, y, move_direction);
14017 GfxAction[x][y] = ACTION_PUSHING;
14019 if (mode == DF_SNAP)
14020 ContinueMoving(x, y);
14022 MovPos[x][y] = (dx != 0 ? dx : dy);
14024 Pushed[x][y] = TRUE;
14025 Pushed[nextx][nexty] = TRUE;
14027 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14028 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14030 player->push_delay_value = -1; /* get new value later */
14032 /* check for element change _after_ element has been pushed */
14033 if (game.use_change_when_pushing_bug)
14035 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14036 player->index_bit, dig_side);
14037 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14038 player->index_bit, dig_side);
14041 else if (IS_SWITCHABLE(element))
14043 if (PLAYER_SWITCHING(player, x, y))
14045 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14046 player->index_bit, dig_side);
14051 player->is_switching = TRUE;
14052 player->switch_x = x;
14053 player->switch_y = y;
14055 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14057 if (element == EL_ROBOT_WHEEL)
14059 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14063 game.robot_wheel_active = TRUE;
14065 TEST_DrawLevelField(x, y);
14067 else if (element == EL_SP_TERMINAL)
14071 SCAN_PLAYFIELD(xx, yy)
14073 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14077 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14079 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14081 ResetGfxAnimation(xx, yy);
14082 TEST_DrawLevelField(xx, yy);
14086 else if (IS_BELT_SWITCH(element))
14088 ToggleBeltSwitch(x, y);
14090 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14091 element == EL_SWITCHGATE_SWITCH_DOWN ||
14092 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14093 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14095 ToggleSwitchgateSwitch(x, y);
14097 else if (element == EL_LIGHT_SWITCH ||
14098 element == EL_LIGHT_SWITCH_ACTIVE)
14100 ToggleLightSwitch(x, y);
14102 else if (element == EL_TIMEGATE_SWITCH ||
14103 element == EL_DC_TIMEGATE_SWITCH)
14105 ActivateTimegateSwitch(x, y);
14107 else if (element == EL_BALLOON_SWITCH_LEFT ||
14108 element == EL_BALLOON_SWITCH_RIGHT ||
14109 element == EL_BALLOON_SWITCH_UP ||
14110 element == EL_BALLOON_SWITCH_DOWN ||
14111 element == EL_BALLOON_SWITCH_NONE ||
14112 element == EL_BALLOON_SWITCH_ANY)
14114 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14115 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14116 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14117 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14118 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14121 else if (element == EL_LAMP)
14123 Feld[x][y] = EL_LAMP_ACTIVE;
14124 local_player->lights_still_needed--;
14126 ResetGfxAnimation(x, y);
14127 TEST_DrawLevelField(x, y);
14129 else if (element == EL_TIME_ORB_FULL)
14131 Feld[x][y] = EL_TIME_ORB_EMPTY;
14133 if (level.time > 0 || level.use_time_orb_bug)
14135 TimeLeft += level.time_orb_time;
14136 game.no_time_limit = FALSE;
14138 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14140 DisplayGameControlValues();
14143 ResetGfxAnimation(x, y);
14144 TEST_DrawLevelField(x, y);
14146 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14147 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14151 game.ball_state = !game.ball_state;
14153 SCAN_PLAYFIELD(xx, yy)
14155 int e = Feld[xx][yy];
14157 if (game.ball_state)
14159 if (e == EL_EMC_MAGIC_BALL)
14160 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14161 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14162 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14166 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14167 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14168 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14169 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14174 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14175 player->index_bit, dig_side);
14177 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14178 player->index_bit, dig_side);
14180 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14181 player->index_bit, dig_side);
14187 if (!PLAYER_SWITCHING(player, x, y))
14189 player->is_switching = TRUE;
14190 player->switch_x = x;
14191 player->switch_y = y;
14193 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14194 player->index_bit, dig_side);
14195 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14196 player->index_bit, dig_side);
14198 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14199 player->index_bit, dig_side);
14200 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14201 player->index_bit, dig_side);
14204 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14205 player->index_bit, dig_side);
14206 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14207 player->index_bit, dig_side);
14209 return MP_NO_ACTION;
14212 player->push_delay = -1;
14214 if (is_player) /* function can also be called by EL_PENGUIN */
14216 if (Feld[x][y] != element) /* really digged/collected something */
14218 player->is_collecting = !player->is_digging;
14219 player->is_active = TRUE;
14226 static boolean DigFieldByCE(int x, int y, int digging_element)
14228 int element = Feld[x][y];
14230 if (!IS_FREE(x, y))
14232 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14233 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14236 /* no element can dig solid indestructible elements */
14237 if (IS_INDESTRUCTIBLE(element) &&
14238 !IS_DIGGABLE(element) &&
14239 !IS_COLLECTIBLE(element))
14242 if (AmoebaNr[x][y] &&
14243 (element == EL_AMOEBA_FULL ||
14244 element == EL_BD_AMOEBA ||
14245 element == EL_AMOEBA_GROWING))
14247 AmoebaCnt[AmoebaNr[x][y]]--;
14248 AmoebaCnt2[AmoebaNr[x][y]]--;
14251 if (IS_MOVING(x, y))
14252 RemoveMovingField(x, y);
14256 TEST_DrawLevelField(x, y);
14259 /* if digged element was about to explode, prevent the explosion */
14260 ExplodeField[x][y] = EX_TYPE_NONE;
14262 PlayLevelSoundAction(x, y, action);
14265 Store[x][y] = EL_EMPTY;
14267 /* this makes it possible to leave the removed element again */
14268 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14269 Store[x][y] = element;
14274 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14276 int jx = player->jx, jy = player->jy;
14277 int x = jx + dx, y = jy + dy;
14278 int snap_direction = (dx == -1 ? MV_LEFT :
14279 dx == +1 ? MV_RIGHT :
14281 dy == +1 ? MV_DOWN : MV_NONE);
14282 boolean can_continue_snapping = (level.continuous_snapping &&
14283 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14285 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14288 if (!player->active || !IN_LEV_FIELD(x, y))
14296 if (player->MovPos == 0)
14297 player->is_pushing = FALSE;
14299 player->is_snapping = FALSE;
14301 if (player->MovPos == 0)
14303 player->is_moving = FALSE;
14304 player->is_digging = FALSE;
14305 player->is_collecting = FALSE;
14311 /* prevent snapping with already pressed snap key when not allowed */
14312 if (player->is_snapping && !can_continue_snapping)
14315 player->MovDir = snap_direction;
14317 if (player->MovPos == 0)
14319 player->is_moving = FALSE;
14320 player->is_digging = FALSE;
14321 player->is_collecting = FALSE;
14324 player->is_dropping = FALSE;
14325 player->is_dropping_pressed = FALSE;
14326 player->drop_pressed_delay = 0;
14328 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14331 player->is_snapping = TRUE;
14332 player->is_active = TRUE;
14334 if (player->MovPos == 0)
14336 player->is_moving = FALSE;
14337 player->is_digging = FALSE;
14338 player->is_collecting = FALSE;
14341 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14342 TEST_DrawLevelField(player->last_jx, player->last_jy);
14344 TEST_DrawLevelField(x, y);
14349 static boolean DropElement(struct PlayerInfo *player)
14351 int old_element, new_element;
14352 int dropx = player->jx, dropy = player->jy;
14353 int drop_direction = player->MovDir;
14354 int drop_side = drop_direction;
14355 int drop_element = get_next_dropped_element(player);
14357 /* do not drop an element on top of another element; when holding drop key
14358 pressed without moving, dropped element must move away before the next
14359 element can be dropped (this is especially important if the next element
14360 is dynamite, which can be placed on background for historical reasons) */
14361 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14364 if (IS_THROWABLE(drop_element))
14366 dropx += GET_DX_FROM_DIR(drop_direction);
14367 dropy += GET_DY_FROM_DIR(drop_direction);
14369 if (!IN_LEV_FIELD(dropx, dropy))
14373 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14374 new_element = drop_element; /* default: no change when dropping */
14376 /* check if player is active, not moving and ready to drop */
14377 if (!player->active || player->MovPos || player->drop_delay > 0)
14380 /* check if player has anything that can be dropped */
14381 if (new_element == EL_UNDEFINED)
14384 /* only set if player has anything that can be dropped */
14385 player->is_dropping_pressed = TRUE;
14387 /* check if drop key was pressed long enough for EM style dynamite */
14388 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14391 /* check if anything can be dropped at the current position */
14392 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14395 /* collected custom elements can only be dropped on empty fields */
14396 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14399 if (old_element != EL_EMPTY)
14400 Back[dropx][dropy] = old_element; /* store old element on this field */
14402 ResetGfxAnimation(dropx, dropy);
14403 ResetRandomAnimationValue(dropx, dropy);
14405 if (player->inventory_size > 0 ||
14406 player->inventory_infinite_element != EL_UNDEFINED)
14408 if (player->inventory_size > 0)
14410 player->inventory_size--;
14412 DrawGameDoorValues();
14414 if (new_element == EL_DYNAMITE)
14415 new_element = EL_DYNAMITE_ACTIVE;
14416 else if (new_element == EL_EM_DYNAMITE)
14417 new_element = EL_EM_DYNAMITE_ACTIVE;
14418 else if (new_element == EL_SP_DISK_RED)
14419 new_element = EL_SP_DISK_RED_ACTIVE;
14422 Feld[dropx][dropy] = new_element;
14424 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14425 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14426 el2img(Feld[dropx][dropy]), 0);
14428 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14430 /* needed if previous element just changed to "empty" in the last frame */
14431 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14433 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14434 player->index_bit, drop_side);
14435 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14437 player->index_bit, drop_side);
14439 TestIfElementTouchesCustomElement(dropx, dropy);
14441 else /* player is dropping a dyna bomb */
14443 player->dynabombs_left--;
14445 Feld[dropx][dropy] = new_element;
14447 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14448 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14449 el2img(Feld[dropx][dropy]), 0);
14451 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14454 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14455 InitField_WithBug1(dropx, dropy, FALSE);
14457 new_element = Feld[dropx][dropy]; /* element might have changed */
14459 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14460 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14462 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14463 MovDir[dropx][dropy] = drop_direction;
14465 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14467 /* do not cause impact style collision by dropping elements that can fall */
14468 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14471 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14472 player->is_dropping = TRUE;
14474 player->drop_pressed_delay = 0;
14475 player->is_dropping_pressed = FALSE;
14477 player->drop_x = dropx;
14478 player->drop_y = dropy;
14483 /* ------------------------------------------------------------------------- */
14484 /* game sound playing functions */
14485 /* ------------------------------------------------------------------------- */
14487 static int *loop_sound_frame = NULL;
14488 static int *loop_sound_volume = NULL;
14490 void InitPlayLevelSound()
14492 int num_sounds = getSoundListSize();
14494 checked_free(loop_sound_frame);
14495 checked_free(loop_sound_volume);
14497 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14498 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14501 static void PlayLevelSound(int x, int y, int nr)
14503 int sx = SCREENX(x), sy = SCREENY(y);
14504 int volume, stereo_position;
14505 int max_distance = 8;
14506 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14508 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14509 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14512 if (!IN_LEV_FIELD(x, y) ||
14513 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14514 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14517 volume = SOUND_MAX_VOLUME;
14519 if (!IN_SCR_FIELD(sx, sy))
14521 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14522 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14524 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14527 stereo_position = (SOUND_MAX_LEFT +
14528 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14529 (SCR_FIELDX + 2 * max_distance));
14531 if (IS_LOOP_SOUND(nr))
14533 /* This assures that quieter loop sounds do not overwrite louder ones,
14534 while restarting sound volume comparison with each new game frame. */
14536 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14539 loop_sound_volume[nr] = volume;
14540 loop_sound_frame[nr] = FrameCounter;
14543 PlaySoundExt(nr, volume, stereo_position, type);
14546 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14548 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14549 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14550 y < LEVELY(BY1) ? LEVELY(BY1) :
14551 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14555 static void PlayLevelSoundAction(int x, int y, int action)
14557 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14560 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14562 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14564 if (sound_effect != SND_UNDEFINED)
14565 PlayLevelSound(x, y, sound_effect);
14568 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14571 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14573 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14574 PlayLevelSound(x, y, sound_effect);
14577 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14579 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14581 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14582 PlayLevelSound(x, y, sound_effect);
14585 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14587 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14589 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14590 StopSound(sound_effect);
14593 static int getLevelMusicNr()
14595 if (levelset.music[level_nr] != MUS_UNDEFINED)
14596 return levelset.music[level_nr]; /* from config file */
14598 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14601 static void FadeLevelSounds()
14606 static void FadeLevelMusic()
14608 int music_nr = getLevelMusicNr();
14609 char *curr_music = getCurrentlyPlayingMusicFilename();
14610 char *next_music = getMusicInfoEntryFilename(music_nr);
14612 if (!strEqual(curr_music, next_music))
14616 void FadeLevelSoundsAndMusic()
14622 static void PlayLevelMusic()
14624 int music_nr = getLevelMusicNr();
14625 char *curr_music = getCurrentlyPlayingMusicFilename();
14626 char *next_music = getMusicInfoEntryFilename(music_nr);
14628 if (!strEqual(curr_music, next_music))
14629 PlayMusic(music_nr);
14632 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14634 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14635 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14636 int x = xx - 1 - offset;
14637 int y = yy - 1 - offset;
14642 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14646 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14650 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14654 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14658 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14662 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14666 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14669 case SAMPLE_android_clone:
14670 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14673 case SAMPLE_android_move:
14674 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14677 case SAMPLE_spring:
14678 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14682 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14686 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14689 case SAMPLE_eater_eat:
14690 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14694 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14697 case SAMPLE_collect:
14698 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14701 case SAMPLE_diamond:
14702 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14705 case SAMPLE_squash:
14706 /* !!! CHECK THIS !!! */
14708 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14710 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14714 case SAMPLE_wonderfall:
14715 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14719 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14723 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14727 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14731 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14735 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14739 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14742 case SAMPLE_wonder:
14743 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14747 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14750 case SAMPLE_exit_open:
14751 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14754 case SAMPLE_exit_leave:
14755 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14758 case SAMPLE_dynamite:
14759 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14763 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14767 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14771 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14775 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14779 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14783 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14787 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14792 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14794 int element = map_element_SP_to_RND(element_sp);
14795 int action = map_action_SP_to_RND(action_sp);
14796 int offset = (setup.sp_show_border_elements ? 0 : 1);
14797 int x = xx - offset;
14798 int y = yy - offset;
14800 PlayLevelSoundElementAction(x, y, element, action);
14803 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14805 int element = map_element_MM_to_RND(element_mm);
14806 int action = map_action_MM_to_RND(action_mm);
14808 int x = xx - offset;
14809 int y = yy - offset;
14811 if (!IS_MM_ELEMENT(element))
14812 element = EL_MM_DEFAULT;
14814 PlayLevelSoundElementAction(x, y, element, action);
14817 void PlaySound_MM(int sound_mm)
14819 int sound = map_sound_MM_to_RND(sound_mm);
14821 if (sound == SND_UNDEFINED)
14827 void PlaySoundLoop_MM(int sound_mm)
14829 int sound = map_sound_MM_to_RND(sound_mm);
14831 if (sound == SND_UNDEFINED)
14834 PlaySoundLoop(sound);
14837 void StopSound_MM(int sound_mm)
14839 int sound = map_sound_MM_to_RND(sound_mm);
14841 if (sound == SND_UNDEFINED)
14847 void RaiseScore(int value)
14849 local_player->score += value;
14851 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14853 DisplayGameControlValues();
14856 void RaiseScoreElement(int element)
14861 case EL_BD_DIAMOND:
14862 case EL_EMERALD_YELLOW:
14863 case EL_EMERALD_RED:
14864 case EL_EMERALD_PURPLE:
14865 case EL_SP_INFOTRON:
14866 RaiseScore(level.score[SC_EMERALD]);
14869 RaiseScore(level.score[SC_DIAMOND]);
14872 RaiseScore(level.score[SC_CRYSTAL]);
14875 RaiseScore(level.score[SC_PEARL]);
14878 case EL_BD_BUTTERFLY:
14879 case EL_SP_ELECTRON:
14880 RaiseScore(level.score[SC_BUG]);
14883 case EL_BD_FIREFLY:
14884 case EL_SP_SNIKSNAK:
14885 RaiseScore(level.score[SC_SPACESHIP]);
14888 case EL_DARK_YAMYAM:
14889 RaiseScore(level.score[SC_YAMYAM]);
14892 RaiseScore(level.score[SC_ROBOT]);
14895 RaiseScore(level.score[SC_PACMAN]);
14898 RaiseScore(level.score[SC_NUT]);
14901 case EL_EM_DYNAMITE:
14902 case EL_SP_DISK_RED:
14903 case EL_DYNABOMB_INCREASE_NUMBER:
14904 case EL_DYNABOMB_INCREASE_SIZE:
14905 case EL_DYNABOMB_INCREASE_POWER:
14906 RaiseScore(level.score[SC_DYNAMITE]);
14908 case EL_SHIELD_NORMAL:
14909 case EL_SHIELD_DEADLY:
14910 RaiseScore(level.score[SC_SHIELD]);
14912 case EL_EXTRA_TIME:
14913 RaiseScore(level.extra_time_score);
14927 case EL_DC_KEY_WHITE:
14928 RaiseScore(level.score[SC_KEY]);
14931 RaiseScore(element_info[element].collect_score);
14936 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14938 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14940 /* closing door required in case of envelope style request dialogs */
14942 CloseDoor(DOOR_CLOSE_1);
14944 #if defined(NETWORK_AVALIABLE)
14945 if (options.network)
14946 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14951 FadeSkipNextFadeIn();
14953 SetGameStatus(GAME_MODE_MAIN);
14958 else /* continue playing the game */
14960 if (tape.playing && tape.deactivate_display)
14961 TapeDeactivateDisplayOff(TRUE);
14963 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14965 if (tape.playing && tape.deactivate_display)
14966 TapeDeactivateDisplayOn();
14970 void RequestQuitGame(boolean ask_if_really_quit)
14972 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14973 boolean skip_request = AllPlayersGone || quick_quit;
14975 RequestQuitGameExt(skip_request, quick_quit,
14976 "Do you really want to quit the game?");
14979 void RequestRestartGame(char *message)
14981 game.restart_game_message = NULL;
14983 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14985 StartGameActions(options.network, setup.autorecord, level.random_seed);
14989 SetGameStatus(GAME_MODE_MAIN);
14996 /* ------------------------------------------------------------------------- */
14997 /* random generator functions */
14998 /* ------------------------------------------------------------------------- */
15000 unsigned int InitEngineRandom_RND(int seed)
15002 game.num_random_calls = 0;
15004 return InitEngineRandom(seed);
15007 unsigned int RND(int max)
15011 game.num_random_calls++;
15013 return GetEngineRandom(max);
15020 /* ------------------------------------------------------------------------- */
15021 /* game engine snapshot handling functions */
15022 /* ------------------------------------------------------------------------- */
15024 struct EngineSnapshotInfo
15026 /* runtime values for custom element collect score */
15027 int collect_score[NUM_CUSTOM_ELEMENTS];
15029 /* runtime values for group element choice position */
15030 int choice_pos[NUM_GROUP_ELEMENTS];
15032 /* runtime values for belt position animations */
15033 int belt_graphic[4][NUM_BELT_PARTS];
15034 int belt_anim_mode[4][NUM_BELT_PARTS];
15037 static struct EngineSnapshotInfo engine_snapshot_rnd;
15038 static char *snapshot_level_identifier = NULL;
15039 static int snapshot_level_nr = -1;
15041 static void SaveEngineSnapshotValues_RND()
15043 static int belt_base_active_element[4] =
15045 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15046 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15047 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15048 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15052 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15054 int element = EL_CUSTOM_START + i;
15056 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15059 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15061 int element = EL_GROUP_START + i;
15063 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15066 for (i = 0; i < 4; i++)
15068 for (j = 0; j < NUM_BELT_PARTS; j++)
15070 int element = belt_base_active_element[i] + j;
15071 int graphic = el2img(element);
15072 int anim_mode = graphic_info[graphic].anim_mode;
15074 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15075 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15080 static void LoadEngineSnapshotValues_RND()
15082 unsigned int num_random_calls = game.num_random_calls;
15085 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15087 int element = EL_CUSTOM_START + i;
15089 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15092 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15094 int element = EL_GROUP_START + i;
15096 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15099 for (i = 0; i < 4; i++)
15101 for (j = 0; j < NUM_BELT_PARTS; j++)
15103 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15104 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15106 graphic_info[graphic].anim_mode = anim_mode;
15110 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15112 InitRND(tape.random_seed);
15113 for (i = 0; i < num_random_calls; i++)
15117 if (game.num_random_calls != num_random_calls)
15119 Error(ERR_INFO, "number of random calls out of sync");
15120 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15121 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15122 Error(ERR_EXIT, "this should not happen -- please debug");
15126 void FreeEngineSnapshotSingle()
15128 FreeSnapshotSingle();
15130 setString(&snapshot_level_identifier, NULL);
15131 snapshot_level_nr = -1;
15134 void FreeEngineSnapshotList()
15136 FreeSnapshotList();
15139 ListNode *SaveEngineSnapshotBuffers()
15141 ListNode *buffers = NULL;
15143 /* copy some special values to a structure better suited for the snapshot */
15145 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15146 SaveEngineSnapshotValues_RND();
15147 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15148 SaveEngineSnapshotValues_EM();
15149 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15150 SaveEngineSnapshotValues_SP(&buffers);
15151 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15152 SaveEngineSnapshotValues_MM(&buffers);
15154 /* save values stored in special snapshot structure */
15156 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15158 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15159 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15160 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15161 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15162 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15165 /* save further RND engine values */
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15168 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15171 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15173 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15174 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15176 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15177 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15178 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15180 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15182 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15184 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15186 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15188 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15190 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15191 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15193 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15194 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15195 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15196 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15197 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15198 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15199 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15200 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15201 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15202 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15203 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15204 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15205 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15206 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15207 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15208 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15209 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15210 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15212 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15213 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15215 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15216 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15217 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15219 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15220 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15222 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15223 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15224 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15225 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15226 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15228 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15229 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15232 ListNode *node = engine_snapshot_list_rnd;
15235 while (node != NULL)
15237 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15242 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15248 void SaveEngineSnapshotSingle()
15250 ListNode *buffers = SaveEngineSnapshotBuffers();
15252 /* finally save all snapshot buffers to single snapshot */
15253 SaveSnapshotSingle(buffers);
15255 /* save level identification information */
15256 setString(&snapshot_level_identifier, leveldir_current->identifier);
15257 snapshot_level_nr = level_nr;
15260 boolean CheckSaveEngineSnapshotToList()
15262 boolean save_snapshot =
15263 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15264 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15265 game.snapshot.changed_action) ||
15266 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15267 game.snapshot.collected_item));
15269 game.snapshot.changed_action = FALSE;
15270 game.snapshot.collected_item = FALSE;
15271 game.snapshot.save_snapshot = save_snapshot;
15273 return save_snapshot;
15276 void SaveEngineSnapshotToList()
15278 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15282 ListNode *buffers = SaveEngineSnapshotBuffers();
15284 /* finally save all snapshot buffers to snapshot list */
15285 SaveSnapshotToList(buffers);
15288 void SaveEngineSnapshotToListInitial()
15290 FreeEngineSnapshotList();
15292 SaveEngineSnapshotToList();
15295 void LoadEngineSnapshotValues()
15297 /* restore special values from snapshot structure */
15299 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15300 LoadEngineSnapshotValues_RND();
15301 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15302 LoadEngineSnapshotValues_EM();
15303 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15304 LoadEngineSnapshotValues_SP();
15305 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15306 LoadEngineSnapshotValues_MM();
15309 void LoadEngineSnapshotSingle()
15311 LoadSnapshotSingle();
15313 LoadEngineSnapshotValues();
15316 void LoadEngineSnapshot_Undo(int steps)
15318 LoadSnapshotFromList_Older(steps);
15320 LoadEngineSnapshotValues();
15323 void LoadEngineSnapshot_Redo(int steps)
15325 LoadSnapshotFromList_Newer(steps);
15327 LoadEngineSnapshotValues();
15330 boolean CheckEngineSnapshotSingle()
15332 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15333 snapshot_level_nr == level_nr);
15336 boolean CheckEngineSnapshotList()
15338 return CheckSnapshotList();
15342 /* ---------- new game button stuff ---------------------------------------- */
15349 boolean *setup_value;
15350 boolean allowed_on_tape;
15352 } gamebutton_info[NUM_GAME_BUTTONS] =
15355 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15356 GAME_CTRL_ID_STOP, NULL,
15360 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15361 GAME_CTRL_ID_PAUSE, NULL,
15365 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15366 GAME_CTRL_ID_PLAY, NULL,
15370 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15371 GAME_CTRL_ID_UNDO, NULL,
15375 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15376 GAME_CTRL_ID_REDO, NULL,
15380 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15381 GAME_CTRL_ID_SAVE, NULL,
15385 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15386 GAME_CTRL_ID_PAUSE2, NULL,
15390 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15391 GAME_CTRL_ID_LOAD, NULL,
15395 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15396 GAME_CTRL_ID_PANEL_STOP, NULL,
15400 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15401 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15402 FALSE, "pause game"
15405 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15406 GAME_CTRL_ID_PANEL_PLAY, NULL,
15410 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15411 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15412 TRUE, "background music on/off"
15415 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15416 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15417 TRUE, "sound loops on/off"
15420 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15421 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15422 TRUE, "normal sounds on/off"
15425 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15426 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15427 FALSE, "background music on/off"
15430 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15431 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15432 FALSE, "sound loops on/off"
15435 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15436 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15437 FALSE, "normal sounds on/off"
15441 void CreateGameButtons()
15445 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15447 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15448 struct XY *pos = gamebutton_info[i].pos;
15449 struct GadgetInfo *gi;
15452 unsigned int event_mask;
15453 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15454 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15455 int base_x = (on_tape ? VX : DX);
15456 int base_y = (on_tape ? VY : DY);
15457 int gd_x = gfx->src_x;
15458 int gd_y = gfx->src_y;
15459 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15460 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15461 int gd_xa = gfx->src_x + gfx->active_xoffset;
15462 int gd_ya = gfx->src_y + gfx->active_yoffset;
15463 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15464 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15467 if (gfx->bitmap == NULL)
15469 game_gadget[id] = NULL;
15474 if (id == GAME_CTRL_ID_STOP ||
15475 id == GAME_CTRL_ID_PANEL_STOP ||
15476 id == GAME_CTRL_ID_PLAY ||
15477 id == GAME_CTRL_ID_PANEL_PLAY ||
15478 id == GAME_CTRL_ID_SAVE ||
15479 id == GAME_CTRL_ID_LOAD)
15481 button_type = GD_TYPE_NORMAL_BUTTON;
15483 event_mask = GD_EVENT_RELEASED;
15485 else if (id == GAME_CTRL_ID_UNDO ||
15486 id == GAME_CTRL_ID_REDO)
15488 button_type = GD_TYPE_NORMAL_BUTTON;
15490 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15494 button_type = GD_TYPE_CHECK_BUTTON;
15495 checked = (gamebutton_info[i].setup_value != NULL ?
15496 *gamebutton_info[i].setup_value : FALSE);
15497 event_mask = GD_EVENT_PRESSED;
15500 gi = CreateGadget(GDI_CUSTOM_ID, id,
15501 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15502 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15503 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15504 GDI_WIDTH, gfx->width,
15505 GDI_HEIGHT, gfx->height,
15506 GDI_TYPE, button_type,
15507 GDI_STATE, GD_BUTTON_UNPRESSED,
15508 GDI_CHECKED, checked,
15509 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15510 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15511 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15512 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15513 GDI_DIRECT_DRAW, FALSE,
15514 GDI_EVENT_MASK, event_mask,
15515 GDI_CALLBACK_ACTION, HandleGameButtons,
15519 Error(ERR_EXIT, "cannot create gadget");
15521 game_gadget[id] = gi;
15525 void FreeGameButtons()
15529 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15530 FreeGadget(game_gadget[i]);
15533 static void UnmapGameButtonsAtSamePosition(int id)
15537 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15539 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15540 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15541 UnmapGadget(game_gadget[i]);
15544 static void UnmapGameButtonsAtSamePosition_All()
15546 if (setup.show_snapshot_buttons)
15548 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15549 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15550 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15554 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15555 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15556 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15558 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15559 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15560 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15564 static void MapGameButtonsAtSamePosition(int id)
15568 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15570 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15571 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15572 MapGadget(game_gadget[i]);
15574 UnmapGameButtonsAtSamePosition_All();
15577 void MapUndoRedoButtons()
15579 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15580 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15582 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15583 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15585 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15588 void UnmapUndoRedoButtons()
15590 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15591 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15593 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15594 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15596 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15599 void MapGameButtonsExt(boolean on_tape)
15603 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15604 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15605 i != GAME_CTRL_ID_UNDO &&
15606 i != GAME_CTRL_ID_REDO)
15607 MapGadget(game_gadget[i]);
15609 UnmapGameButtonsAtSamePosition_All();
15611 RedrawGameButtons();
15614 void UnmapGameButtonsExt(boolean on_tape)
15618 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15619 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15620 UnmapGadget(game_gadget[i]);
15623 void RedrawGameButtonsExt(boolean on_tape)
15627 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15628 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15629 RedrawGadget(game_gadget[i]);
15631 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15632 redraw_mask &= ~REDRAW_ALL;
15635 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15640 gi->checked = state;
15643 void RedrawSoundButtonGadget(int id)
15645 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15646 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15647 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15648 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15649 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15650 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15653 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15654 RedrawGadget(game_gadget[id2]);
15657 void MapGameButtons()
15659 MapGameButtonsExt(FALSE);
15662 void UnmapGameButtons()
15664 UnmapGameButtonsExt(FALSE);
15667 void RedrawGameButtons()
15669 RedrawGameButtonsExt(FALSE);
15672 void MapGameButtonsOnTape()
15674 MapGameButtonsExt(TRUE);
15677 void UnmapGameButtonsOnTape()
15679 UnmapGameButtonsExt(TRUE);
15682 void RedrawGameButtonsOnTape()
15684 RedrawGameButtonsExt(TRUE);
15687 void GameUndoRedoExt()
15689 ClearPlayerAction();
15691 tape.pausing = TRUE;
15694 UpdateAndDisplayGameControlValues();
15696 DrawCompleteVideoDisplay();
15697 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15698 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15699 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15704 void GameUndo(int steps)
15706 if (!CheckEngineSnapshotList())
15709 LoadEngineSnapshot_Undo(steps);
15714 void GameRedo(int steps)
15716 if (!CheckEngineSnapshotList())
15719 LoadEngineSnapshot_Redo(steps);
15724 static void HandleGameButtonsExt(int id, int button)
15726 static boolean game_undo_executed = FALSE;
15727 int steps = BUTTON_STEPSIZE(button);
15728 boolean handle_game_buttons =
15729 (game_status == GAME_MODE_PLAYING ||
15730 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15732 if (!handle_game_buttons)
15737 case GAME_CTRL_ID_STOP:
15738 case GAME_CTRL_ID_PANEL_STOP:
15739 if (game_status == GAME_MODE_MAIN)
15745 RequestQuitGame(TRUE);
15749 case GAME_CTRL_ID_PAUSE:
15750 case GAME_CTRL_ID_PAUSE2:
15751 case GAME_CTRL_ID_PANEL_PAUSE:
15752 if (options.network && game_status == GAME_MODE_PLAYING)
15754 #if defined(NETWORK_AVALIABLE)
15756 SendToServer_ContinuePlaying();
15758 SendToServer_PausePlaying();
15762 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15764 game_undo_executed = FALSE;
15768 case GAME_CTRL_ID_PLAY:
15769 case GAME_CTRL_ID_PANEL_PLAY:
15770 if (game_status == GAME_MODE_MAIN)
15772 StartGameActions(options.network, setup.autorecord, level.random_seed);
15774 else if (tape.pausing)
15776 #if defined(NETWORK_AVALIABLE)
15777 if (options.network)
15778 SendToServer_ContinuePlaying();
15781 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15785 case GAME_CTRL_ID_UNDO:
15786 // Important: When using "save snapshot when collecting an item" mode,
15787 // load last (current) snapshot for first "undo" after pressing "pause"
15788 // (else the last-but-one snapshot would be loaded, because the snapshot
15789 // pointer already points to the last snapshot when pressing "pause",
15790 // which is fine for "every step/move" mode, but not for "every collect")
15791 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15792 !game_undo_executed)
15795 game_undo_executed = TRUE;
15800 case GAME_CTRL_ID_REDO:
15804 case GAME_CTRL_ID_SAVE:
15808 case GAME_CTRL_ID_LOAD:
15812 case SOUND_CTRL_ID_MUSIC:
15813 case SOUND_CTRL_ID_PANEL_MUSIC:
15814 if (setup.sound_music)
15816 setup.sound_music = FALSE;
15820 else if (audio.music_available)
15822 setup.sound = setup.sound_music = TRUE;
15824 SetAudioMode(setup.sound);
15826 if (game_status == GAME_MODE_PLAYING)
15830 RedrawSoundButtonGadget(id);
15834 case SOUND_CTRL_ID_LOOPS:
15835 case SOUND_CTRL_ID_PANEL_LOOPS:
15836 if (setup.sound_loops)
15837 setup.sound_loops = FALSE;
15838 else if (audio.loops_available)
15840 setup.sound = setup.sound_loops = TRUE;
15842 SetAudioMode(setup.sound);
15845 RedrawSoundButtonGadget(id);
15849 case SOUND_CTRL_ID_SIMPLE:
15850 case SOUND_CTRL_ID_PANEL_SIMPLE:
15851 if (setup.sound_simple)
15852 setup.sound_simple = FALSE;
15853 else if (audio.sound_available)
15855 setup.sound = setup.sound_simple = TRUE;
15857 SetAudioMode(setup.sound);
15860 RedrawSoundButtonGadget(id);
15869 static void HandleGameButtons(struct GadgetInfo *gi)
15871 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15874 void HandleSoundButtonKeys(Key key)
15876 if (key == setup.shortcut.sound_simple)
15877 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15878 else if (key == setup.shortcut.sound_loops)
15879 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15880 else if (key == setup.shortcut.sound_music)
15881 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);