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 SOUND_CTRL_ID_MUSIC 8
1017 #define SOUND_CTRL_ID_LOOPS 9
1018 #define SOUND_CTRL_ID_SIMPLE 10
1020 #define NUM_GAME_BUTTONS 11
1023 /* forward declaration for internal use */
1025 static void CreateField(int, int, int);
1027 static void ResetGfxAnimation(int, int);
1029 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1030 static void AdvanceFrameAndPlayerCounters(int);
1032 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1033 static boolean MovePlayer(struct PlayerInfo *, int, int);
1034 static void ScrollPlayer(struct PlayerInfo *, int);
1035 static void ScrollScreen(struct PlayerInfo *, int);
1037 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static boolean DigFieldByCE(int, int, int);
1039 static boolean SnapField(struct PlayerInfo *, int, int);
1040 static boolean DropElement(struct PlayerInfo *);
1042 static void InitBeltMovement(void);
1043 static void CloseAllOpenTimegates(void);
1044 static void CheckGravityMovement(struct PlayerInfo *);
1045 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1046 static void KillPlayerUnlessEnemyProtected(int, int);
1047 static void KillPlayerUnlessExplosionProtected(int, int);
1049 static void TestIfPlayerTouchesCustomElement(int, int);
1050 static void TestIfElementTouchesCustomElement(int, int);
1051 static void TestIfElementHitsCustomElement(int, int, int);
1053 static void HandleElementChange(int, int, int);
1054 static void ExecuteCustomElementAction(int, int, int, int);
1055 static boolean ChangeElement(int, int, int, int);
1057 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1058 #define CheckTriggeredElementChange(x, y, e, ev) \
1059 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1060 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1061 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1062 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1063 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1064 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1065 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1067 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1068 #define CheckElementChange(x, y, e, te, ev) \
1069 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1070 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1071 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1072 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1073 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1075 static void PlayLevelSound(int, int, int);
1076 static void PlayLevelSoundNearest(int, int, int);
1077 static void PlayLevelSoundAction(int, int, int);
1078 static void PlayLevelSoundElementAction(int, int, int, int);
1079 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1080 static void PlayLevelSoundActionIfLoop(int, int, int);
1081 static void StopLevelSoundActionIfLoop(int, int, int);
1082 static void PlayLevelMusic();
1083 static void FadeLevelSoundsAndMusic();
1085 static void HandleGameButtons(struct GadgetInfo *);
1087 int AmoebeNachbarNr(int, int);
1088 void AmoebeUmwandeln(int, int);
1089 void ContinueMoving(int, int);
1090 void Bang(int, int);
1091 void InitMovDir(int, int);
1092 void InitAmoebaNr(int, int);
1093 int NewHiScore(void);
1095 void TestIfGoodThingHitsBadThing(int, int, int);
1096 void TestIfBadThingHitsGoodThing(int, int, int);
1097 void TestIfPlayerTouchesBadThing(int, int);
1098 void TestIfPlayerRunsIntoBadThing(int, int, int);
1099 void TestIfBadThingTouchesPlayer(int, int);
1100 void TestIfBadThingRunsIntoPlayer(int, int, int);
1101 void TestIfFriendTouchesBadThing(int, int);
1102 void TestIfBadThingTouchesFriend(int, int);
1103 void TestIfBadThingTouchesOtherBadThing(int, int);
1104 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1106 void KillPlayer(struct PlayerInfo *);
1107 void BuryPlayer(struct PlayerInfo *);
1108 void RemovePlayer(struct PlayerInfo *);
1110 static int getInvisibleActiveFromInvisibleElement(int);
1111 static int getInvisibleFromInvisibleActiveElement(int);
1113 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1115 /* for detection of endless loops, caused by custom element programming */
1116 /* (using maximal playfield width x 10 is just a rough approximation) */
1117 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1119 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1121 if (recursion_loop_detected) \
1124 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1126 recursion_loop_detected = TRUE; \
1127 recursion_loop_element = (e); \
1130 recursion_loop_depth++; \
1133 #define RECURSION_LOOP_DETECTION_END() \
1135 recursion_loop_depth--; \
1138 static int recursion_loop_depth;
1139 static boolean recursion_loop_detected;
1140 static boolean recursion_loop_element;
1142 static int map_player_action[MAX_PLAYERS];
1145 /* ------------------------------------------------------------------------- */
1146 /* definition of elements that automatically change to other elements after */
1147 /* a specified time, eventually calling a function when changing */
1148 /* ------------------------------------------------------------------------- */
1150 /* forward declaration for changer functions */
1151 static void InitBuggyBase(int, int);
1152 static void WarnBuggyBase(int, int);
1154 static void InitTrap(int, int);
1155 static void ActivateTrap(int, int);
1156 static void ChangeActiveTrap(int, int);
1158 static void InitRobotWheel(int, int);
1159 static void RunRobotWheel(int, int);
1160 static void StopRobotWheel(int, int);
1162 static void InitTimegateWheel(int, int);
1163 static void RunTimegateWheel(int, int);
1165 static void InitMagicBallDelay(int, int);
1166 static void ActivateMagicBall(int, int);
1168 struct ChangingElementInfo
1173 void (*pre_change_function)(int x, int y);
1174 void (*change_function)(int x, int y);
1175 void (*post_change_function)(int x, int y);
1178 static struct ChangingElementInfo change_delay_list[] =
1213 EL_STEEL_EXIT_OPENING,
1221 EL_STEEL_EXIT_CLOSING,
1222 EL_STEEL_EXIT_CLOSED,
1245 EL_EM_STEEL_EXIT_OPENING,
1246 EL_EM_STEEL_EXIT_OPEN,
1253 EL_EM_STEEL_EXIT_CLOSING,
1277 EL_SWITCHGATE_OPENING,
1285 EL_SWITCHGATE_CLOSING,
1286 EL_SWITCHGATE_CLOSED,
1293 EL_TIMEGATE_OPENING,
1301 EL_TIMEGATE_CLOSING,
1310 EL_ACID_SPLASH_LEFT,
1318 EL_ACID_SPLASH_RIGHT,
1327 EL_SP_BUGGY_BASE_ACTIVATING,
1334 EL_SP_BUGGY_BASE_ACTIVATING,
1335 EL_SP_BUGGY_BASE_ACTIVE,
1342 EL_SP_BUGGY_BASE_ACTIVE,
1366 EL_ROBOT_WHEEL_ACTIVE,
1374 EL_TIMEGATE_SWITCH_ACTIVE,
1382 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1383 EL_DC_TIMEGATE_SWITCH,
1390 EL_EMC_MAGIC_BALL_ACTIVE,
1391 EL_EMC_MAGIC_BALL_ACTIVE,
1398 EL_EMC_SPRING_BUMPER_ACTIVE,
1399 EL_EMC_SPRING_BUMPER,
1406 EL_DIAGONAL_SHRINKING,
1414 EL_DIAGONAL_GROWING,
1435 int push_delay_fixed, push_delay_random;
1439 { EL_SPRING, 0, 0 },
1440 { EL_BALLOON, 0, 0 },
1442 { EL_SOKOBAN_OBJECT, 2, 0 },
1443 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1444 { EL_SATELLITE, 2, 0 },
1445 { EL_SP_DISK_YELLOW, 2, 0 },
1447 { EL_UNDEFINED, 0, 0 },
1455 move_stepsize_list[] =
1457 { EL_AMOEBA_DROP, 2 },
1458 { EL_AMOEBA_DROPPING, 2 },
1459 { EL_QUICKSAND_FILLING, 1 },
1460 { EL_QUICKSAND_EMPTYING, 1 },
1461 { EL_QUICKSAND_FAST_FILLING, 2 },
1462 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1463 { EL_MAGIC_WALL_FILLING, 2 },
1464 { EL_MAGIC_WALL_EMPTYING, 2 },
1465 { EL_BD_MAGIC_WALL_FILLING, 2 },
1466 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1467 { EL_DC_MAGIC_WALL_FILLING, 2 },
1468 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1470 { EL_UNDEFINED, 0 },
1478 collect_count_list[] =
1481 { EL_BD_DIAMOND, 1 },
1482 { EL_EMERALD_YELLOW, 1 },
1483 { EL_EMERALD_RED, 1 },
1484 { EL_EMERALD_PURPLE, 1 },
1486 { EL_SP_INFOTRON, 1 },
1490 { EL_UNDEFINED, 0 },
1498 access_direction_list[] =
1500 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1501 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1502 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1503 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1504 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1505 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1506 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1507 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1508 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1509 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1510 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1512 { EL_SP_PORT_LEFT, MV_RIGHT },
1513 { EL_SP_PORT_RIGHT, MV_LEFT },
1514 { EL_SP_PORT_UP, MV_DOWN },
1515 { EL_SP_PORT_DOWN, MV_UP },
1516 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1517 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1518 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1519 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1520 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1521 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1522 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1523 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1524 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1525 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1526 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1527 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1528 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1529 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1530 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1532 { EL_UNDEFINED, MV_NONE }
1535 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1537 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1538 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1539 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1540 IS_JUST_CHANGING(x, y))
1542 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1544 /* static variables for playfield scan mode (scanning forward or backward) */
1545 static int playfield_scan_start_x = 0;
1546 static int playfield_scan_start_y = 0;
1547 static int playfield_scan_delta_x = 1;
1548 static int playfield_scan_delta_y = 1;
1550 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1551 (y) >= 0 && (y) <= lev_fieldy - 1; \
1552 (y) += playfield_scan_delta_y) \
1553 for ((x) = playfield_scan_start_x; \
1554 (x) >= 0 && (x) <= lev_fieldx - 1; \
1555 (x) += playfield_scan_delta_x)
1558 void DEBUG_SetMaximumDynamite()
1562 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1563 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1564 local_player->inventory_element[local_player->inventory_size++] =
1569 static void InitPlayfieldScanModeVars()
1571 if (game.use_reverse_scan_direction)
1573 playfield_scan_start_x = lev_fieldx - 1;
1574 playfield_scan_start_y = lev_fieldy - 1;
1576 playfield_scan_delta_x = -1;
1577 playfield_scan_delta_y = -1;
1581 playfield_scan_start_x = 0;
1582 playfield_scan_start_y = 0;
1584 playfield_scan_delta_x = 1;
1585 playfield_scan_delta_y = 1;
1589 static void InitPlayfieldScanMode(int mode)
1591 game.use_reverse_scan_direction =
1592 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1594 InitPlayfieldScanModeVars();
1597 static int get_move_delay_from_stepsize(int move_stepsize)
1600 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1602 /* make sure that stepsize value is always a power of 2 */
1603 move_stepsize = (1 << log_2(move_stepsize));
1605 return TILEX / move_stepsize;
1608 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1611 int player_nr = player->index_nr;
1612 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1613 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1615 /* do no immediately change move delay -- the player might just be moving */
1616 player->move_delay_value_next = move_delay;
1618 /* information if player can move must be set separately */
1619 player->cannot_move = cannot_move;
1623 player->move_delay = game.initial_move_delay[player_nr];
1624 player->move_delay_value = game.initial_move_delay_value[player_nr];
1626 player->move_delay_value_next = -1;
1628 player->move_delay_reset_counter = 0;
1632 void GetPlayerConfig()
1634 GameFrameDelay = setup.game_frame_delay;
1636 if (!audio.sound_available)
1637 setup.sound_simple = FALSE;
1639 if (!audio.loops_available)
1640 setup.sound_loops = FALSE;
1642 if (!audio.music_available)
1643 setup.sound_music = FALSE;
1645 if (!video.fullscreen_available)
1646 setup.fullscreen = FALSE;
1648 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1650 SetAudioMode(setup.sound);
1653 int GetElementFromGroupElement(int element)
1655 if (IS_GROUP_ELEMENT(element))
1657 struct ElementGroupInfo *group = element_info[element].group;
1658 int last_anim_random_frame = gfx.anim_random_frame;
1661 if (group->choice_mode == ANIM_RANDOM)
1662 gfx.anim_random_frame = RND(group->num_elements_resolved);
1664 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1665 group->choice_mode, 0,
1668 if (group->choice_mode == ANIM_RANDOM)
1669 gfx.anim_random_frame = last_anim_random_frame;
1671 group->choice_pos++;
1673 element = group->element_resolved[element_pos];
1679 static void InitPlayerField(int x, int y, int element, boolean init_game)
1681 if (element == EL_SP_MURPHY)
1685 if (stored_player[0].present)
1687 Feld[x][y] = EL_SP_MURPHY_CLONE;
1693 stored_player[0].initial_element = element;
1694 stored_player[0].use_murphy = TRUE;
1696 if (!level.use_artwork_element[0])
1697 stored_player[0].artwork_element = EL_SP_MURPHY;
1700 Feld[x][y] = EL_PLAYER_1;
1706 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1707 int jx = player->jx, jy = player->jy;
1709 player->present = TRUE;
1711 player->block_last_field = (element == EL_SP_MURPHY ?
1712 level.sp_block_last_field :
1713 level.block_last_field);
1715 /* ---------- initialize player's last field block delay --------------- */
1717 /* always start with reliable default value (no adjustment needed) */
1718 player->block_delay_adjustment = 0;
1720 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1721 if (player->block_last_field && element == EL_SP_MURPHY)
1722 player->block_delay_adjustment = 1;
1724 /* special case 2: in game engines before 3.1.1, blocking was different */
1725 if (game.use_block_last_field_bug)
1726 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1728 if (!options.network || player->connected)
1730 player->active = TRUE;
1732 /* remove potentially duplicate players */
1733 if (StorePlayer[jx][jy] == Feld[x][y])
1734 StorePlayer[jx][jy] = 0;
1736 StorePlayer[x][y] = Feld[x][y];
1738 #if DEBUG_INIT_PLAYER
1741 printf("- player element %d activated", player->element_nr);
1742 printf(" (local player is %d and currently %s)\n",
1743 local_player->element_nr,
1744 local_player->active ? "active" : "not active");
1749 Feld[x][y] = EL_EMPTY;
1751 player->jx = player->last_jx = x;
1752 player->jy = player->last_jy = y;
1757 int player_nr = GET_PLAYER_NR(element);
1758 struct PlayerInfo *player = &stored_player[player_nr];
1760 if (player->active && player->killed)
1761 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1765 static void InitField(int x, int y, boolean init_game)
1767 int element = Feld[x][y];
1776 InitPlayerField(x, y, element, init_game);
1779 case EL_SOKOBAN_FIELD_PLAYER:
1780 element = Feld[x][y] = EL_PLAYER_1;
1781 InitField(x, y, init_game);
1783 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1784 InitField(x, y, init_game);
1787 case EL_SOKOBAN_FIELD_EMPTY:
1788 local_player->sokobanfields_still_needed++;
1792 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1793 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1794 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1795 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1796 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1797 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1798 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1799 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1800 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1801 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1810 case EL_SPACESHIP_RIGHT:
1811 case EL_SPACESHIP_UP:
1812 case EL_SPACESHIP_LEFT:
1813 case EL_SPACESHIP_DOWN:
1814 case EL_BD_BUTTERFLY:
1815 case EL_BD_BUTTERFLY_RIGHT:
1816 case EL_BD_BUTTERFLY_UP:
1817 case EL_BD_BUTTERFLY_LEFT:
1818 case EL_BD_BUTTERFLY_DOWN:
1820 case EL_BD_FIREFLY_RIGHT:
1821 case EL_BD_FIREFLY_UP:
1822 case EL_BD_FIREFLY_LEFT:
1823 case EL_BD_FIREFLY_DOWN:
1824 case EL_PACMAN_RIGHT:
1826 case EL_PACMAN_LEFT:
1827 case EL_PACMAN_DOWN:
1829 case EL_YAMYAM_LEFT:
1830 case EL_YAMYAM_RIGHT:
1832 case EL_YAMYAM_DOWN:
1833 case EL_DARK_YAMYAM:
1836 case EL_SP_SNIKSNAK:
1837 case EL_SP_ELECTRON:
1846 case EL_AMOEBA_FULL:
1851 case EL_AMOEBA_DROP:
1852 if (y == lev_fieldy - 1)
1854 Feld[x][y] = EL_AMOEBA_GROWING;
1855 Store[x][y] = EL_AMOEBA_WET;
1859 case EL_DYNAMITE_ACTIVE:
1860 case EL_SP_DISK_RED_ACTIVE:
1861 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1862 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1863 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1864 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1865 MovDelay[x][y] = 96;
1868 case EL_EM_DYNAMITE_ACTIVE:
1869 MovDelay[x][y] = 32;
1873 local_player->lights_still_needed++;
1877 local_player->friends_still_needed++;
1882 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1885 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1886 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1887 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1888 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1889 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1890 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1891 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1892 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1893 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1894 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1895 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1896 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1899 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1900 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1901 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1903 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1905 game.belt_dir[belt_nr] = belt_dir;
1906 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1908 else /* more than one switch -- set it like the first switch */
1910 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1915 case EL_LIGHT_SWITCH_ACTIVE:
1917 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1920 case EL_INVISIBLE_STEELWALL:
1921 case EL_INVISIBLE_WALL:
1922 case EL_INVISIBLE_SAND:
1923 if (game.light_time_left > 0 ||
1924 game.lenses_time_left > 0)
1925 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1928 case EL_EMC_MAGIC_BALL:
1929 if (game.ball_state)
1930 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1933 case EL_EMC_MAGIC_BALL_SWITCH:
1934 if (game.ball_state)
1935 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1938 case EL_TRIGGER_PLAYER:
1939 case EL_TRIGGER_ELEMENT:
1940 case EL_TRIGGER_CE_VALUE:
1941 case EL_TRIGGER_CE_SCORE:
1943 case EL_ANY_ELEMENT:
1944 case EL_CURRENT_CE_VALUE:
1945 case EL_CURRENT_CE_SCORE:
1962 /* reference elements should not be used on the playfield */
1963 Feld[x][y] = EL_EMPTY;
1967 if (IS_CUSTOM_ELEMENT(element))
1969 if (CAN_MOVE(element))
1972 if (!element_info[element].use_last_ce_value || init_game)
1973 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1975 else if (IS_GROUP_ELEMENT(element))
1977 Feld[x][y] = GetElementFromGroupElement(element);
1979 InitField(x, y, init_game);
1986 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1989 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1991 InitField(x, y, init_game);
1993 /* not needed to call InitMovDir() -- already done by InitField()! */
1994 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1995 CAN_MOVE(Feld[x][y]))
1999 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2001 int old_element = Feld[x][y];
2003 InitField(x, y, init_game);
2005 /* not needed to call InitMovDir() -- already done by InitField()! */
2006 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2007 CAN_MOVE(old_element) &&
2008 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2011 /* this case is in fact a combination of not less than three bugs:
2012 first, it calls InitMovDir() for elements that can move, although this is
2013 already done by InitField(); then, it checks the element that was at this
2014 field _before_ the call to InitField() (which can change it); lastly, it
2015 was not called for "mole with direction" elements, which were treated as
2016 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2020 static int get_key_element_from_nr(int key_nr)
2022 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2023 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2024 EL_EM_KEY_1 : EL_KEY_1);
2026 return key_base_element + key_nr;
2029 static int get_next_dropped_element(struct PlayerInfo *player)
2031 return (player->inventory_size > 0 ?
2032 player->inventory_element[player->inventory_size - 1] :
2033 player->inventory_infinite_element != EL_UNDEFINED ?
2034 player->inventory_infinite_element :
2035 player->dynabombs_left > 0 ?
2036 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2040 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2042 /* pos >= 0: get element from bottom of the stack;
2043 pos < 0: get element from top of the stack */
2047 int min_inventory_size = -pos;
2048 int inventory_pos = player->inventory_size - min_inventory_size;
2049 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2051 return (player->inventory_size >= min_inventory_size ?
2052 player->inventory_element[inventory_pos] :
2053 player->inventory_infinite_element != EL_UNDEFINED ?
2054 player->inventory_infinite_element :
2055 player->dynabombs_left >= min_dynabombs_left ?
2056 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2061 int min_dynabombs_left = pos + 1;
2062 int min_inventory_size = pos + 1 - player->dynabombs_left;
2063 int inventory_pos = pos - player->dynabombs_left;
2065 return (player->inventory_infinite_element != EL_UNDEFINED ?
2066 player->inventory_infinite_element :
2067 player->dynabombs_left >= min_dynabombs_left ?
2068 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2069 player->inventory_size >= min_inventory_size ?
2070 player->inventory_element[inventory_pos] :
2075 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2077 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2078 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2081 if (gpo1->sort_priority != gpo2->sort_priority)
2082 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2084 compare_result = gpo1->nr - gpo2->nr;
2086 return compare_result;
2089 int getPlayerInventorySize(int player_nr)
2091 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2092 return level.native_em_level->ply[player_nr]->dynamite;
2093 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2094 return level.native_sp_level->game_sp->red_disk_count;
2096 return stored_player[player_nr].inventory_size;
2099 void InitGameControlValues()
2103 for (i = 0; game_panel_controls[i].nr != -1; i++)
2105 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2106 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2107 struct TextPosInfo *pos = gpc->pos;
2109 int type = gpc->type;
2113 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2114 Error(ERR_EXIT, "this should not happen -- please debug");
2117 /* force update of game controls after initialization */
2118 gpc->value = gpc->last_value = -1;
2119 gpc->frame = gpc->last_frame = -1;
2120 gpc->gfx_frame = -1;
2122 /* determine panel value width for later calculation of alignment */
2123 if (type == TYPE_INTEGER || type == TYPE_STRING)
2125 pos->width = pos->size * getFontWidth(pos->font);
2126 pos->height = getFontHeight(pos->font);
2128 else if (type == TYPE_ELEMENT)
2130 pos->width = pos->size;
2131 pos->height = pos->size;
2134 /* fill structure for game panel draw order */
2136 gpo->sort_priority = pos->sort_priority;
2139 /* sort game panel controls according to sort_priority and control number */
2140 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2141 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2144 void UpdatePlayfieldElementCount()
2146 boolean use_element_count = FALSE;
2149 /* first check if it is needed at all to calculate playfield element count */
2150 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2151 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2152 use_element_count = TRUE;
2154 if (!use_element_count)
2157 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2158 element_info[i].element_count = 0;
2160 SCAN_PLAYFIELD(x, y)
2162 element_info[Feld[x][y]].element_count++;
2165 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2166 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2167 if (IS_IN_GROUP(j, i))
2168 element_info[EL_GROUP_START + i].element_count +=
2169 element_info[j].element_count;
2172 void UpdateGameControlValues()
2175 int time = (local_player->LevelSolved ?
2176 local_player->LevelSolved_CountingTime :
2177 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2178 level.native_em_level->lev->time :
2179 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2180 level.native_sp_level->game_sp->time_played :
2181 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2182 game_mm.energy_left :
2183 game.no_time_limit ? TimePlayed : TimeLeft);
2184 int score = (local_player->LevelSolved ?
2185 local_player->LevelSolved_CountingScore :
2186 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2187 level.native_em_level->lev->score :
2188 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2189 level.native_sp_level->game_sp->score :
2190 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2192 local_player->score);
2193 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194 level.native_em_level->lev->required :
2195 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196 level.native_sp_level->game_sp->infotrons_still_needed :
2197 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198 game_mm.kettles_still_needed :
2199 local_player->gems_still_needed);
2200 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201 level.native_em_level->lev->required > 0 :
2202 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2204 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205 game_mm.kettles_still_needed > 0 ||
2206 game_mm.lights_still_needed > 0 :
2207 local_player->gems_still_needed > 0 ||
2208 local_player->sokobanfields_still_needed > 0 ||
2209 local_player->lights_still_needed > 0);
2210 int health = (local_player->LevelSolved ?
2211 local_player->LevelSolved_CountingHealth :
2212 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2213 MM_HEALTH(game_mm.laser_overload_value) :
2214 local_player->health);
2216 UpdatePlayfieldElementCount();
2218 /* update game panel control values */
2220 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2221 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2223 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2224 for (i = 0; i < MAX_NUM_KEYS; i++)
2225 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2226 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2227 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2229 if (game.centered_player_nr == -1)
2231 for (i = 0; i < MAX_PLAYERS; i++)
2233 /* only one player in Supaplex game engine */
2234 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2237 for (k = 0; k < MAX_NUM_KEYS; k++)
2239 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2241 if (level.native_em_level->ply[i]->keys & (1 << k))
2242 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2243 get_key_element_from_nr(k);
2245 else if (stored_player[i].key[k])
2246 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2247 get_key_element_from_nr(k);
2250 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2251 getPlayerInventorySize(i);
2253 if (stored_player[i].num_white_keys > 0)
2254 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2257 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2258 stored_player[i].num_white_keys;
2263 int player_nr = game.centered_player_nr;
2265 for (k = 0; k < MAX_NUM_KEYS; k++)
2267 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2269 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2270 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2271 get_key_element_from_nr(k);
2273 else if (stored_player[player_nr].key[k])
2274 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2275 get_key_element_from_nr(k);
2278 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2279 getPlayerInventorySize(player_nr);
2281 if (stored_player[player_nr].num_white_keys > 0)
2282 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2284 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2285 stored_player[player_nr].num_white_keys;
2288 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2290 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2291 get_inventory_element_from_pos(local_player, i);
2292 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2293 get_inventory_element_from_pos(local_player, -i - 1);
2296 game_panel_controls[GAME_PANEL_SCORE].value = score;
2297 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2299 game_panel_controls[GAME_PANEL_TIME].value = time;
2301 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2302 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2303 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2305 if (game.no_time_limit)
2306 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2308 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2310 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2311 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2313 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2315 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2316 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2318 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2319 local_player->shield_normal_time_left;
2320 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2321 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2323 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2324 local_player->shield_deadly_time_left;
2326 game_panel_controls[GAME_PANEL_EXIT].value =
2327 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2329 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2330 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2331 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2332 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2333 EL_EMC_MAGIC_BALL_SWITCH);
2335 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2336 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2337 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2338 game.light_time_left;
2340 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2341 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2342 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2343 game.timegate_time_left;
2345 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2346 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2348 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2349 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2350 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2351 game.lenses_time_left;
2353 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2354 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2355 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2356 game.magnify_time_left;
2358 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2359 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2360 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2361 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2362 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2363 EL_BALLOON_SWITCH_NONE);
2365 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2366 local_player->dynabomb_count;
2367 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2368 local_player->dynabomb_size;
2369 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2370 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2372 game_panel_controls[GAME_PANEL_PENGUINS].value =
2373 local_player->friends_still_needed;
2375 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2376 local_player->sokobanfields_still_needed;
2377 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2378 local_player->sokobanfields_still_needed;
2380 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2381 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2383 for (i = 0; i < NUM_BELTS; i++)
2385 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2386 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2387 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2388 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2389 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2392 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2393 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2394 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2395 game.magic_wall_time_left;
2397 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2398 local_player->gravity;
2400 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2401 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2403 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2404 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2405 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2406 game.panel.element[i].id : EL_UNDEFINED);
2408 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2410 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2411 element_info[game.panel.element_count[i].id].element_count : 0);
2413 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2414 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2415 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2416 element_info[game.panel.ce_score[i].id].collect_score : 0);
2418 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2420 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2421 element_info[game.panel.ce_score_element[i].id].collect_score :
2424 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2425 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2426 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2428 /* update game panel control frames */
2430 for (i = 0; game_panel_controls[i].nr != -1; i++)
2432 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2434 if (gpc->type == TYPE_ELEMENT)
2436 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2438 int last_anim_random_frame = gfx.anim_random_frame;
2439 int element = gpc->value;
2440 int graphic = el2panelimg(element);
2442 if (gpc->value != gpc->last_value)
2445 gpc->gfx_random = INIT_GFX_RANDOM();
2451 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2452 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2453 gpc->gfx_random = INIT_GFX_RANDOM();
2456 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2457 gfx.anim_random_frame = gpc->gfx_random;
2459 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2460 gpc->gfx_frame = element_info[element].collect_score;
2462 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2465 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2466 gfx.anim_random_frame = last_anim_random_frame;
2469 else if (gpc->type == TYPE_GRAPHIC)
2471 if (gpc->graphic != IMG_UNDEFINED)
2473 int last_anim_random_frame = gfx.anim_random_frame;
2474 int graphic = gpc->graphic;
2476 if (gpc->value != gpc->last_value)
2479 gpc->gfx_random = INIT_GFX_RANDOM();
2485 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2486 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2487 gpc->gfx_random = INIT_GFX_RANDOM();
2490 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2491 gfx.anim_random_frame = gpc->gfx_random;
2493 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2495 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496 gfx.anim_random_frame = last_anim_random_frame;
2502 void DisplayGameControlValues()
2504 boolean redraw_panel = FALSE;
2507 for (i = 0; game_panel_controls[i].nr != -1; i++)
2509 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2511 if (PANEL_DEACTIVATED(gpc->pos))
2514 if (gpc->value == gpc->last_value &&
2515 gpc->frame == gpc->last_frame)
2518 redraw_panel = TRUE;
2524 /* copy default game door content to main double buffer */
2526 /* !!! CHECK AGAIN !!! */
2527 SetPanelBackground();
2528 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2529 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2531 /* redraw game control buttons */
2532 RedrawGameButtons();
2534 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2536 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2538 int nr = game_panel_order[i].nr;
2539 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2540 struct TextPosInfo *pos = gpc->pos;
2541 int type = gpc->type;
2542 int value = gpc->value;
2543 int frame = gpc->frame;
2544 int size = pos->size;
2545 int font = pos->font;
2546 boolean draw_masked = pos->draw_masked;
2547 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2549 if (PANEL_DEACTIVATED(pos))
2552 gpc->last_value = value;
2553 gpc->last_frame = frame;
2555 if (type == TYPE_INTEGER)
2557 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2558 nr == GAME_PANEL_TIME)
2560 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2562 if (use_dynamic_size) /* use dynamic number of digits */
2564 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2565 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2566 int size2 = size1 + 1;
2567 int font1 = pos->font;
2568 int font2 = pos->font_alt;
2570 size = (value < value_change ? size1 : size2);
2571 font = (value < value_change ? font1 : font2);
2575 /* correct text size if "digits" is zero or less */
2577 size = strlen(int2str(value, size));
2579 /* dynamically correct text alignment */
2580 pos->width = size * getFontWidth(font);
2582 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2583 int2str(value, size), font, mask_mode);
2585 else if (type == TYPE_ELEMENT)
2587 int element, graphic;
2591 int dst_x = PANEL_XPOS(pos);
2592 int dst_y = PANEL_YPOS(pos);
2594 if (value != EL_UNDEFINED && value != EL_EMPTY)
2597 graphic = el2panelimg(value);
2599 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2601 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2604 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2607 width = graphic_info[graphic].width * size / TILESIZE;
2608 height = graphic_info[graphic].height * size / TILESIZE;
2611 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2614 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2618 else if (type == TYPE_GRAPHIC)
2620 int graphic = gpc->graphic;
2621 int graphic_active = gpc->graphic_active;
2625 int dst_x = PANEL_XPOS(pos);
2626 int dst_y = PANEL_YPOS(pos);
2627 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2628 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2630 if (graphic != IMG_UNDEFINED && !skip)
2632 if (pos->style == STYLE_REVERSE)
2633 value = 100 - value;
2635 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2637 if (pos->direction & MV_HORIZONTAL)
2639 width = graphic_info[graphic_active].width * value / 100;
2640 height = graphic_info[graphic_active].height;
2642 if (pos->direction == MV_LEFT)
2644 src_x += graphic_info[graphic_active].width - width;
2645 dst_x += graphic_info[graphic_active].width - width;
2650 width = graphic_info[graphic_active].width;
2651 height = graphic_info[graphic_active].height * value / 100;
2653 if (pos->direction == MV_UP)
2655 src_y += graphic_info[graphic_active].height - height;
2656 dst_y += graphic_info[graphic_active].height - height;
2661 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2664 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2667 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2669 if (pos->direction & MV_HORIZONTAL)
2671 if (pos->direction == MV_RIGHT)
2678 dst_x = PANEL_XPOS(pos);
2681 width = graphic_info[graphic].width - width;
2685 if (pos->direction == MV_DOWN)
2692 dst_y = PANEL_YPOS(pos);
2695 height = graphic_info[graphic].height - height;
2699 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2702 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2706 else if (type == TYPE_STRING)
2708 boolean active = (value != 0);
2709 char *state_normal = "off";
2710 char *state_active = "on";
2711 char *state = (active ? state_active : state_normal);
2712 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2713 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2714 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2715 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2717 if (nr == GAME_PANEL_GRAVITY_STATE)
2719 int font1 = pos->font; /* (used for normal state) */
2720 int font2 = pos->font_alt; /* (used for active state) */
2722 font = (active ? font2 : font1);
2731 /* don't truncate output if "chars" is zero or less */
2734 /* dynamically correct text alignment */
2735 pos->width = size * getFontWidth(font);
2738 s_cut = getStringCopyN(s, size);
2740 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2741 s_cut, font, mask_mode);
2747 redraw_mask |= REDRAW_DOOR_1;
2750 SetGameStatus(GAME_MODE_PLAYING);
2753 void UpdateAndDisplayGameControlValues()
2755 if (tape.deactivate_display)
2758 UpdateGameControlValues();
2759 DisplayGameControlValues();
2762 void UpdateGameDoorValues()
2764 UpdateGameControlValues();
2767 void DrawGameDoorValues()
2769 DisplayGameControlValues();
2774 =============================================================================
2776 -----------------------------------------------------------------------------
2777 initialize game engine due to level / tape version number
2778 =============================================================================
2781 static void InitGameEngine()
2783 int i, j, k, l, x, y;
2785 /* set game engine from tape file when re-playing, else from level file */
2786 game.engine_version = (tape.playing ? tape.engine_version :
2787 level.game_version);
2789 /* set single or multi-player game mode (needed for re-playing tapes) */
2790 game.team_mode = setup.team_mode;
2794 int num_players = 0;
2796 for (i = 0; i < MAX_PLAYERS; i++)
2797 if (tape.player_participates[i])
2800 /* multi-player tapes contain input data for more than one player */
2801 game.team_mode = (num_players > 1);
2804 /* ---------------------------------------------------------------------- */
2805 /* set flags for bugs and changes according to active game engine version */
2806 /* ---------------------------------------------------------------------- */
2809 Summary of bugfix/change:
2810 Fixed handling for custom elements that change when pushed by the player.
2812 Fixed/changed in version:
2816 Before 3.1.0, custom elements that "change when pushing" changed directly
2817 after the player started pushing them (until then handled in "DigField()").
2818 Since 3.1.0, these custom elements are not changed until the "pushing"
2819 move of the element is finished (now handled in "ContinueMoving()").
2821 Affected levels/tapes:
2822 The first condition is generally needed for all levels/tapes before version
2823 3.1.0, which might use the old behaviour before it was changed; known tapes
2824 that are affected are some tapes from the level set "Walpurgis Gardens" by
2826 The second condition is an exception from the above case and is needed for
2827 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2828 above (including some development versions of 3.1.0), but before it was
2829 known that this change would break tapes like the above and was fixed in
2830 3.1.1, so that the changed behaviour was active although the engine version
2831 while recording maybe was before 3.1.0. There is at least one tape that is
2832 affected by this exception, which is the tape for the one-level set "Bug
2833 Machine" by Juergen Bonhagen.
2836 game.use_change_when_pushing_bug =
2837 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2839 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2840 tape.game_version < VERSION_IDENT(3,1,1,0)));
2843 Summary of bugfix/change:
2844 Fixed handling for blocking the field the player leaves when moving.
2846 Fixed/changed in version:
2850 Before 3.1.1, when "block last field when moving" was enabled, the field
2851 the player is leaving when moving was blocked for the time of the move,
2852 and was directly unblocked afterwards. This resulted in the last field
2853 being blocked for exactly one less than the number of frames of one player
2854 move. Additionally, even when blocking was disabled, the last field was
2855 blocked for exactly one frame.
2856 Since 3.1.1, due to changes in player movement handling, the last field
2857 is not blocked at all when blocking is disabled. When blocking is enabled,
2858 the last field is blocked for exactly the number of frames of one player
2859 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2860 last field is blocked for exactly one more than the number of frames of
2863 Affected levels/tapes:
2864 (!!! yet to be determined -- probably many !!!)
2867 game.use_block_last_field_bug =
2868 (game.engine_version < VERSION_IDENT(3,1,1,0));
2870 game_em.use_single_button =
2871 (game.engine_version > VERSION_IDENT(4,0,0,2));
2873 game_em.use_snap_key_bug =
2874 (game.engine_version < VERSION_IDENT(4,0,1,0));
2876 /* ---------------------------------------------------------------------- */
2878 /* set maximal allowed number of custom element changes per game frame */
2879 game.max_num_changes_per_frame = 1;
2881 /* default scan direction: scan playfield from top/left to bottom/right */
2882 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2884 /* dynamically adjust element properties according to game engine version */
2885 InitElementPropertiesEngine(game.engine_version);
2888 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2889 printf(" tape version == %06d [%s] [file: %06d]\n",
2890 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2892 printf(" => game.engine_version == %06d\n", game.engine_version);
2895 /* ---------- initialize player's initial move delay --------------------- */
2897 /* dynamically adjust player properties according to level information */
2898 for (i = 0; i < MAX_PLAYERS; i++)
2899 game.initial_move_delay_value[i] =
2900 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2902 /* dynamically adjust player properties according to game engine version */
2903 for (i = 0; i < MAX_PLAYERS; i++)
2904 game.initial_move_delay[i] =
2905 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2906 game.initial_move_delay_value[i] : 0);
2908 /* ---------- initialize player's initial push delay --------------------- */
2910 /* dynamically adjust player properties according to game engine version */
2911 game.initial_push_delay_value =
2912 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2914 /* ---------- initialize changing elements ------------------------------- */
2916 /* initialize changing elements information */
2917 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2919 struct ElementInfo *ei = &element_info[i];
2921 /* this pointer might have been changed in the level editor */
2922 ei->change = &ei->change_page[0];
2924 if (!IS_CUSTOM_ELEMENT(i))
2926 ei->change->target_element = EL_EMPTY_SPACE;
2927 ei->change->delay_fixed = 0;
2928 ei->change->delay_random = 0;
2929 ei->change->delay_frames = 1;
2932 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2934 ei->has_change_event[j] = FALSE;
2936 ei->event_page_nr[j] = 0;
2937 ei->event_page[j] = &ei->change_page[0];
2941 /* add changing elements from pre-defined list */
2942 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2944 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2945 struct ElementInfo *ei = &element_info[ch_delay->element];
2947 ei->change->target_element = ch_delay->target_element;
2948 ei->change->delay_fixed = ch_delay->change_delay;
2950 ei->change->pre_change_function = ch_delay->pre_change_function;
2951 ei->change->change_function = ch_delay->change_function;
2952 ei->change->post_change_function = ch_delay->post_change_function;
2954 ei->change->can_change = TRUE;
2955 ei->change->can_change_or_has_action = TRUE;
2957 ei->has_change_event[CE_DELAY] = TRUE;
2959 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2960 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2963 /* ---------- initialize internal run-time variables --------------------- */
2965 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2967 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2969 for (j = 0; j < ei->num_change_pages; j++)
2971 ei->change_page[j].can_change_or_has_action =
2972 (ei->change_page[j].can_change |
2973 ei->change_page[j].has_action);
2977 /* add change events from custom element configuration */
2978 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2980 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2982 for (j = 0; j < ei->num_change_pages; j++)
2984 if (!ei->change_page[j].can_change_or_has_action)
2987 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2989 /* only add event page for the first page found with this event */
2990 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2992 ei->has_change_event[k] = TRUE;
2994 ei->event_page_nr[k] = j;
2995 ei->event_page[k] = &ei->change_page[j];
3001 /* ---------- initialize reference elements in change conditions --------- */
3003 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3005 int element = EL_CUSTOM_START + i;
3006 struct ElementInfo *ei = &element_info[element];
3008 for (j = 0; j < ei->num_change_pages; j++)
3010 int trigger_element = ei->change_page[j].initial_trigger_element;
3012 if (trigger_element >= EL_PREV_CE_8 &&
3013 trigger_element <= EL_NEXT_CE_8)
3014 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3016 ei->change_page[j].trigger_element = trigger_element;
3020 /* ---------- initialize run-time trigger player and element ------------- */
3022 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3024 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3026 for (j = 0; j < ei->num_change_pages; j++)
3028 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3029 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3030 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3031 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3032 ei->change_page[j].actual_trigger_ce_value = 0;
3033 ei->change_page[j].actual_trigger_ce_score = 0;
3037 /* ---------- initialize trigger events ---------------------------------- */
3039 /* initialize trigger events information */
3040 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3041 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3042 trigger_events[i][j] = FALSE;
3044 /* add trigger events from element change event properties */
3045 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047 struct ElementInfo *ei = &element_info[i];
3049 for (j = 0; j < ei->num_change_pages; j++)
3051 if (!ei->change_page[j].can_change_or_has_action)
3054 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3056 int trigger_element = ei->change_page[j].trigger_element;
3058 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3060 if (ei->change_page[j].has_event[k])
3062 if (IS_GROUP_ELEMENT(trigger_element))
3064 struct ElementGroupInfo *group =
3065 element_info[trigger_element].group;
3067 for (l = 0; l < group->num_elements_resolved; l++)
3068 trigger_events[group->element_resolved[l]][k] = TRUE;
3070 else if (trigger_element == EL_ANY_ELEMENT)
3071 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3072 trigger_events[l][k] = TRUE;
3074 trigger_events[trigger_element][k] = TRUE;
3081 /* ---------- initialize push delay -------------------------------------- */
3083 /* initialize push delay values to default */
3084 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3086 if (!IS_CUSTOM_ELEMENT(i))
3088 /* set default push delay values (corrected since version 3.0.7-1) */
3089 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3091 element_info[i].push_delay_fixed = 2;
3092 element_info[i].push_delay_random = 8;
3096 element_info[i].push_delay_fixed = 8;
3097 element_info[i].push_delay_random = 8;
3102 /* set push delay value for certain elements from pre-defined list */
3103 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3105 int e = push_delay_list[i].element;
3107 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3108 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3111 /* set push delay value for Supaplex elements for newer engine versions */
3112 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3114 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3116 if (IS_SP_ELEMENT(i))
3118 /* set SP push delay to just enough to push under a falling zonk */
3119 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3121 element_info[i].push_delay_fixed = delay;
3122 element_info[i].push_delay_random = 0;
3127 /* ---------- initialize move stepsize ----------------------------------- */
3129 /* initialize move stepsize values to default */
3130 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3131 if (!IS_CUSTOM_ELEMENT(i))
3132 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3134 /* set move stepsize value for certain elements from pre-defined list */
3135 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3137 int e = move_stepsize_list[i].element;
3139 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3142 /* ---------- initialize collect score ----------------------------------- */
3144 /* initialize collect score values for custom elements from initial value */
3145 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3146 if (IS_CUSTOM_ELEMENT(i))
3147 element_info[i].collect_score = element_info[i].collect_score_initial;
3149 /* ---------- initialize collect count ----------------------------------- */
3151 /* initialize collect count values for non-custom elements */
3152 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153 if (!IS_CUSTOM_ELEMENT(i))
3154 element_info[i].collect_count_initial = 0;
3156 /* add collect count values for all elements from pre-defined list */
3157 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3158 element_info[collect_count_list[i].element].collect_count_initial =
3159 collect_count_list[i].count;
3161 /* ---------- initialize access direction -------------------------------- */
3163 /* initialize access direction values to default (access from every side) */
3164 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3165 if (!IS_CUSTOM_ELEMENT(i))
3166 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3168 /* set access direction value for certain elements from pre-defined list */
3169 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3170 element_info[access_direction_list[i].element].access_direction =
3171 access_direction_list[i].direction;
3173 /* ---------- initialize explosion content ------------------------------- */
3174 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3176 if (IS_CUSTOM_ELEMENT(i))
3179 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3181 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3183 element_info[i].content.e[x][y] =
3184 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3185 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3186 i == EL_PLAYER_3 ? EL_EMERALD :
3187 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3188 i == EL_MOLE ? EL_EMERALD_RED :
3189 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3190 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3191 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3192 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3193 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3194 i == EL_WALL_EMERALD ? EL_EMERALD :
3195 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3196 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3197 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3198 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3199 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3200 i == EL_WALL_PEARL ? EL_PEARL :
3201 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3206 /* ---------- initialize recursion detection ------------------------------ */
3207 recursion_loop_depth = 0;
3208 recursion_loop_detected = FALSE;
3209 recursion_loop_element = EL_UNDEFINED;
3211 /* ---------- initialize graphics engine ---------------------------------- */
3212 game.scroll_delay_value =
3213 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3214 setup.scroll_delay ? setup.scroll_delay_value : 0);
3215 game.scroll_delay_value =
3216 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3218 /* ---------- initialize game engine snapshots ---------------------------- */
3219 for (i = 0; i < MAX_PLAYERS; i++)
3220 game.snapshot.last_action[i] = 0;
3221 game.snapshot.changed_action = FALSE;
3222 game.snapshot.collected_item = FALSE;
3223 game.snapshot.mode =
3224 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3225 SNAPSHOT_MODE_EVERY_STEP :
3226 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3227 SNAPSHOT_MODE_EVERY_MOVE :
3228 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3229 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3230 game.snapshot.save_snapshot = FALSE;
3232 /* ---------- initialize level time for Supaplex engine ------------------- */
3233 /* Supaplex levels with time limit currently unsupported -- should be added */
3234 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3238 int get_num_special_action(int element, int action_first, int action_last)
3240 int num_special_action = 0;
3243 for (i = action_first; i <= action_last; i++)
3245 boolean found = FALSE;
3247 for (j = 0; j < NUM_DIRECTIONS; j++)
3248 if (el_act_dir2img(element, i, j) !=
3249 el_act_dir2img(element, ACTION_DEFAULT, j))
3253 num_special_action++;
3258 return num_special_action;
3263 =============================================================================
3265 -----------------------------------------------------------------------------
3266 initialize and start new game
3267 =============================================================================
3272 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3273 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3274 int fade_mask = REDRAW_FIELD;
3276 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3277 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3278 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3279 int initial_move_dir = MV_DOWN;
3282 // required here to update video display before fading (FIX THIS)
3283 DrawMaskedBorder(REDRAW_DOOR_2);
3285 if (!game.restart_level)
3286 CloseDoor(DOOR_CLOSE_1);
3288 SetGameStatus(GAME_MODE_PLAYING);
3290 if (level_editor_test_game)
3291 FadeSkipNextFadeIn();
3293 FadeSetEnterScreen();
3295 if (CheckIfGlobalBorderHasChanged())
3296 fade_mask = REDRAW_ALL;
3298 FadeLevelSoundsAndMusic();
3300 ExpireSoundLoops(TRUE);
3304 /* needed if different viewport properties defined for playing */
3305 ChangeViewportPropertiesIfNeeded();
3309 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3311 DrawCompleteVideoDisplay();
3314 InitGameControlValues();
3316 /* don't play tapes over network */
3317 network_playing = (options.network && !tape.playing);
3319 for (i = 0; i < MAX_PLAYERS; i++)
3321 struct PlayerInfo *player = &stored_player[i];
3323 player->index_nr = i;
3324 player->index_bit = (1 << i);
3325 player->element_nr = EL_PLAYER_1 + i;
3327 player->present = FALSE;
3328 player->active = FALSE;
3329 player->mapped = FALSE;
3331 player->killed = FALSE;
3332 player->reanimated = FALSE;
3335 player->effective_action = 0;
3336 player->programmed_action = 0;
3338 player->mouse_action.lx = 0;
3339 player->mouse_action.ly = 0;
3340 player->mouse_action.button = 0;
3342 player->effective_mouse_action.lx = 0;
3343 player->effective_mouse_action.ly = 0;
3344 player->effective_mouse_action.button = 0;
3347 player->score_final = 0;
3349 player->health = MAX_HEALTH;
3350 player->health_final = MAX_HEALTH;
3352 player->gems_still_needed = level.gems_needed;
3353 player->sokobanfields_still_needed = 0;
3354 player->lights_still_needed = 0;
3355 player->friends_still_needed = 0;
3357 for (j = 0; j < MAX_NUM_KEYS; j++)
3358 player->key[j] = FALSE;
3360 player->num_white_keys = 0;
3362 player->dynabomb_count = 0;
3363 player->dynabomb_size = 1;
3364 player->dynabombs_left = 0;
3365 player->dynabomb_xl = FALSE;
3367 player->MovDir = initial_move_dir;
3370 player->GfxDir = initial_move_dir;
3371 player->GfxAction = ACTION_DEFAULT;
3373 player->StepFrame = 0;
3375 player->initial_element = player->element_nr;
3376 player->artwork_element =
3377 (level.use_artwork_element[i] ? level.artwork_element[i] :
3378 player->element_nr);
3379 player->use_murphy = FALSE;
3381 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3382 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3384 player->gravity = level.initial_player_gravity[i];
3386 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3388 player->actual_frame_counter = 0;
3390 player->step_counter = 0;
3392 player->last_move_dir = initial_move_dir;
3394 player->is_active = FALSE;
3396 player->is_waiting = FALSE;
3397 player->is_moving = FALSE;
3398 player->is_auto_moving = FALSE;
3399 player->is_digging = FALSE;
3400 player->is_snapping = FALSE;
3401 player->is_collecting = FALSE;
3402 player->is_pushing = FALSE;
3403 player->is_switching = FALSE;
3404 player->is_dropping = FALSE;
3405 player->is_dropping_pressed = FALSE;
3407 player->is_bored = FALSE;
3408 player->is_sleeping = FALSE;
3410 player->was_waiting = TRUE;
3411 player->was_moving = FALSE;
3412 player->was_snapping = FALSE;
3413 player->was_dropping = FALSE;
3415 player->force_dropping = FALSE;
3417 player->frame_counter_bored = -1;
3418 player->frame_counter_sleeping = -1;
3420 player->anim_delay_counter = 0;
3421 player->post_delay_counter = 0;
3423 player->dir_waiting = initial_move_dir;
3424 player->action_waiting = ACTION_DEFAULT;
3425 player->last_action_waiting = ACTION_DEFAULT;
3426 player->special_action_bored = ACTION_DEFAULT;
3427 player->special_action_sleeping = ACTION_DEFAULT;
3429 player->switch_x = -1;
3430 player->switch_y = -1;
3432 player->drop_x = -1;
3433 player->drop_y = -1;
3435 player->show_envelope = 0;
3437 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3439 player->push_delay = -1; /* initialized when pushing starts */
3440 player->push_delay_value = game.initial_push_delay_value;
3442 player->drop_delay = 0;
3443 player->drop_pressed_delay = 0;
3445 player->last_jx = -1;
3446 player->last_jy = -1;
3450 player->shield_normal_time_left = 0;
3451 player->shield_deadly_time_left = 0;
3453 player->inventory_infinite_element = EL_UNDEFINED;
3454 player->inventory_size = 0;
3456 if (level.use_initial_inventory[i])
3458 for (j = 0; j < level.initial_inventory_size[i]; j++)
3460 int element = level.initial_inventory_content[i][j];
3461 int collect_count = element_info[element].collect_count_initial;
3464 if (!IS_CUSTOM_ELEMENT(element))
3467 if (collect_count == 0)
3468 player->inventory_infinite_element = element;
3470 for (k = 0; k < collect_count; k++)
3471 if (player->inventory_size < MAX_INVENTORY_SIZE)
3472 player->inventory_element[player->inventory_size++] = element;
3476 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3477 SnapField(player, 0, 0);
3479 player->LevelSolved = FALSE;
3480 player->GameOver = FALSE;
3482 player->LevelSolved_GameWon = FALSE;
3483 player->LevelSolved_GameEnd = FALSE;
3484 player->LevelSolved_PanelOff = FALSE;
3485 player->LevelSolved_SaveTape = FALSE;
3486 player->LevelSolved_SaveScore = FALSE;
3488 player->LevelSolved_CountingTime = 0;
3489 player->LevelSolved_CountingScore = 0;
3490 player->LevelSolved_CountingHealth = 0;
3492 map_player_action[i] = i;
3495 network_player_action_received = FALSE;
3497 #if defined(NETWORK_AVALIABLE)
3498 /* initial null action */
3499 if (network_playing)
3500 SendToServer_MovePlayer(MV_NONE);
3509 TimeLeft = level.time;
3512 ScreenMovDir = MV_NONE;
3516 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3518 AllPlayersGone = FALSE;
3520 game.no_time_limit = (level.time == 0);
3522 game.yamyam_content_nr = 0;
3523 game.robot_wheel_active = FALSE;
3524 game.magic_wall_active = FALSE;
3525 game.magic_wall_time_left = 0;
3526 game.light_time_left = 0;
3527 game.timegate_time_left = 0;
3528 game.switchgate_pos = 0;
3529 game.wind_direction = level.wind_direction_initial;
3531 game.lenses_time_left = 0;
3532 game.magnify_time_left = 0;
3534 game.ball_state = level.ball_state_initial;
3535 game.ball_content_nr = 0;
3537 game.envelope_active = FALSE;
3539 /* set focus to local player for network games, else to all players */
3540 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3541 game.centered_player_nr_next = game.centered_player_nr;
3542 game.set_centered_player = FALSE;
3544 if (network_playing && tape.recording)
3546 /* store client dependent player focus when recording network games */
3547 tape.centered_player_nr_next = game.centered_player_nr_next;
3548 tape.set_centered_player = TRUE;
3551 for (i = 0; i < NUM_BELTS; i++)
3553 game.belt_dir[i] = MV_NONE;
3554 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3557 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3558 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3560 #if DEBUG_INIT_PLAYER
3563 printf("Player status at level initialization:\n");
3567 SCAN_PLAYFIELD(x, y)
3569 Feld[x][y] = level.field[x][y];
3570 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3571 ChangeDelay[x][y] = 0;
3572 ChangePage[x][y] = -1;
3573 CustomValue[x][y] = 0; /* initialized in InitField() */
3574 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3576 WasJustMoving[x][y] = 0;
3577 WasJustFalling[x][y] = 0;
3578 CheckCollision[x][y] = 0;
3579 CheckImpact[x][y] = 0;
3581 Pushed[x][y] = FALSE;
3583 ChangeCount[x][y] = 0;
3584 ChangeEvent[x][y] = -1;
3586 ExplodePhase[x][y] = 0;
3587 ExplodeDelay[x][y] = 0;
3588 ExplodeField[x][y] = EX_TYPE_NONE;
3590 RunnerVisit[x][y] = 0;
3591 PlayerVisit[x][y] = 0;
3594 GfxRandom[x][y] = INIT_GFX_RANDOM();
3595 GfxElement[x][y] = EL_UNDEFINED;
3596 GfxAction[x][y] = ACTION_DEFAULT;
3597 GfxDir[x][y] = MV_NONE;
3598 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3601 SCAN_PLAYFIELD(x, y)
3603 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3605 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3607 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3610 InitField(x, y, TRUE);
3612 ResetGfxAnimation(x, y);
3617 for (i = 0; i < MAX_PLAYERS; i++)
3619 struct PlayerInfo *player = &stored_player[i];
3621 /* set number of special actions for bored and sleeping animation */
3622 player->num_special_action_bored =
3623 get_num_special_action(player->artwork_element,
3624 ACTION_BORING_1, ACTION_BORING_LAST);
3625 player->num_special_action_sleeping =
3626 get_num_special_action(player->artwork_element,
3627 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3630 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3631 emulate_sb ? EMU_SOKOBAN :
3632 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3634 /* initialize type of slippery elements */
3635 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637 if (!IS_CUSTOM_ELEMENT(i))
3639 /* default: elements slip down either to the left or right randomly */
3640 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3642 /* SP style elements prefer to slip down on the left side */
3643 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3644 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3646 /* BD style elements prefer to slip down on the left side */
3647 if (game.emulation == EMU_BOULDERDASH)
3648 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3652 /* initialize explosion and ignition delay */
3653 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3655 if (!IS_CUSTOM_ELEMENT(i))
3658 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3659 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3660 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3661 int last_phase = (num_phase + 1) * delay;
3662 int half_phase = (num_phase / 2) * delay;
3664 element_info[i].explosion_delay = last_phase - 1;
3665 element_info[i].ignition_delay = half_phase;
3667 if (i == EL_BLACK_ORB)
3668 element_info[i].ignition_delay = 1;
3672 /* correct non-moving belts to start moving left */
3673 for (i = 0; i < NUM_BELTS; i++)
3674 if (game.belt_dir[i] == MV_NONE)
3675 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3677 #if USE_NEW_PLAYER_ASSIGNMENTS
3678 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3679 /* choose default local player */
3680 local_player = &stored_player[0];
3682 for (i = 0; i < MAX_PLAYERS; i++)
3683 stored_player[i].connected = FALSE;
3685 local_player->connected = TRUE;
3686 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3690 for (i = 0; i < MAX_PLAYERS; i++)
3691 stored_player[i].connected = tape.player_participates[i];
3693 else if (game.team_mode && !options.network)
3695 /* try to guess locally connected team mode players (needed for correct
3696 assignment of player figures from level to locally playing players) */
3698 for (i = 0; i < MAX_PLAYERS; i++)
3699 if (setup.input[i].use_joystick ||
3700 setup.input[i].key.left != KSYM_UNDEFINED)
3701 stored_player[i].connected = TRUE;
3704 #if DEBUG_INIT_PLAYER
3707 printf("Player status after level initialization:\n");
3709 for (i = 0; i < MAX_PLAYERS; i++)
3711 struct PlayerInfo *player = &stored_player[i];
3713 printf("- player %d: present == %d, connected == %d, active == %d",
3719 if (local_player == player)
3720 printf(" (local player)");
3727 #if DEBUG_INIT_PLAYER
3729 printf("Reassigning players ...\n");
3732 /* check if any connected player was not found in playfield */
3733 for (i = 0; i < MAX_PLAYERS; i++)
3735 struct PlayerInfo *player = &stored_player[i];
3737 if (player->connected && !player->present)
3739 struct PlayerInfo *field_player = NULL;
3741 #if DEBUG_INIT_PLAYER
3743 printf("- looking for field player for player %d ...\n", i + 1);
3746 /* assign first free player found that is present in the playfield */
3748 /* first try: look for unmapped playfield player that is not connected */
3749 for (j = 0; j < MAX_PLAYERS; j++)
3750 if (field_player == NULL &&
3751 stored_player[j].present &&
3752 !stored_player[j].mapped &&
3753 !stored_player[j].connected)
3754 field_player = &stored_player[j];
3756 /* second try: look for *any* unmapped playfield player */
3757 for (j = 0; j < MAX_PLAYERS; j++)
3758 if (field_player == NULL &&
3759 stored_player[j].present &&
3760 !stored_player[j].mapped)
3761 field_player = &stored_player[j];
3763 if (field_player != NULL)
3765 int jx = field_player->jx, jy = field_player->jy;
3767 #if DEBUG_INIT_PLAYER
3769 printf("- found player %d\n", field_player->index_nr + 1);
3772 player->present = FALSE;
3773 player->active = FALSE;
3775 field_player->present = TRUE;
3776 field_player->active = TRUE;
3779 player->initial_element = field_player->initial_element;
3780 player->artwork_element = field_player->artwork_element;
3782 player->block_last_field = field_player->block_last_field;
3783 player->block_delay_adjustment = field_player->block_delay_adjustment;
3786 StorePlayer[jx][jy] = field_player->element_nr;
3788 field_player->jx = field_player->last_jx = jx;
3789 field_player->jy = field_player->last_jy = jy;
3791 if (local_player == player)
3792 local_player = field_player;
3794 map_player_action[field_player->index_nr] = i;
3796 field_player->mapped = TRUE;
3798 #if DEBUG_INIT_PLAYER
3800 printf("- map_player_action[%d] == %d\n",
3801 field_player->index_nr + 1, i + 1);
3806 if (player->connected && player->present)
3807 player->mapped = TRUE;
3810 #if DEBUG_INIT_PLAYER
3813 printf("Player status after player assignment (first stage):\n");
3815 for (i = 0; i < MAX_PLAYERS; i++)
3817 struct PlayerInfo *player = &stored_player[i];
3819 printf("- player %d: present == %d, connected == %d, active == %d",
3825 if (local_player == player)
3826 printf(" (local player)");
3835 /* check if any connected player was not found in playfield */
3836 for (i = 0; i < MAX_PLAYERS; i++)
3838 struct PlayerInfo *player = &stored_player[i];
3840 if (player->connected && !player->present)
3842 for (j = 0; j < MAX_PLAYERS; j++)
3844 struct PlayerInfo *field_player = &stored_player[j];
3845 int jx = field_player->jx, jy = field_player->jy;
3847 /* assign first free player found that is present in the playfield */
3848 if (field_player->present && !field_player->connected)
3850 player->present = TRUE;
3851 player->active = TRUE;
3853 field_player->present = FALSE;
3854 field_player->active = FALSE;
3856 player->initial_element = field_player->initial_element;
3857 player->artwork_element = field_player->artwork_element;
3859 player->block_last_field = field_player->block_last_field;
3860 player->block_delay_adjustment = field_player->block_delay_adjustment;
3862 StorePlayer[jx][jy] = player->element_nr;
3864 player->jx = player->last_jx = jx;
3865 player->jy = player->last_jy = jy;
3875 printf("::: local_player->present == %d\n", local_player->present);
3880 /* when playing a tape, eliminate all players who do not participate */
3882 #if USE_NEW_PLAYER_ASSIGNMENTS
3884 if (!game.team_mode)
3886 for (i = 0; i < MAX_PLAYERS; i++)
3888 if (stored_player[i].active &&
3889 !tape.player_participates[map_player_action[i]])
3891 struct PlayerInfo *player = &stored_player[i];
3892 int jx = player->jx, jy = player->jy;
3894 #if DEBUG_INIT_PLAYER
3896 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3899 player->active = FALSE;
3900 StorePlayer[jx][jy] = 0;
3901 Feld[jx][jy] = EL_EMPTY;
3908 for (i = 0; i < MAX_PLAYERS; i++)
3910 if (stored_player[i].active &&
3911 !tape.player_participates[i])
3913 struct PlayerInfo *player = &stored_player[i];
3914 int jx = player->jx, jy = player->jy;
3916 player->active = FALSE;
3917 StorePlayer[jx][jy] = 0;
3918 Feld[jx][jy] = EL_EMPTY;
3923 else if (!options.network && !game.team_mode) /* && !tape.playing */
3925 /* when in single player mode, eliminate all but the first active player */
3927 for (i = 0; i < MAX_PLAYERS; i++)
3929 if (stored_player[i].active)
3931 for (j = i + 1; j < MAX_PLAYERS; j++)
3933 if (stored_player[j].active)
3935 struct PlayerInfo *player = &stored_player[j];
3936 int jx = player->jx, jy = player->jy;
3938 player->active = FALSE;
3939 player->present = FALSE;
3941 StorePlayer[jx][jy] = 0;
3942 Feld[jx][jy] = EL_EMPTY;
3949 /* when recording the game, store which players take part in the game */
3952 #if USE_NEW_PLAYER_ASSIGNMENTS
3953 for (i = 0; i < MAX_PLAYERS; i++)
3954 if (stored_player[i].connected)
3955 tape.player_participates[i] = TRUE;
3957 for (i = 0; i < MAX_PLAYERS; i++)
3958 if (stored_player[i].active)
3959 tape.player_participates[i] = TRUE;
3963 #if DEBUG_INIT_PLAYER
3966 printf("Player status after player assignment (final stage):\n");
3968 for (i = 0; i < MAX_PLAYERS; i++)
3970 struct PlayerInfo *player = &stored_player[i];
3972 printf("- player %d: present == %d, connected == %d, active == %d",
3978 if (local_player == player)
3979 printf(" (local player)");
3986 if (BorderElement == EL_EMPTY)
3989 SBX_Right = lev_fieldx - SCR_FIELDX;
3991 SBY_Lower = lev_fieldy - SCR_FIELDY;
3996 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3998 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4001 if (full_lev_fieldx <= SCR_FIELDX)
4002 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4003 if (full_lev_fieldy <= SCR_FIELDY)
4004 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4006 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4008 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4011 /* if local player not found, look for custom element that might create
4012 the player (make some assumptions about the right custom element) */
4013 if (!local_player->present)
4015 int start_x = 0, start_y = 0;
4016 int found_rating = 0;
4017 int found_element = EL_UNDEFINED;
4018 int player_nr = local_player->index_nr;
4020 SCAN_PLAYFIELD(x, y)
4022 int element = Feld[x][y];
4027 if (level.use_start_element[player_nr] &&
4028 level.start_element[player_nr] == element &&
4035 found_element = element;
4038 if (!IS_CUSTOM_ELEMENT(element))
4041 if (CAN_CHANGE(element))
4043 for (i = 0; i < element_info[element].num_change_pages; i++)
4045 /* check for player created from custom element as single target */
4046 content = element_info[element].change_page[i].target_element;
4047 is_player = ELEM_IS_PLAYER(content);
4049 if (is_player && (found_rating < 3 ||
4050 (found_rating == 3 && element < found_element)))
4056 found_element = element;
4061 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4063 /* check for player created from custom element as explosion content */
4064 content = element_info[element].content.e[xx][yy];
4065 is_player = ELEM_IS_PLAYER(content);
4067 if (is_player && (found_rating < 2 ||
4068 (found_rating == 2 && element < found_element)))
4070 start_x = x + xx - 1;
4071 start_y = y + yy - 1;
4074 found_element = element;
4077 if (!CAN_CHANGE(element))
4080 for (i = 0; i < element_info[element].num_change_pages; i++)
4082 /* check for player created from custom element as extended target */
4084 element_info[element].change_page[i].target_content.e[xx][yy];
4086 is_player = ELEM_IS_PLAYER(content);
4088 if (is_player && (found_rating < 1 ||
4089 (found_rating == 1 && element < found_element)))
4091 start_x = x + xx - 1;
4092 start_y = y + yy - 1;
4095 found_element = element;
4101 scroll_x = SCROLL_POSITION_X(start_x);
4102 scroll_y = SCROLL_POSITION_Y(start_y);
4106 scroll_x = SCROLL_POSITION_X(local_player->jx);
4107 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4110 /* !!! FIX THIS (START) !!! */
4111 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4113 InitGameEngine_EM();
4115 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4117 InitGameEngine_SP();
4119 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4121 InitGameEngine_MM();
4125 DrawLevel(REDRAW_FIELD);
4128 /* after drawing the level, correct some elements */
4129 if (game.timegate_time_left == 0)
4130 CloseAllOpenTimegates();
4133 /* blit playfield from scroll buffer to normal back buffer for fading in */
4134 BlitScreenToBitmap(backbuffer);
4135 /* !!! FIX THIS (END) !!! */
4137 DrawMaskedBorder(fade_mask);
4142 // full screen redraw is required at this point in the following cases:
4143 // - special editor door undrawn when game was started from level editor
4144 // - drawing area (playfield) was changed and has to be removed completely
4145 redraw_mask = REDRAW_ALL;
4149 if (!game.restart_level)
4151 /* copy default game door content to main double buffer */
4153 /* !!! CHECK AGAIN !!! */
4154 SetPanelBackground();
4155 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4156 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4159 SetPanelBackground();
4160 SetDrawBackgroundMask(REDRAW_DOOR_1);
4162 UpdateAndDisplayGameControlValues();
4164 if (!game.restart_level)
4170 CreateGameButtons();
4172 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4173 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4174 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4179 /* copy actual game door content to door double buffer for OpenDoor() */
4180 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4182 OpenDoor(DOOR_OPEN_ALL);
4184 PlaySound(SND_GAME_STARTING);
4186 if (setup.sound_music)
4189 KeyboardAutoRepeatOffUnlessAutoplay();
4191 #if DEBUG_INIT_PLAYER
4194 printf("Player status (final):\n");
4196 for (i = 0; i < MAX_PLAYERS; i++)
4198 struct PlayerInfo *player = &stored_player[i];
4200 printf("- player %d: present == %d, connected == %d, active == %d",
4206 if (local_player == player)
4207 printf(" (local player)");
4220 if (!game.restart_level && !tape.playing)
4222 LevelStats_incPlayed(level_nr);
4224 SaveLevelSetup_SeriesInfo();
4227 game.restart_level = FALSE;
4229 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4230 InitGameActions_MM();
4232 SaveEngineSnapshotToListInitial();
4235 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4236 int actual_player_x, int actual_player_y)
4238 /* this is used for non-R'n'D game engines to update certain engine values */
4240 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4242 actual_player_x = correctLevelPosX_EM(actual_player_x);
4243 actual_player_y = correctLevelPosY_EM(actual_player_y);
4246 /* needed to determine if sounds are played within the visible screen area */
4247 scroll_x = actual_scroll_x;
4248 scroll_y = actual_scroll_y;
4250 /* needed to get player position for "follow finger" playing input method */
4251 local_player->jx = actual_player_x;
4252 local_player->jy = actual_player_y;
4255 void InitMovDir(int x, int y)
4257 int i, element = Feld[x][y];
4258 static int xy[4][2] =
4265 static int direction[3][4] =
4267 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4268 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4269 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4278 Feld[x][y] = EL_BUG;
4279 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4282 case EL_SPACESHIP_RIGHT:
4283 case EL_SPACESHIP_UP:
4284 case EL_SPACESHIP_LEFT:
4285 case EL_SPACESHIP_DOWN:
4286 Feld[x][y] = EL_SPACESHIP;
4287 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4290 case EL_BD_BUTTERFLY_RIGHT:
4291 case EL_BD_BUTTERFLY_UP:
4292 case EL_BD_BUTTERFLY_LEFT:
4293 case EL_BD_BUTTERFLY_DOWN:
4294 Feld[x][y] = EL_BD_BUTTERFLY;
4295 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4298 case EL_BD_FIREFLY_RIGHT:
4299 case EL_BD_FIREFLY_UP:
4300 case EL_BD_FIREFLY_LEFT:
4301 case EL_BD_FIREFLY_DOWN:
4302 Feld[x][y] = EL_BD_FIREFLY;
4303 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4306 case EL_PACMAN_RIGHT:
4308 case EL_PACMAN_LEFT:
4309 case EL_PACMAN_DOWN:
4310 Feld[x][y] = EL_PACMAN;
4311 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4314 case EL_YAMYAM_LEFT:
4315 case EL_YAMYAM_RIGHT:
4317 case EL_YAMYAM_DOWN:
4318 Feld[x][y] = EL_YAMYAM;
4319 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4322 case EL_SP_SNIKSNAK:
4323 MovDir[x][y] = MV_UP;
4326 case EL_SP_ELECTRON:
4327 MovDir[x][y] = MV_LEFT;
4334 Feld[x][y] = EL_MOLE;
4335 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4339 if (IS_CUSTOM_ELEMENT(element))
4341 struct ElementInfo *ei = &element_info[element];
4342 int move_direction_initial = ei->move_direction_initial;
4343 int move_pattern = ei->move_pattern;
4345 if (move_direction_initial == MV_START_PREVIOUS)
4347 if (MovDir[x][y] != MV_NONE)
4350 move_direction_initial = MV_START_AUTOMATIC;
4353 if (move_direction_initial == MV_START_RANDOM)
4354 MovDir[x][y] = 1 << RND(4);
4355 else if (move_direction_initial & MV_ANY_DIRECTION)
4356 MovDir[x][y] = move_direction_initial;
4357 else if (move_pattern == MV_ALL_DIRECTIONS ||
4358 move_pattern == MV_TURNING_LEFT ||
4359 move_pattern == MV_TURNING_RIGHT ||
4360 move_pattern == MV_TURNING_LEFT_RIGHT ||
4361 move_pattern == MV_TURNING_RIGHT_LEFT ||
4362 move_pattern == MV_TURNING_RANDOM)
4363 MovDir[x][y] = 1 << RND(4);
4364 else if (move_pattern == MV_HORIZONTAL)
4365 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4366 else if (move_pattern == MV_VERTICAL)
4367 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4368 else if (move_pattern & MV_ANY_DIRECTION)
4369 MovDir[x][y] = element_info[element].move_pattern;
4370 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4371 move_pattern == MV_ALONG_RIGHT_SIDE)
4373 /* use random direction as default start direction */
4374 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4375 MovDir[x][y] = 1 << RND(4);
4377 for (i = 0; i < NUM_DIRECTIONS; i++)
4379 int x1 = x + xy[i][0];
4380 int y1 = y + xy[i][1];
4382 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4384 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4385 MovDir[x][y] = direction[0][i];
4387 MovDir[x][y] = direction[1][i];
4396 MovDir[x][y] = 1 << RND(4);
4398 if (element != EL_BUG &&
4399 element != EL_SPACESHIP &&
4400 element != EL_BD_BUTTERFLY &&
4401 element != EL_BD_FIREFLY)
4404 for (i = 0; i < NUM_DIRECTIONS; i++)
4406 int x1 = x + xy[i][0];
4407 int y1 = y + xy[i][1];
4409 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4411 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4413 MovDir[x][y] = direction[0][i];
4416 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4417 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4419 MovDir[x][y] = direction[1][i];
4428 GfxDir[x][y] = MovDir[x][y];
4431 void InitAmoebaNr(int x, int y)
4434 int group_nr = AmoebeNachbarNr(x, y);
4438 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4440 if (AmoebaCnt[i] == 0)
4448 AmoebaNr[x][y] = group_nr;
4449 AmoebaCnt[group_nr]++;
4450 AmoebaCnt2[group_nr]++;
4453 static void PlayerWins(struct PlayerInfo *player)
4455 player->LevelSolved = TRUE;
4456 player->GameOver = TRUE;
4458 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4459 level.native_em_level->lev->score :
4460 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4463 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4464 MM_HEALTH(game_mm.laser_overload_value) :
4467 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4469 player->LevelSolved_CountingScore = player->score_final;
4470 player->LevelSolved_CountingHealth = player->health_final;
4475 static int time_count_steps;
4476 static int time, time_final;
4477 static int score, score_final;
4478 static int health, health_final;
4479 static int game_over_delay_1 = 0;
4480 static int game_over_delay_2 = 0;
4481 static int game_over_delay_3 = 0;
4482 int game_over_delay_value_1 = 50;
4483 int game_over_delay_value_2 = 25;
4484 int game_over_delay_value_3 = 50;
4486 if (!local_player->LevelSolved_GameWon)
4490 /* do not start end game actions before the player stops moving (to exit) */
4491 if (local_player->MovPos)
4494 local_player->LevelSolved_GameWon = TRUE;
4495 local_player->LevelSolved_SaveTape = tape.recording;
4496 local_player->LevelSolved_SaveScore = !tape.playing;
4500 LevelStats_incSolved(level_nr);
4502 SaveLevelSetup_SeriesInfo();
4505 if (tape.auto_play) /* tape might already be stopped here */
4506 tape.auto_play_level_solved = TRUE;
4510 game_over_delay_1 = 0;
4511 game_over_delay_2 = 0;
4512 game_over_delay_3 = game_over_delay_value_3;
4514 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4515 score = score_final = local_player->score_final;
4516 health = health_final = local_player->health_final;
4518 if (level.score[SC_TIME_BONUS] > 0)
4523 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4525 else if (game.no_time_limit && TimePlayed < 999)
4528 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4531 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4533 game_over_delay_1 = game_over_delay_value_1;
4535 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4538 score_final += health * level.score[SC_TIME_BONUS];
4540 game_over_delay_2 = game_over_delay_value_2;
4543 local_player->score_final = score_final;
4544 local_player->health_final = health_final;
4547 if (level_editor_test_game)
4550 score = score_final;
4552 local_player->LevelSolved_CountingTime = time;
4553 local_player->LevelSolved_CountingScore = score;
4555 game_panel_controls[GAME_PANEL_TIME].value = time;
4556 game_panel_controls[GAME_PANEL_SCORE].value = score;
4558 DisplayGameControlValues();
4561 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4563 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4565 /* close exit door after last player */
4566 if ((AllPlayersGone &&
4567 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4568 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4569 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4570 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4571 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4573 int element = Feld[ExitX][ExitY];
4575 Feld[ExitX][ExitY] =
4576 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4577 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4578 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4579 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4580 EL_EM_STEEL_EXIT_CLOSING);
4582 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4585 /* player disappears */
4586 DrawLevelField(ExitX, ExitY);
4589 for (i = 0; i < MAX_PLAYERS; i++)
4591 struct PlayerInfo *player = &stored_player[i];
4593 if (player->present)
4595 RemovePlayer(player);
4597 /* player disappears */
4598 DrawLevelField(player->jx, player->jy);
4603 PlaySound(SND_GAME_WINNING);
4606 if (game_over_delay_1 > 0)
4608 game_over_delay_1--;
4613 if (time != time_final)
4615 int time_to_go = ABS(time_final - time);
4616 int time_count_dir = (time < time_final ? +1 : -1);
4618 if (time_to_go < time_count_steps)
4619 time_count_steps = 1;
4621 time += time_count_steps * time_count_dir;
4622 score += time_count_steps * level.score[SC_TIME_BONUS];
4624 local_player->LevelSolved_CountingTime = time;
4625 local_player->LevelSolved_CountingScore = score;
4627 game_panel_controls[GAME_PANEL_TIME].value = time;
4628 game_panel_controls[GAME_PANEL_SCORE].value = score;
4630 DisplayGameControlValues();
4632 if (time == time_final)
4633 StopSound(SND_GAME_LEVELTIME_BONUS);
4634 else if (setup.sound_loops)
4635 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4637 PlaySound(SND_GAME_LEVELTIME_BONUS);
4642 if (game_over_delay_2 > 0)
4644 game_over_delay_2--;
4649 if (health != health_final)
4651 int health_count_dir = (health < health_final ? +1 : -1);
4653 health += health_count_dir;
4654 score += level.score[SC_TIME_BONUS];
4656 local_player->LevelSolved_CountingHealth = health;
4657 local_player->LevelSolved_CountingScore = score;
4659 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4660 game_panel_controls[GAME_PANEL_SCORE].value = score;
4662 DisplayGameControlValues();
4664 if (health == health_final)
4665 StopSound(SND_GAME_LEVELTIME_BONUS);
4666 else if (setup.sound_loops)
4667 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4669 PlaySound(SND_GAME_LEVELTIME_BONUS);
4674 local_player->LevelSolved_PanelOff = TRUE;
4676 if (game_over_delay_3 > 0)
4678 game_over_delay_3--;
4689 boolean raise_level = FALSE;
4691 local_player->LevelSolved_GameEnd = TRUE;
4693 if (!global.use_envelope_request)
4694 CloseDoor(DOOR_CLOSE_1);
4696 if (local_player->LevelSolved_SaveTape)
4698 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4701 CloseDoor(DOOR_CLOSE_ALL);
4703 if (level_editor_test_game)
4705 SetGameStatus(GAME_MODE_MAIN);
4712 if (!local_player->LevelSolved_SaveScore)
4714 SetGameStatus(GAME_MODE_MAIN);
4721 if (level_nr == leveldir_current->handicap_level)
4723 leveldir_current->handicap_level++;
4725 SaveLevelSetup_SeriesInfo();
4728 if (setup.increment_levels &&
4729 level_nr < leveldir_current->last_level)
4730 raise_level = TRUE; /* advance to next level */
4732 if ((hi_pos = NewHiScore()) >= 0)
4734 SetGameStatus(GAME_MODE_SCORES);
4736 DrawHallOfFame(hi_pos);
4746 SetGameStatus(GAME_MODE_MAIN);
4762 boolean one_score_entry_per_name = !program.many_scores_per_name;
4764 LoadScore(level_nr);
4766 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4767 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4770 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4772 if (local_player->score_final > highscore[k].Score)
4774 /* player has made it to the hall of fame */
4776 if (k < MAX_SCORE_ENTRIES - 1)
4778 int m = MAX_SCORE_ENTRIES - 1;
4780 if (one_score_entry_per_name)
4782 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4783 if (strEqual(setup.player_name, highscore[l].Name))
4786 if (m == k) /* player's new highscore overwrites his old one */
4790 for (l = m; l > k; l--)
4792 strcpy(highscore[l].Name, highscore[l - 1].Name);
4793 highscore[l].Score = highscore[l - 1].Score;
4799 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4800 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4801 highscore[k].Score = local_player->score_final;
4806 else if (one_score_entry_per_name &&
4807 !strncmp(setup.player_name, highscore[k].Name,
4808 MAX_PLAYER_NAME_LEN))
4809 break; /* player already there with a higher score */
4813 SaveScore(level_nr);
4818 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4820 int element = Feld[x][y];
4821 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4822 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4823 int horiz_move = (dx != 0);
4824 int sign = (horiz_move ? dx : dy);
4825 int step = sign * element_info[element].move_stepsize;
4827 /* special values for move stepsize for spring and things on conveyor belt */
4830 if (CAN_FALL(element) &&
4831 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4832 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4833 else if (element == EL_SPRING)
4834 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4840 inline static int getElementMoveStepsize(int x, int y)
4842 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4845 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4847 if (player->GfxAction != action || player->GfxDir != dir)
4849 player->GfxAction = action;
4850 player->GfxDir = dir;
4852 player->StepFrame = 0;
4856 static void ResetGfxFrame(int x, int y)
4858 // profiling showed that "autotest" spends 10~20% of its time in this function
4859 if (DrawingDeactivatedField())
4862 int element = Feld[x][y];
4863 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4865 if (graphic_info[graphic].anim_global_sync)
4866 GfxFrame[x][y] = FrameCounter;
4867 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4868 GfxFrame[x][y] = CustomValue[x][y];
4869 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4870 GfxFrame[x][y] = element_info[element].collect_score;
4871 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4872 GfxFrame[x][y] = ChangeDelay[x][y];
4875 static void ResetGfxAnimation(int x, int y)
4877 GfxAction[x][y] = ACTION_DEFAULT;
4878 GfxDir[x][y] = MovDir[x][y];
4881 ResetGfxFrame(x, y);
4884 static void ResetRandomAnimationValue(int x, int y)
4886 GfxRandom[x][y] = INIT_GFX_RANDOM();
4889 void InitMovingField(int x, int y, int direction)
4891 int element = Feld[x][y];
4892 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4893 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4896 boolean is_moving_before, is_moving_after;
4898 /* check if element was/is moving or being moved before/after mode change */
4899 is_moving_before = (WasJustMoving[x][y] != 0);
4900 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4902 /* reset animation only for moving elements which change direction of moving
4903 or which just started or stopped moving
4904 (else CEs with property "can move" / "not moving" are reset each frame) */
4905 if (is_moving_before != is_moving_after ||
4906 direction != MovDir[x][y])
4907 ResetGfxAnimation(x, y);
4909 MovDir[x][y] = direction;
4910 GfxDir[x][y] = direction;
4912 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4913 direction == MV_DOWN && CAN_FALL(element) ?
4914 ACTION_FALLING : ACTION_MOVING);
4916 /* this is needed for CEs with property "can move" / "not moving" */
4918 if (is_moving_after)
4920 if (Feld[newx][newy] == EL_EMPTY)
4921 Feld[newx][newy] = EL_BLOCKED;
4923 MovDir[newx][newy] = MovDir[x][y];
4925 CustomValue[newx][newy] = CustomValue[x][y];
4927 GfxFrame[newx][newy] = GfxFrame[x][y];
4928 GfxRandom[newx][newy] = GfxRandom[x][y];
4929 GfxAction[newx][newy] = GfxAction[x][y];
4930 GfxDir[newx][newy] = GfxDir[x][y];
4934 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4936 int direction = MovDir[x][y];
4937 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4938 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4944 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4946 int oldx = x, oldy = y;
4947 int direction = MovDir[x][y];
4949 if (direction == MV_LEFT)
4951 else if (direction == MV_RIGHT)
4953 else if (direction == MV_UP)
4955 else if (direction == MV_DOWN)
4958 *comes_from_x = oldx;
4959 *comes_from_y = oldy;
4962 int MovingOrBlocked2Element(int x, int y)
4964 int element = Feld[x][y];
4966 if (element == EL_BLOCKED)
4970 Blocked2Moving(x, y, &oldx, &oldy);
4971 return Feld[oldx][oldy];
4977 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4979 /* like MovingOrBlocked2Element(), but if element is moving
4980 and (x,y) is the field the moving element is just leaving,
4981 return EL_BLOCKED instead of the element value */
4982 int element = Feld[x][y];
4984 if (IS_MOVING(x, y))
4986 if (element == EL_BLOCKED)
4990 Blocked2Moving(x, y, &oldx, &oldy);
4991 return Feld[oldx][oldy];
5000 static void RemoveField(int x, int y)
5002 Feld[x][y] = EL_EMPTY;
5008 CustomValue[x][y] = 0;
5011 ChangeDelay[x][y] = 0;
5012 ChangePage[x][y] = -1;
5013 Pushed[x][y] = FALSE;
5015 GfxElement[x][y] = EL_UNDEFINED;
5016 GfxAction[x][y] = ACTION_DEFAULT;
5017 GfxDir[x][y] = MV_NONE;
5020 void RemoveMovingField(int x, int y)
5022 int oldx = x, oldy = y, newx = x, newy = y;
5023 int element = Feld[x][y];
5024 int next_element = EL_UNDEFINED;
5026 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5029 if (IS_MOVING(x, y))
5031 Moving2Blocked(x, y, &newx, &newy);
5033 if (Feld[newx][newy] != EL_BLOCKED)
5035 /* element is moving, but target field is not free (blocked), but
5036 already occupied by something different (example: acid pool);
5037 in this case, only remove the moving field, but not the target */
5039 RemoveField(oldx, oldy);
5041 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5043 TEST_DrawLevelField(oldx, oldy);
5048 else if (element == EL_BLOCKED)
5050 Blocked2Moving(x, y, &oldx, &oldy);
5051 if (!IS_MOVING(oldx, oldy))
5055 if (element == EL_BLOCKED &&
5056 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5057 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5058 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5059 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5060 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5061 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5062 next_element = get_next_element(Feld[oldx][oldy]);
5064 RemoveField(oldx, oldy);
5065 RemoveField(newx, newy);
5067 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5069 if (next_element != EL_UNDEFINED)
5070 Feld[oldx][oldy] = next_element;
5072 TEST_DrawLevelField(oldx, oldy);
5073 TEST_DrawLevelField(newx, newy);
5076 void DrawDynamite(int x, int y)
5078 int sx = SCREENX(x), sy = SCREENY(y);
5079 int graphic = el2img(Feld[x][y]);
5082 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5085 if (IS_WALKABLE_INSIDE(Back[x][y]))
5089 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5090 else if (Store[x][y])
5091 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5093 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5095 if (Back[x][y] || Store[x][y])
5096 DrawGraphicThruMask(sx, sy, graphic, frame);
5098 DrawGraphic(sx, sy, graphic, frame);
5101 void CheckDynamite(int x, int y)
5103 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5107 if (MovDelay[x][y] != 0)
5110 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5116 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5121 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5123 boolean num_checked_players = 0;
5126 for (i = 0; i < MAX_PLAYERS; i++)
5128 if (stored_player[i].active)
5130 int sx = stored_player[i].jx;
5131 int sy = stored_player[i].jy;
5133 if (num_checked_players == 0)
5140 *sx1 = MIN(*sx1, sx);
5141 *sy1 = MIN(*sy1, sy);
5142 *sx2 = MAX(*sx2, sx);
5143 *sy2 = MAX(*sy2, sy);
5146 num_checked_players++;
5151 static boolean checkIfAllPlayersFitToScreen_RND()
5153 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5155 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5157 return (sx2 - sx1 < SCR_FIELDX &&
5158 sy2 - sy1 < SCR_FIELDY);
5161 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5163 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5165 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5167 *sx = (sx1 + sx2) / 2;
5168 *sy = (sy1 + sy2) / 2;
5171 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5172 boolean center_screen, boolean quick_relocation)
5174 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5175 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5176 boolean no_delay = (tape.warp_forward);
5177 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5178 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5179 int new_scroll_x, new_scroll_y;
5181 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5183 /* case 1: quick relocation inside visible screen (without scrolling) */
5190 if (!level.shifted_relocation || center_screen)
5192 /* relocation _with_ centering of screen */
5194 new_scroll_x = SCROLL_POSITION_X(x);
5195 new_scroll_y = SCROLL_POSITION_Y(y);
5199 /* relocation _without_ centering of screen */
5201 int center_scroll_x = SCROLL_POSITION_X(old_x);
5202 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5203 int offset_x = x + (scroll_x - center_scroll_x);
5204 int offset_y = y + (scroll_y - center_scroll_y);
5206 /* for new screen position, apply previous offset to center position */
5207 new_scroll_x = SCROLL_POSITION_X(offset_x);
5208 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5211 if (quick_relocation)
5213 /* case 2: quick relocation (redraw without visible scrolling) */
5215 scroll_x = new_scroll_x;
5216 scroll_y = new_scroll_y;
5223 /* case 3: visible relocation (with scrolling to new position) */
5225 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5227 SetVideoFrameDelay(wait_delay_value);
5229 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5232 int fx = FX, fy = FY;
5234 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5235 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5237 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5243 fx += dx * TILEX / 2;
5244 fy += dy * TILEY / 2;
5246 ScrollLevel(dx, dy);
5249 /* scroll in two steps of half tile size to make things smoother */
5250 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5252 /* scroll second step to align at full tile size */
5253 BlitScreenToBitmap(window);
5259 SetVideoFrameDelay(frame_delay_value_old);
5262 void RelocatePlayer(int jx, int jy, int el_player_raw)
5264 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5265 int player_nr = GET_PLAYER_NR(el_player);
5266 struct PlayerInfo *player = &stored_player[player_nr];
5267 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5268 boolean no_delay = (tape.warp_forward);
5269 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5270 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5271 int old_jx = player->jx;
5272 int old_jy = player->jy;
5273 int old_element = Feld[old_jx][old_jy];
5274 int element = Feld[jx][jy];
5275 boolean player_relocated = (old_jx != jx || old_jy != jy);
5277 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5278 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5279 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5280 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5281 int leave_side_horiz = move_dir_horiz;
5282 int leave_side_vert = move_dir_vert;
5283 int enter_side = enter_side_horiz | enter_side_vert;
5284 int leave_side = leave_side_horiz | leave_side_vert;
5286 if (player->GameOver) /* do not reanimate dead player */
5289 if (!player_relocated) /* no need to relocate the player */
5292 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5294 RemoveField(jx, jy); /* temporarily remove newly placed player */
5295 DrawLevelField(jx, jy);
5298 if (player->present)
5300 while (player->MovPos)
5302 ScrollPlayer(player, SCROLL_GO_ON);
5303 ScrollScreen(NULL, SCROLL_GO_ON);
5305 AdvanceFrameAndPlayerCounters(player->index_nr);
5309 BackToFront_WithFrameDelay(wait_delay_value);
5312 DrawPlayer(player); /* needed here only to cleanup last field */
5313 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5315 player->is_moving = FALSE;
5318 if (IS_CUSTOM_ELEMENT(old_element))
5319 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5321 player->index_bit, leave_side);
5323 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5325 player->index_bit, leave_side);
5327 Feld[jx][jy] = el_player;
5328 InitPlayerField(jx, jy, el_player, TRUE);
5330 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5331 possible that the relocation target field did not contain a player element,
5332 but a walkable element, to which the new player was relocated -- in this
5333 case, restore that (already initialized!) element on the player field */
5334 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5336 Feld[jx][jy] = element; /* restore previously existing element */
5339 /* only visually relocate centered player */
5340 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5341 FALSE, level.instant_relocation);
5343 TestIfPlayerTouchesBadThing(jx, jy);
5344 TestIfPlayerTouchesCustomElement(jx, jy);
5346 if (IS_CUSTOM_ELEMENT(element))
5347 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5348 player->index_bit, enter_side);
5350 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5351 player->index_bit, enter_side);
5353 if (player->is_switching)
5355 /* ensure that relocation while still switching an element does not cause
5356 a new element to be treated as also switched directly after relocation
5357 (this is important for teleporter switches that teleport the player to
5358 a place where another teleporter switch is in the same direction, which
5359 would then incorrectly be treated as immediately switched before the
5360 direction key that caused the switch was released) */
5362 player->switch_x += jx - old_jx;
5363 player->switch_y += jy - old_jy;
5367 void Explode(int ex, int ey, int phase, int mode)
5373 /* !!! eliminate this variable !!! */
5374 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5376 if (game.explosions_delayed)
5378 ExplodeField[ex][ey] = mode;
5382 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5384 int center_element = Feld[ex][ey];
5385 int artwork_element, explosion_element; /* set these values later */
5387 /* remove things displayed in background while burning dynamite */
5388 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5391 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5393 /* put moving element to center field (and let it explode there) */
5394 center_element = MovingOrBlocked2Element(ex, ey);
5395 RemoveMovingField(ex, ey);
5396 Feld[ex][ey] = center_element;
5399 /* now "center_element" is finally determined -- set related values now */
5400 artwork_element = center_element; /* for custom player artwork */
5401 explosion_element = center_element; /* for custom player artwork */
5403 if (IS_PLAYER(ex, ey))
5405 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5407 artwork_element = stored_player[player_nr].artwork_element;
5409 if (level.use_explosion_element[player_nr])
5411 explosion_element = level.explosion_element[player_nr];
5412 artwork_element = explosion_element;
5416 if (mode == EX_TYPE_NORMAL ||
5417 mode == EX_TYPE_CENTER ||
5418 mode == EX_TYPE_CROSS)
5419 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5421 last_phase = element_info[explosion_element].explosion_delay + 1;
5423 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5425 int xx = x - ex + 1;
5426 int yy = y - ey + 1;
5429 if (!IN_LEV_FIELD(x, y) ||
5430 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5431 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5434 element = Feld[x][y];
5436 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5438 element = MovingOrBlocked2Element(x, y);
5440 if (!IS_EXPLOSION_PROOF(element))
5441 RemoveMovingField(x, y);
5444 /* indestructible elements can only explode in center (but not flames) */
5445 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5446 mode == EX_TYPE_BORDER)) ||
5447 element == EL_FLAMES)
5450 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5451 behaviour, for example when touching a yamyam that explodes to rocks
5452 with active deadly shield, a rock is created under the player !!! */
5453 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5455 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5456 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5457 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5459 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5462 if (IS_ACTIVE_BOMB(element))
5464 /* re-activate things under the bomb like gate or penguin */
5465 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5472 /* save walkable background elements while explosion on same tile */
5473 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5474 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5475 Back[x][y] = element;
5477 /* ignite explodable elements reached by other explosion */
5478 if (element == EL_EXPLOSION)
5479 element = Store2[x][y];
5481 if (AmoebaNr[x][y] &&
5482 (element == EL_AMOEBA_FULL ||
5483 element == EL_BD_AMOEBA ||
5484 element == EL_AMOEBA_GROWING))
5486 AmoebaCnt[AmoebaNr[x][y]]--;
5487 AmoebaCnt2[AmoebaNr[x][y]]--;
5492 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5494 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5496 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5498 if (PLAYERINFO(ex, ey)->use_murphy)
5499 Store[x][y] = EL_EMPTY;
5502 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5503 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5504 else if (ELEM_IS_PLAYER(center_element))
5505 Store[x][y] = EL_EMPTY;
5506 else if (center_element == EL_YAMYAM)
5507 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5508 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5509 Store[x][y] = element_info[center_element].content.e[xx][yy];
5511 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5512 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5513 otherwise) -- FIX THIS !!! */
5514 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5515 Store[x][y] = element_info[element].content.e[1][1];
5517 else if (!CAN_EXPLODE(element))
5518 Store[x][y] = element_info[element].content.e[1][1];
5521 Store[x][y] = EL_EMPTY;
5523 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5524 center_element == EL_AMOEBA_TO_DIAMOND)
5525 Store2[x][y] = element;
5527 Feld[x][y] = EL_EXPLOSION;
5528 GfxElement[x][y] = artwork_element;
5530 ExplodePhase[x][y] = 1;
5531 ExplodeDelay[x][y] = last_phase;
5536 if (center_element == EL_YAMYAM)
5537 game.yamyam_content_nr =
5538 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5550 GfxFrame[x][y] = 0; /* restart explosion animation */
5552 last_phase = ExplodeDelay[x][y];
5554 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5556 /* this can happen if the player leaves an explosion just in time */
5557 if (GfxElement[x][y] == EL_UNDEFINED)
5558 GfxElement[x][y] = EL_EMPTY;
5560 border_element = Store2[x][y];
5561 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5562 border_element = StorePlayer[x][y];
5564 if (phase == element_info[border_element].ignition_delay ||
5565 phase == last_phase)
5567 boolean border_explosion = FALSE;
5569 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5570 !PLAYER_EXPLOSION_PROTECTED(x, y))
5572 KillPlayerUnlessExplosionProtected(x, y);
5573 border_explosion = TRUE;
5575 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5577 Feld[x][y] = Store2[x][y];
5580 border_explosion = TRUE;
5582 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5584 AmoebeUmwandeln(x, y);
5586 border_explosion = TRUE;
5589 /* if an element just explodes due to another explosion (chain-reaction),
5590 do not immediately end the new explosion when it was the last frame of
5591 the explosion (as it would be done in the following "if"-statement!) */
5592 if (border_explosion && phase == last_phase)
5596 if (phase == last_phase)
5600 element = Feld[x][y] = Store[x][y];
5601 Store[x][y] = Store2[x][y] = 0;
5602 GfxElement[x][y] = EL_UNDEFINED;
5604 /* player can escape from explosions and might therefore be still alive */
5605 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5606 element <= EL_PLAYER_IS_EXPLODING_4)
5608 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5609 int explosion_element = EL_PLAYER_1 + player_nr;
5610 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5611 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5613 if (level.use_explosion_element[player_nr])
5614 explosion_element = level.explosion_element[player_nr];
5616 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5617 element_info[explosion_element].content.e[xx][yy]);
5620 /* restore probably existing indestructible background element */
5621 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5622 element = Feld[x][y] = Back[x][y];
5625 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5626 GfxDir[x][y] = MV_NONE;
5627 ChangeDelay[x][y] = 0;
5628 ChangePage[x][y] = -1;
5630 CustomValue[x][y] = 0;
5632 InitField_WithBug2(x, y, FALSE);
5634 TEST_DrawLevelField(x, y);
5636 TestIfElementTouchesCustomElement(x, y);
5638 if (GFX_CRUMBLED(element))
5639 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5641 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5642 StorePlayer[x][y] = 0;
5644 if (ELEM_IS_PLAYER(element))
5645 RelocatePlayer(x, y, element);
5647 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5649 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5650 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5653 TEST_DrawLevelFieldCrumbled(x, y);
5655 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5657 DrawLevelElement(x, y, Back[x][y]);
5658 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5660 else if (IS_WALKABLE_UNDER(Back[x][y]))
5662 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5663 DrawLevelElementThruMask(x, y, Back[x][y]);
5665 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5666 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5670 void DynaExplode(int ex, int ey)
5673 int dynabomb_element = Feld[ex][ey];
5674 int dynabomb_size = 1;
5675 boolean dynabomb_xl = FALSE;
5676 struct PlayerInfo *player;
5677 static int xy[4][2] =
5685 if (IS_ACTIVE_BOMB(dynabomb_element))
5687 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5688 dynabomb_size = player->dynabomb_size;
5689 dynabomb_xl = player->dynabomb_xl;
5690 player->dynabombs_left++;
5693 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5695 for (i = 0; i < NUM_DIRECTIONS; i++)
5697 for (j = 1; j <= dynabomb_size; j++)
5699 int x = ex + j * xy[i][0];
5700 int y = ey + j * xy[i][1];
5703 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5706 element = Feld[x][y];
5708 /* do not restart explosions of fields with active bombs */
5709 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5712 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5714 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5715 !IS_DIGGABLE(element) && !dynabomb_xl)
5721 void Bang(int x, int y)
5723 int element = MovingOrBlocked2Element(x, y);
5724 int explosion_type = EX_TYPE_NORMAL;
5726 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5728 struct PlayerInfo *player = PLAYERINFO(x, y);
5730 element = Feld[x][y] = player->initial_element;
5732 if (level.use_explosion_element[player->index_nr])
5734 int explosion_element = level.explosion_element[player->index_nr];
5736 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5737 explosion_type = EX_TYPE_CROSS;
5738 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5739 explosion_type = EX_TYPE_CENTER;
5747 case EL_BD_BUTTERFLY:
5750 case EL_DARK_YAMYAM:
5754 RaiseScoreElement(element);
5757 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5758 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5759 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5760 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5761 case EL_DYNABOMB_INCREASE_NUMBER:
5762 case EL_DYNABOMB_INCREASE_SIZE:
5763 case EL_DYNABOMB_INCREASE_POWER:
5764 explosion_type = EX_TYPE_DYNA;
5767 case EL_DC_LANDMINE:
5768 explosion_type = EX_TYPE_CENTER;
5773 case EL_LAMP_ACTIVE:
5774 case EL_AMOEBA_TO_DIAMOND:
5775 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5776 explosion_type = EX_TYPE_CENTER;
5780 if (element_info[element].explosion_type == EXPLODES_CROSS)
5781 explosion_type = EX_TYPE_CROSS;
5782 else if (element_info[element].explosion_type == EXPLODES_1X1)
5783 explosion_type = EX_TYPE_CENTER;
5787 if (explosion_type == EX_TYPE_DYNA)
5790 Explode(x, y, EX_PHASE_START, explosion_type);
5792 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5795 void SplashAcid(int x, int y)
5797 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5798 (!IN_LEV_FIELD(x - 1, y - 2) ||
5799 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5800 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5802 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5803 (!IN_LEV_FIELD(x + 1, y - 2) ||
5804 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5805 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5807 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5810 static void InitBeltMovement()
5812 static int belt_base_element[4] =
5814 EL_CONVEYOR_BELT_1_LEFT,
5815 EL_CONVEYOR_BELT_2_LEFT,
5816 EL_CONVEYOR_BELT_3_LEFT,
5817 EL_CONVEYOR_BELT_4_LEFT
5819 static int belt_base_active_element[4] =
5821 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5822 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5823 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5824 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5829 /* set frame order for belt animation graphic according to belt direction */
5830 for (i = 0; i < NUM_BELTS; i++)
5834 for (j = 0; j < NUM_BELT_PARTS; j++)
5836 int element = belt_base_active_element[belt_nr] + j;
5837 int graphic_1 = el2img(element);
5838 int graphic_2 = el2panelimg(element);
5840 if (game.belt_dir[i] == MV_LEFT)
5842 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5843 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5847 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5848 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5853 SCAN_PLAYFIELD(x, y)
5855 int element = Feld[x][y];
5857 for (i = 0; i < NUM_BELTS; i++)
5859 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5861 int e_belt_nr = getBeltNrFromBeltElement(element);
5864 if (e_belt_nr == belt_nr)
5866 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5868 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5875 static void ToggleBeltSwitch(int x, int y)
5877 static int belt_base_element[4] =
5879 EL_CONVEYOR_BELT_1_LEFT,
5880 EL_CONVEYOR_BELT_2_LEFT,
5881 EL_CONVEYOR_BELT_3_LEFT,
5882 EL_CONVEYOR_BELT_4_LEFT
5884 static int belt_base_active_element[4] =
5886 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5887 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5888 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5889 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5891 static int belt_base_switch_element[4] =
5893 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5894 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5895 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5896 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5898 static int belt_move_dir[4] =
5906 int element = Feld[x][y];
5907 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5908 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5909 int belt_dir = belt_move_dir[belt_dir_nr];
5912 if (!IS_BELT_SWITCH(element))
5915 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5916 game.belt_dir[belt_nr] = belt_dir;
5918 if (belt_dir_nr == 3)
5921 /* set frame order for belt animation graphic according to belt direction */
5922 for (i = 0; i < NUM_BELT_PARTS; i++)
5924 int element = belt_base_active_element[belt_nr] + i;
5925 int graphic_1 = el2img(element);
5926 int graphic_2 = el2panelimg(element);
5928 if (belt_dir == MV_LEFT)
5930 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5931 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5935 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5936 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5940 SCAN_PLAYFIELD(xx, yy)
5942 int element = Feld[xx][yy];
5944 if (IS_BELT_SWITCH(element))
5946 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5948 if (e_belt_nr == belt_nr)
5950 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5951 TEST_DrawLevelField(xx, yy);
5954 else if (IS_BELT(element) && belt_dir != MV_NONE)
5956 int e_belt_nr = getBeltNrFromBeltElement(element);
5958 if (e_belt_nr == belt_nr)
5960 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5962 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5963 TEST_DrawLevelField(xx, yy);
5966 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5968 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5970 if (e_belt_nr == belt_nr)
5972 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5974 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5975 TEST_DrawLevelField(xx, yy);
5981 static void ToggleSwitchgateSwitch(int x, int y)
5985 game.switchgate_pos = !game.switchgate_pos;
5987 SCAN_PLAYFIELD(xx, yy)
5989 int element = Feld[xx][yy];
5991 if (element == EL_SWITCHGATE_SWITCH_UP)
5993 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5994 TEST_DrawLevelField(xx, yy);
5996 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5998 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5999 TEST_DrawLevelField(xx, yy);
6001 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6003 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6004 TEST_DrawLevelField(xx, yy);
6006 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6008 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6009 TEST_DrawLevelField(xx, yy);
6011 else if (element == EL_SWITCHGATE_OPEN ||
6012 element == EL_SWITCHGATE_OPENING)
6014 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6016 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6018 else if (element == EL_SWITCHGATE_CLOSED ||
6019 element == EL_SWITCHGATE_CLOSING)
6021 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6023 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6028 static int getInvisibleActiveFromInvisibleElement(int element)
6030 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6031 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6032 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6036 static int getInvisibleFromInvisibleActiveElement(int element)
6038 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6039 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6040 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6044 static void RedrawAllLightSwitchesAndInvisibleElements()
6048 SCAN_PLAYFIELD(x, y)
6050 int element = Feld[x][y];
6052 if (element == EL_LIGHT_SWITCH &&
6053 game.light_time_left > 0)
6055 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6056 TEST_DrawLevelField(x, y);
6058 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6059 game.light_time_left == 0)
6061 Feld[x][y] = EL_LIGHT_SWITCH;
6062 TEST_DrawLevelField(x, y);
6064 else if (element == EL_EMC_DRIPPER &&
6065 game.light_time_left > 0)
6067 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6068 TEST_DrawLevelField(x, y);
6070 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6071 game.light_time_left == 0)
6073 Feld[x][y] = EL_EMC_DRIPPER;
6074 TEST_DrawLevelField(x, y);
6076 else if (element == EL_INVISIBLE_STEELWALL ||
6077 element == EL_INVISIBLE_WALL ||
6078 element == EL_INVISIBLE_SAND)
6080 if (game.light_time_left > 0)
6081 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6083 TEST_DrawLevelField(x, y);
6085 /* uncrumble neighbour fields, if needed */
6086 if (element == EL_INVISIBLE_SAND)
6087 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6089 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6090 element == EL_INVISIBLE_WALL_ACTIVE ||
6091 element == EL_INVISIBLE_SAND_ACTIVE)
6093 if (game.light_time_left == 0)
6094 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6096 TEST_DrawLevelField(x, y);
6098 /* re-crumble neighbour fields, if needed */
6099 if (element == EL_INVISIBLE_SAND)
6100 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6105 static void RedrawAllInvisibleElementsForLenses()
6109 SCAN_PLAYFIELD(x, y)
6111 int element = Feld[x][y];
6113 if (element == EL_EMC_DRIPPER &&
6114 game.lenses_time_left > 0)
6116 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6117 TEST_DrawLevelField(x, y);
6119 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6120 game.lenses_time_left == 0)
6122 Feld[x][y] = EL_EMC_DRIPPER;
6123 TEST_DrawLevelField(x, y);
6125 else if (element == EL_INVISIBLE_STEELWALL ||
6126 element == EL_INVISIBLE_WALL ||
6127 element == EL_INVISIBLE_SAND)
6129 if (game.lenses_time_left > 0)
6130 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6132 TEST_DrawLevelField(x, y);
6134 /* uncrumble neighbour fields, if needed */
6135 if (element == EL_INVISIBLE_SAND)
6136 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6138 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6139 element == EL_INVISIBLE_WALL_ACTIVE ||
6140 element == EL_INVISIBLE_SAND_ACTIVE)
6142 if (game.lenses_time_left == 0)
6143 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6145 TEST_DrawLevelField(x, y);
6147 /* re-crumble neighbour fields, if needed */
6148 if (element == EL_INVISIBLE_SAND)
6149 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6154 static void RedrawAllInvisibleElementsForMagnifier()
6158 SCAN_PLAYFIELD(x, y)
6160 int element = Feld[x][y];
6162 if (element == EL_EMC_FAKE_GRASS &&
6163 game.magnify_time_left > 0)
6165 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6166 TEST_DrawLevelField(x, y);
6168 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6169 game.magnify_time_left == 0)
6171 Feld[x][y] = EL_EMC_FAKE_GRASS;
6172 TEST_DrawLevelField(x, y);
6174 else if (IS_GATE_GRAY(element) &&
6175 game.magnify_time_left > 0)
6177 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6178 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6179 IS_EM_GATE_GRAY(element) ?
6180 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6181 IS_EMC_GATE_GRAY(element) ?
6182 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6183 IS_DC_GATE_GRAY(element) ?
6184 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6186 TEST_DrawLevelField(x, y);
6188 else if (IS_GATE_GRAY_ACTIVE(element) &&
6189 game.magnify_time_left == 0)
6191 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6192 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6193 IS_EM_GATE_GRAY_ACTIVE(element) ?
6194 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6195 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6196 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6197 IS_DC_GATE_GRAY_ACTIVE(element) ?
6198 EL_DC_GATE_WHITE_GRAY :
6200 TEST_DrawLevelField(x, y);
6205 static void ToggleLightSwitch(int x, int y)
6207 int element = Feld[x][y];
6209 game.light_time_left =
6210 (element == EL_LIGHT_SWITCH ?
6211 level.time_light * FRAMES_PER_SECOND : 0);
6213 RedrawAllLightSwitchesAndInvisibleElements();
6216 static void ActivateTimegateSwitch(int x, int y)
6220 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6222 SCAN_PLAYFIELD(xx, yy)
6224 int element = Feld[xx][yy];
6226 if (element == EL_TIMEGATE_CLOSED ||
6227 element == EL_TIMEGATE_CLOSING)
6229 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6230 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6234 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6236 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6237 TEST_DrawLevelField(xx, yy);
6243 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6244 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6247 void Impact(int x, int y)
6249 boolean last_line = (y == lev_fieldy - 1);
6250 boolean object_hit = FALSE;
6251 boolean impact = (last_line || object_hit);
6252 int element = Feld[x][y];
6253 int smashed = EL_STEELWALL;
6255 if (!last_line) /* check if element below was hit */
6257 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6260 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6261 MovDir[x][y + 1] != MV_DOWN ||
6262 MovPos[x][y + 1] <= TILEY / 2));
6264 /* do not smash moving elements that left the smashed field in time */
6265 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6266 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6269 #if USE_QUICKSAND_IMPACT_BUGFIX
6270 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6272 RemoveMovingField(x, y + 1);
6273 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6274 Feld[x][y + 2] = EL_ROCK;
6275 TEST_DrawLevelField(x, y + 2);
6280 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6282 RemoveMovingField(x, y + 1);
6283 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6284 Feld[x][y + 2] = EL_ROCK;
6285 TEST_DrawLevelField(x, y + 2);
6292 smashed = MovingOrBlocked2Element(x, y + 1);
6294 impact = (last_line || object_hit);
6297 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6299 SplashAcid(x, y + 1);
6303 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6304 /* only reset graphic animation if graphic really changes after impact */
6306 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6308 ResetGfxAnimation(x, y);
6309 TEST_DrawLevelField(x, y);
6312 if (impact && CAN_EXPLODE_IMPACT(element))
6317 else if (impact && element == EL_PEARL &&
6318 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6320 ResetGfxAnimation(x, y);
6322 Feld[x][y] = EL_PEARL_BREAKING;
6323 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6326 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6328 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6333 if (impact && element == EL_AMOEBA_DROP)
6335 if (object_hit && IS_PLAYER(x, y + 1))
6336 KillPlayerUnlessEnemyProtected(x, y + 1);
6337 else if (object_hit && smashed == EL_PENGUIN)
6341 Feld[x][y] = EL_AMOEBA_GROWING;
6342 Store[x][y] = EL_AMOEBA_WET;
6344 ResetRandomAnimationValue(x, y);
6349 if (object_hit) /* check which object was hit */
6351 if ((CAN_PASS_MAGIC_WALL(element) &&
6352 (smashed == EL_MAGIC_WALL ||
6353 smashed == EL_BD_MAGIC_WALL)) ||
6354 (CAN_PASS_DC_MAGIC_WALL(element) &&
6355 smashed == EL_DC_MAGIC_WALL))
6358 int activated_magic_wall =
6359 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6360 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6361 EL_DC_MAGIC_WALL_ACTIVE);
6363 /* activate magic wall / mill */
6364 SCAN_PLAYFIELD(xx, yy)
6366 if (Feld[xx][yy] == smashed)
6367 Feld[xx][yy] = activated_magic_wall;
6370 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6371 game.magic_wall_active = TRUE;
6373 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6374 SND_MAGIC_WALL_ACTIVATING :
6375 smashed == EL_BD_MAGIC_WALL ?
6376 SND_BD_MAGIC_WALL_ACTIVATING :
6377 SND_DC_MAGIC_WALL_ACTIVATING));
6380 if (IS_PLAYER(x, y + 1))
6382 if (CAN_SMASH_PLAYER(element))
6384 KillPlayerUnlessEnemyProtected(x, y + 1);
6388 else if (smashed == EL_PENGUIN)
6390 if (CAN_SMASH_PLAYER(element))
6396 else if (element == EL_BD_DIAMOND)
6398 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6404 else if (((element == EL_SP_INFOTRON ||
6405 element == EL_SP_ZONK) &&
6406 (smashed == EL_SP_SNIKSNAK ||
6407 smashed == EL_SP_ELECTRON ||
6408 smashed == EL_SP_DISK_ORANGE)) ||
6409 (element == EL_SP_INFOTRON &&
6410 smashed == EL_SP_DISK_YELLOW))
6415 else if (CAN_SMASH_EVERYTHING(element))
6417 if (IS_CLASSIC_ENEMY(smashed) ||
6418 CAN_EXPLODE_SMASHED(smashed))
6423 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6425 if (smashed == EL_LAMP ||
6426 smashed == EL_LAMP_ACTIVE)
6431 else if (smashed == EL_NUT)
6433 Feld[x][y + 1] = EL_NUT_BREAKING;
6434 PlayLevelSound(x, y, SND_NUT_BREAKING);
6435 RaiseScoreElement(EL_NUT);
6438 else if (smashed == EL_PEARL)
6440 ResetGfxAnimation(x, y);
6442 Feld[x][y + 1] = EL_PEARL_BREAKING;
6443 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6446 else if (smashed == EL_DIAMOND)
6448 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6449 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6452 else if (IS_BELT_SWITCH(smashed))
6454 ToggleBeltSwitch(x, y + 1);
6456 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6457 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6458 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6459 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6461 ToggleSwitchgateSwitch(x, y + 1);
6463 else if (smashed == EL_LIGHT_SWITCH ||
6464 smashed == EL_LIGHT_SWITCH_ACTIVE)
6466 ToggleLightSwitch(x, y + 1);
6470 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6472 CheckElementChangeBySide(x, y + 1, smashed, element,
6473 CE_SWITCHED, CH_SIDE_TOP);
6474 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6480 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6485 /* play sound of magic wall / mill */
6487 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6488 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6489 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6491 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6492 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6493 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6494 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6495 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6496 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6501 /* play sound of object that hits the ground */
6502 if (last_line || object_hit)
6503 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6506 inline static void TurnRoundExt(int x, int y)
6518 { 0, 0 }, { 0, 0 }, { 0, 0 },
6523 int left, right, back;
6527 { MV_DOWN, MV_UP, MV_RIGHT },
6528 { MV_UP, MV_DOWN, MV_LEFT },
6530 { MV_LEFT, MV_RIGHT, MV_DOWN },
6534 { MV_RIGHT, MV_LEFT, MV_UP }
6537 int element = Feld[x][y];
6538 int move_pattern = element_info[element].move_pattern;
6540 int old_move_dir = MovDir[x][y];
6541 int left_dir = turn[old_move_dir].left;
6542 int right_dir = turn[old_move_dir].right;
6543 int back_dir = turn[old_move_dir].back;
6545 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6546 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6547 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6548 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6550 int left_x = x + left_dx, left_y = y + left_dy;
6551 int right_x = x + right_dx, right_y = y + right_dy;
6552 int move_x = x + move_dx, move_y = y + move_dy;
6556 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6558 TestIfBadThingTouchesOtherBadThing(x, y);
6560 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6561 MovDir[x][y] = right_dir;
6562 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6563 MovDir[x][y] = left_dir;
6565 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6567 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6570 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6572 TestIfBadThingTouchesOtherBadThing(x, y);
6574 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6575 MovDir[x][y] = left_dir;
6576 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6577 MovDir[x][y] = right_dir;
6579 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6581 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6584 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6586 TestIfBadThingTouchesOtherBadThing(x, y);
6588 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6589 MovDir[x][y] = left_dir;
6590 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6591 MovDir[x][y] = right_dir;
6593 if (MovDir[x][y] != old_move_dir)
6596 else if (element == EL_YAMYAM)
6598 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6599 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6601 if (can_turn_left && can_turn_right)
6602 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6603 else if (can_turn_left)
6604 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6605 else if (can_turn_right)
6606 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6608 MovDir[x][y] = back_dir;
6610 MovDelay[x][y] = 16 + 16 * RND(3);
6612 else if (element == EL_DARK_YAMYAM)
6614 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6616 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6619 if (can_turn_left && can_turn_right)
6620 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6621 else if (can_turn_left)
6622 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6623 else if (can_turn_right)
6624 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6626 MovDir[x][y] = back_dir;
6628 MovDelay[x][y] = 16 + 16 * RND(3);
6630 else if (element == EL_PACMAN)
6632 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6633 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6635 if (can_turn_left && can_turn_right)
6636 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6637 else if (can_turn_left)
6638 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6639 else if (can_turn_right)
6640 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6642 MovDir[x][y] = back_dir;
6644 MovDelay[x][y] = 6 + RND(40);
6646 else if (element == EL_PIG)
6648 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6649 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6650 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6651 boolean should_turn_left, should_turn_right, should_move_on;
6653 int rnd = RND(rnd_value);
6655 should_turn_left = (can_turn_left &&
6657 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6658 y + back_dy + left_dy)));
6659 should_turn_right = (can_turn_right &&
6661 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6662 y + back_dy + right_dy)));
6663 should_move_on = (can_move_on &&
6666 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6667 y + move_dy + left_dy) ||
6668 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6669 y + move_dy + right_dy)));
6671 if (should_turn_left || should_turn_right || should_move_on)
6673 if (should_turn_left && should_turn_right && should_move_on)
6674 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6675 rnd < 2 * rnd_value / 3 ? right_dir :
6677 else if (should_turn_left && should_turn_right)
6678 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6679 else if (should_turn_left && should_move_on)
6680 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6681 else if (should_turn_right && should_move_on)
6682 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6683 else if (should_turn_left)
6684 MovDir[x][y] = left_dir;
6685 else if (should_turn_right)
6686 MovDir[x][y] = right_dir;
6687 else if (should_move_on)
6688 MovDir[x][y] = old_move_dir;
6690 else if (can_move_on && rnd > rnd_value / 8)
6691 MovDir[x][y] = old_move_dir;
6692 else if (can_turn_left && can_turn_right)
6693 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6694 else if (can_turn_left && rnd > rnd_value / 8)
6695 MovDir[x][y] = left_dir;
6696 else if (can_turn_right && rnd > rnd_value/8)
6697 MovDir[x][y] = right_dir;
6699 MovDir[x][y] = back_dir;
6701 xx = x + move_xy[MovDir[x][y]].dx;
6702 yy = y + move_xy[MovDir[x][y]].dy;
6704 if (!IN_LEV_FIELD(xx, yy) ||
6705 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6706 MovDir[x][y] = old_move_dir;
6710 else if (element == EL_DRAGON)
6712 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6713 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6714 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6716 int rnd = RND(rnd_value);
6718 if (can_move_on && rnd > rnd_value / 8)
6719 MovDir[x][y] = old_move_dir;
6720 else if (can_turn_left && can_turn_right)
6721 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6722 else if (can_turn_left && rnd > rnd_value / 8)
6723 MovDir[x][y] = left_dir;
6724 else if (can_turn_right && rnd > rnd_value / 8)
6725 MovDir[x][y] = right_dir;
6727 MovDir[x][y] = back_dir;
6729 xx = x + move_xy[MovDir[x][y]].dx;
6730 yy = y + move_xy[MovDir[x][y]].dy;
6732 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6733 MovDir[x][y] = old_move_dir;
6737 else if (element == EL_MOLE)
6739 boolean can_move_on =
6740 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6741 IS_AMOEBOID(Feld[move_x][move_y]) ||
6742 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6745 boolean can_turn_left =
6746 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6747 IS_AMOEBOID(Feld[left_x][left_y])));
6749 boolean can_turn_right =
6750 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6751 IS_AMOEBOID(Feld[right_x][right_y])));
6753 if (can_turn_left && can_turn_right)
6754 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6755 else if (can_turn_left)
6756 MovDir[x][y] = left_dir;
6758 MovDir[x][y] = right_dir;
6761 if (MovDir[x][y] != old_move_dir)
6764 else if (element == EL_BALLOON)
6766 MovDir[x][y] = game.wind_direction;
6769 else if (element == EL_SPRING)
6771 if (MovDir[x][y] & MV_HORIZONTAL)
6773 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6774 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6776 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6777 ResetGfxAnimation(move_x, move_y);
6778 TEST_DrawLevelField(move_x, move_y);
6780 MovDir[x][y] = back_dir;
6782 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6783 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6784 MovDir[x][y] = MV_NONE;
6789 else if (element == EL_ROBOT ||
6790 element == EL_SATELLITE ||
6791 element == EL_PENGUIN ||
6792 element == EL_EMC_ANDROID)
6794 int attr_x = -1, attr_y = -1;
6805 for (i = 0; i < MAX_PLAYERS; i++)
6807 struct PlayerInfo *player = &stored_player[i];
6808 int jx = player->jx, jy = player->jy;
6810 if (!player->active)
6814 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6822 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6823 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6824 game.engine_version < VERSION_IDENT(3,1,0,0)))
6830 if (element == EL_PENGUIN)
6833 static int xy[4][2] =
6841 for (i = 0; i < NUM_DIRECTIONS; i++)
6843 int ex = x + xy[i][0];
6844 int ey = y + xy[i][1];
6846 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6847 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6848 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6849 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6858 MovDir[x][y] = MV_NONE;
6860 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6861 else if (attr_x > x)
6862 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6864 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6865 else if (attr_y > y)
6866 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6868 if (element == EL_ROBOT)
6872 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6873 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6874 Moving2Blocked(x, y, &newx, &newy);
6876 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6877 MovDelay[x][y] = 8 + 8 * !RND(3);
6879 MovDelay[x][y] = 16;
6881 else if (element == EL_PENGUIN)
6887 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6889 boolean first_horiz = RND(2);
6890 int new_move_dir = MovDir[x][y];
6893 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6894 Moving2Blocked(x, y, &newx, &newy);
6896 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6900 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6901 Moving2Blocked(x, y, &newx, &newy);
6903 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6906 MovDir[x][y] = old_move_dir;
6910 else if (element == EL_SATELLITE)
6916 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6918 boolean first_horiz = RND(2);
6919 int new_move_dir = MovDir[x][y];
6922 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6923 Moving2Blocked(x, y, &newx, &newy);
6925 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6929 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6930 Moving2Blocked(x, y, &newx, &newy);
6932 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6935 MovDir[x][y] = old_move_dir;
6939 else if (element == EL_EMC_ANDROID)
6941 static int check_pos[16] =
6943 -1, /* 0 => (invalid) */
6944 7, /* 1 => MV_LEFT */
6945 3, /* 2 => MV_RIGHT */
6946 -1, /* 3 => (invalid) */
6948 0, /* 5 => MV_LEFT | MV_UP */
6949 2, /* 6 => MV_RIGHT | MV_UP */
6950 -1, /* 7 => (invalid) */
6951 5, /* 8 => MV_DOWN */
6952 6, /* 9 => MV_LEFT | MV_DOWN */
6953 4, /* 10 => MV_RIGHT | MV_DOWN */
6954 -1, /* 11 => (invalid) */
6955 -1, /* 12 => (invalid) */
6956 -1, /* 13 => (invalid) */
6957 -1, /* 14 => (invalid) */
6958 -1, /* 15 => (invalid) */
6966 { -1, -1, MV_LEFT | MV_UP },
6968 { +1, -1, MV_RIGHT | MV_UP },
6969 { +1, 0, MV_RIGHT },
6970 { +1, +1, MV_RIGHT | MV_DOWN },
6972 { -1, +1, MV_LEFT | MV_DOWN },
6975 int start_pos, check_order;
6976 boolean can_clone = FALSE;
6979 /* check if there is any free field around current position */
6980 for (i = 0; i < 8; i++)
6982 int newx = x + check_xy[i].dx;
6983 int newy = y + check_xy[i].dy;
6985 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6993 if (can_clone) /* randomly find an element to clone */
6997 start_pos = check_pos[RND(8)];
6998 check_order = (RND(2) ? -1 : +1);
7000 for (i = 0; i < 8; i++)
7002 int pos_raw = start_pos + i * check_order;
7003 int pos = (pos_raw + 8) % 8;
7004 int newx = x + check_xy[pos].dx;
7005 int newy = y + check_xy[pos].dy;
7007 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7009 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7010 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7012 Store[x][y] = Feld[newx][newy];
7021 if (can_clone) /* randomly find a direction to move */
7025 start_pos = check_pos[RND(8)];
7026 check_order = (RND(2) ? -1 : +1);
7028 for (i = 0; i < 8; i++)
7030 int pos_raw = start_pos + i * check_order;
7031 int pos = (pos_raw + 8) % 8;
7032 int newx = x + check_xy[pos].dx;
7033 int newy = y + check_xy[pos].dy;
7034 int new_move_dir = check_xy[pos].dir;
7036 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7038 MovDir[x][y] = new_move_dir;
7039 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7048 if (can_clone) /* cloning and moving successful */
7051 /* cannot clone -- try to move towards player */
7053 start_pos = check_pos[MovDir[x][y] & 0x0f];
7054 check_order = (RND(2) ? -1 : +1);
7056 for (i = 0; i < 3; i++)
7058 /* first check start_pos, then previous/next or (next/previous) pos */
7059 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7060 int pos = (pos_raw + 8) % 8;
7061 int newx = x + check_xy[pos].dx;
7062 int newy = y + check_xy[pos].dy;
7063 int new_move_dir = check_xy[pos].dir;
7065 if (IS_PLAYER(newx, newy))
7068 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7070 MovDir[x][y] = new_move_dir;
7071 MovDelay[x][y] = level.android_move_time * 8 + 1;
7078 else if (move_pattern == MV_TURNING_LEFT ||
7079 move_pattern == MV_TURNING_RIGHT ||
7080 move_pattern == MV_TURNING_LEFT_RIGHT ||
7081 move_pattern == MV_TURNING_RIGHT_LEFT ||
7082 move_pattern == MV_TURNING_RANDOM ||
7083 move_pattern == MV_ALL_DIRECTIONS)
7085 boolean can_turn_left =
7086 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7087 boolean can_turn_right =
7088 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7090 if (element_info[element].move_stepsize == 0) /* "not moving" */
7093 if (move_pattern == MV_TURNING_LEFT)
7094 MovDir[x][y] = left_dir;
7095 else if (move_pattern == MV_TURNING_RIGHT)
7096 MovDir[x][y] = right_dir;
7097 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7098 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7099 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7100 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7101 else if (move_pattern == MV_TURNING_RANDOM)
7102 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7103 can_turn_right && !can_turn_left ? right_dir :
7104 RND(2) ? left_dir : right_dir);
7105 else if (can_turn_left && can_turn_right)
7106 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7107 else if (can_turn_left)
7108 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7109 else if (can_turn_right)
7110 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7112 MovDir[x][y] = back_dir;
7114 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7116 else if (move_pattern == MV_HORIZONTAL ||
7117 move_pattern == MV_VERTICAL)
7119 if (move_pattern & old_move_dir)
7120 MovDir[x][y] = back_dir;
7121 else if (move_pattern == MV_HORIZONTAL)
7122 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7123 else if (move_pattern == MV_VERTICAL)
7124 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7126 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128 else if (move_pattern & MV_ANY_DIRECTION)
7130 MovDir[x][y] = move_pattern;
7131 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7133 else if (move_pattern & MV_WIND_DIRECTION)
7135 MovDir[x][y] = game.wind_direction;
7136 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7138 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7140 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7141 MovDir[x][y] = left_dir;
7142 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7143 MovDir[x][y] = right_dir;
7145 if (MovDir[x][y] != old_move_dir)
7146 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7148 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7150 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7151 MovDir[x][y] = right_dir;
7152 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7153 MovDir[x][y] = left_dir;
7155 if (MovDir[x][y] != old_move_dir)
7156 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7158 else if (move_pattern == MV_TOWARDS_PLAYER ||
7159 move_pattern == MV_AWAY_FROM_PLAYER)
7161 int attr_x = -1, attr_y = -1;
7163 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7174 for (i = 0; i < MAX_PLAYERS; i++)
7176 struct PlayerInfo *player = &stored_player[i];
7177 int jx = player->jx, jy = player->jy;
7179 if (!player->active)
7183 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7191 MovDir[x][y] = MV_NONE;
7193 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7194 else if (attr_x > x)
7195 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7197 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7198 else if (attr_y > y)
7199 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7201 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7203 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7205 boolean first_horiz = RND(2);
7206 int new_move_dir = MovDir[x][y];
7208 if (element_info[element].move_stepsize == 0) /* "not moving" */
7210 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7211 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7217 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7218 Moving2Blocked(x, y, &newx, &newy);
7220 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7224 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7225 Moving2Blocked(x, y, &newx, &newy);
7227 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7230 MovDir[x][y] = old_move_dir;
7233 else if (move_pattern == MV_WHEN_PUSHED ||
7234 move_pattern == MV_WHEN_DROPPED)
7236 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7237 MovDir[x][y] = MV_NONE;
7241 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7243 static int test_xy[7][2] =
7253 static int test_dir[7] =
7263 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7264 int move_preference = -1000000; /* start with very low preference */
7265 int new_move_dir = MV_NONE;
7266 int start_test = RND(4);
7269 for (i = 0; i < NUM_DIRECTIONS; i++)
7271 int move_dir = test_dir[start_test + i];
7272 int move_dir_preference;
7274 xx = x + test_xy[start_test + i][0];
7275 yy = y + test_xy[start_test + i][1];
7277 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7278 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7280 new_move_dir = move_dir;
7285 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7288 move_dir_preference = -1 * RunnerVisit[xx][yy];
7289 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7290 move_dir_preference = PlayerVisit[xx][yy];
7292 if (move_dir_preference > move_preference)
7294 /* prefer field that has not been visited for the longest time */
7295 move_preference = move_dir_preference;
7296 new_move_dir = move_dir;
7298 else if (move_dir_preference == move_preference &&
7299 move_dir == old_move_dir)
7301 /* prefer last direction when all directions are preferred equally */
7302 move_preference = move_dir_preference;
7303 new_move_dir = move_dir;
7307 MovDir[x][y] = new_move_dir;
7308 if (old_move_dir != new_move_dir)
7309 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7313 static void TurnRound(int x, int y)
7315 int direction = MovDir[x][y];
7319 GfxDir[x][y] = MovDir[x][y];
7321 if (direction != MovDir[x][y])
7325 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7327 ResetGfxFrame(x, y);
7330 static boolean JustBeingPushed(int x, int y)
7334 for (i = 0; i < MAX_PLAYERS; i++)
7336 struct PlayerInfo *player = &stored_player[i];
7338 if (player->active && player->is_pushing && player->MovPos)
7340 int next_jx = player->jx + (player->jx - player->last_jx);
7341 int next_jy = player->jy + (player->jy - player->last_jy);
7343 if (x == next_jx && y == next_jy)
7351 void StartMoving(int x, int y)
7353 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7354 int element = Feld[x][y];
7359 if (MovDelay[x][y] == 0)
7360 GfxAction[x][y] = ACTION_DEFAULT;
7362 if (CAN_FALL(element) && y < lev_fieldy - 1)
7364 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7365 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7366 if (JustBeingPushed(x, y))
7369 if (element == EL_QUICKSAND_FULL)
7371 if (IS_FREE(x, y + 1))
7373 InitMovingField(x, y, MV_DOWN);
7374 started_moving = TRUE;
7376 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7377 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7378 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7379 Store[x][y] = EL_ROCK;
7381 Store[x][y] = EL_ROCK;
7384 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7386 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7388 if (!MovDelay[x][y])
7390 MovDelay[x][y] = TILEY + 1;
7392 ResetGfxAnimation(x, y);
7393 ResetGfxAnimation(x, y + 1);
7398 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7399 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7406 Feld[x][y] = EL_QUICKSAND_EMPTY;
7407 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7408 Store[x][y + 1] = Store[x][y];
7411 PlayLevelSoundAction(x, y, ACTION_FILLING);
7413 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7415 if (!MovDelay[x][y])
7417 MovDelay[x][y] = TILEY + 1;
7419 ResetGfxAnimation(x, y);
7420 ResetGfxAnimation(x, y + 1);
7425 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7426 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7433 Feld[x][y] = EL_QUICKSAND_EMPTY;
7434 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7435 Store[x][y + 1] = Store[x][y];
7438 PlayLevelSoundAction(x, y, ACTION_FILLING);
7441 else if (element == EL_QUICKSAND_FAST_FULL)
7443 if (IS_FREE(x, y + 1))
7445 InitMovingField(x, y, MV_DOWN);
7446 started_moving = TRUE;
7448 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7449 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7450 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7451 Store[x][y] = EL_ROCK;
7453 Store[x][y] = EL_ROCK;
7456 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7458 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7460 if (!MovDelay[x][y])
7462 MovDelay[x][y] = TILEY + 1;
7464 ResetGfxAnimation(x, y);
7465 ResetGfxAnimation(x, y + 1);
7470 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7471 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7478 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7479 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7480 Store[x][y + 1] = Store[x][y];
7483 PlayLevelSoundAction(x, y, ACTION_FILLING);
7485 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7487 if (!MovDelay[x][y])
7489 MovDelay[x][y] = TILEY + 1;
7491 ResetGfxAnimation(x, y);
7492 ResetGfxAnimation(x, y + 1);
7497 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7498 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7505 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7506 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7507 Store[x][y + 1] = Store[x][y];
7510 PlayLevelSoundAction(x, y, ACTION_FILLING);
7513 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7514 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7516 InitMovingField(x, y, MV_DOWN);
7517 started_moving = TRUE;
7519 Feld[x][y] = EL_QUICKSAND_FILLING;
7520 Store[x][y] = element;
7522 PlayLevelSoundAction(x, y, ACTION_FILLING);
7524 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7525 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7527 InitMovingField(x, y, MV_DOWN);
7528 started_moving = TRUE;
7530 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7531 Store[x][y] = element;
7533 PlayLevelSoundAction(x, y, ACTION_FILLING);
7535 else if (element == EL_MAGIC_WALL_FULL)
7537 if (IS_FREE(x, y + 1))
7539 InitMovingField(x, y, MV_DOWN);
7540 started_moving = TRUE;
7542 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7543 Store[x][y] = EL_CHANGED(Store[x][y]);
7545 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7547 if (!MovDelay[x][y])
7548 MovDelay[x][y] = TILEY / 4 + 1;
7557 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7558 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7559 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7563 else if (element == EL_BD_MAGIC_WALL_FULL)
7565 if (IS_FREE(x, y + 1))
7567 InitMovingField(x, y, MV_DOWN);
7568 started_moving = TRUE;
7570 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7571 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7573 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7575 if (!MovDelay[x][y])
7576 MovDelay[x][y] = TILEY / 4 + 1;
7585 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7586 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7587 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7591 else if (element == EL_DC_MAGIC_WALL_FULL)
7593 if (IS_FREE(x, y + 1))
7595 InitMovingField(x, y, MV_DOWN);
7596 started_moving = TRUE;
7598 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7599 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7601 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7603 if (!MovDelay[x][y])
7604 MovDelay[x][y] = TILEY / 4 + 1;
7613 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7614 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7615 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7619 else if ((CAN_PASS_MAGIC_WALL(element) &&
7620 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7621 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7622 (CAN_PASS_DC_MAGIC_WALL(element) &&
7623 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7626 InitMovingField(x, y, MV_DOWN);
7627 started_moving = TRUE;
7630 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7631 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7632 EL_DC_MAGIC_WALL_FILLING);
7633 Store[x][y] = element;
7635 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7637 SplashAcid(x, y + 1);
7639 InitMovingField(x, y, MV_DOWN);
7640 started_moving = TRUE;
7642 Store[x][y] = EL_ACID;
7645 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7646 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7647 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7648 CAN_FALL(element) && WasJustFalling[x][y] &&
7649 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7651 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7652 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7653 (Feld[x][y + 1] == EL_BLOCKED)))
7655 /* this is needed for a special case not covered by calling "Impact()"
7656 from "ContinueMoving()": if an element moves to a tile directly below
7657 another element which was just falling on that tile (which was empty
7658 in the previous frame), the falling element above would just stop
7659 instead of smashing the element below (in previous version, the above
7660 element was just checked for "moving" instead of "falling", resulting
7661 in incorrect smashes caused by horizontal movement of the above
7662 element; also, the case of the player being the element to smash was
7663 simply not covered here... :-/ ) */
7665 CheckCollision[x][y] = 0;
7666 CheckImpact[x][y] = 0;
7670 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7672 if (MovDir[x][y] == MV_NONE)
7674 InitMovingField(x, y, MV_DOWN);
7675 started_moving = TRUE;
7678 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7680 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7681 MovDir[x][y] = MV_DOWN;
7683 InitMovingField(x, y, MV_DOWN);
7684 started_moving = TRUE;
7686 else if (element == EL_AMOEBA_DROP)
7688 Feld[x][y] = EL_AMOEBA_GROWING;
7689 Store[x][y] = EL_AMOEBA_WET;
7691 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7692 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7693 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7694 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7696 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7697 (IS_FREE(x - 1, y + 1) ||
7698 Feld[x - 1][y + 1] == EL_ACID));
7699 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7700 (IS_FREE(x + 1, y + 1) ||
7701 Feld[x + 1][y + 1] == EL_ACID));
7702 boolean can_fall_any = (can_fall_left || can_fall_right);
7703 boolean can_fall_both = (can_fall_left && can_fall_right);
7704 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7706 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7708 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7709 can_fall_right = FALSE;
7710 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7711 can_fall_left = FALSE;
7712 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7713 can_fall_right = FALSE;
7714 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7715 can_fall_left = FALSE;
7717 can_fall_any = (can_fall_left || can_fall_right);
7718 can_fall_both = FALSE;
7723 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7724 can_fall_right = FALSE; /* slip down on left side */
7726 can_fall_left = !(can_fall_right = RND(2));
7728 can_fall_both = FALSE;
7733 /* if not determined otherwise, prefer left side for slipping down */
7734 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7735 started_moving = TRUE;
7738 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7740 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7741 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7742 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7743 int belt_dir = game.belt_dir[belt_nr];
7745 if ((belt_dir == MV_LEFT && left_is_free) ||
7746 (belt_dir == MV_RIGHT && right_is_free))
7748 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7750 InitMovingField(x, y, belt_dir);
7751 started_moving = TRUE;
7753 Pushed[x][y] = TRUE;
7754 Pushed[nextx][y] = TRUE;
7756 GfxAction[x][y] = ACTION_DEFAULT;
7760 MovDir[x][y] = 0; /* if element was moving, stop it */
7765 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7766 if (CAN_MOVE(element) && !started_moving)
7768 int move_pattern = element_info[element].move_pattern;
7771 Moving2Blocked(x, y, &newx, &newy);
7773 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7776 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7777 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7779 WasJustMoving[x][y] = 0;
7780 CheckCollision[x][y] = 0;
7782 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7784 if (Feld[x][y] != element) /* element has changed */
7788 if (!MovDelay[x][y]) /* start new movement phase */
7790 /* all objects that can change their move direction after each step
7791 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7793 if (element != EL_YAMYAM &&
7794 element != EL_DARK_YAMYAM &&
7795 element != EL_PACMAN &&
7796 !(move_pattern & MV_ANY_DIRECTION) &&
7797 move_pattern != MV_TURNING_LEFT &&
7798 move_pattern != MV_TURNING_RIGHT &&
7799 move_pattern != MV_TURNING_LEFT_RIGHT &&
7800 move_pattern != MV_TURNING_RIGHT_LEFT &&
7801 move_pattern != MV_TURNING_RANDOM)
7805 if (MovDelay[x][y] && (element == EL_BUG ||
7806 element == EL_SPACESHIP ||
7807 element == EL_SP_SNIKSNAK ||
7808 element == EL_SP_ELECTRON ||
7809 element == EL_MOLE))
7810 TEST_DrawLevelField(x, y);
7814 if (MovDelay[x][y]) /* wait some time before next movement */
7818 if (element == EL_ROBOT ||
7819 element == EL_YAMYAM ||
7820 element == EL_DARK_YAMYAM)
7822 DrawLevelElementAnimationIfNeeded(x, y, element);
7823 PlayLevelSoundAction(x, y, ACTION_WAITING);
7825 else if (element == EL_SP_ELECTRON)
7826 DrawLevelElementAnimationIfNeeded(x, y, element);
7827 else if (element == EL_DRAGON)
7830 int dir = MovDir[x][y];
7831 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7832 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7833 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7834 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7835 dir == MV_UP ? IMG_FLAMES_1_UP :
7836 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7837 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7839 GfxAction[x][y] = ACTION_ATTACKING;
7841 if (IS_PLAYER(x, y))
7842 DrawPlayerField(x, y);
7844 TEST_DrawLevelField(x, y);
7846 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7848 for (i = 1; i <= 3; i++)
7850 int xx = x + i * dx;
7851 int yy = y + i * dy;
7852 int sx = SCREENX(xx);
7853 int sy = SCREENY(yy);
7854 int flame_graphic = graphic + (i - 1);
7856 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7861 int flamed = MovingOrBlocked2Element(xx, yy);
7863 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7866 RemoveMovingField(xx, yy);
7868 ChangeDelay[xx][yy] = 0;
7870 Feld[xx][yy] = EL_FLAMES;
7872 if (IN_SCR_FIELD(sx, sy))
7874 TEST_DrawLevelFieldCrumbled(xx, yy);
7875 DrawGraphic(sx, sy, flame_graphic, frame);
7880 if (Feld[xx][yy] == EL_FLAMES)
7881 Feld[xx][yy] = EL_EMPTY;
7882 TEST_DrawLevelField(xx, yy);
7887 if (MovDelay[x][y]) /* element still has to wait some time */
7889 PlayLevelSoundAction(x, y, ACTION_WAITING);
7895 /* now make next step */
7897 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7899 if (DONT_COLLIDE_WITH(element) &&
7900 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7901 !PLAYER_ENEMY_PROTECTED(newx, newy))
7903 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7908 else if (CAN_MOVE_INTO_ACID(element) &&
7909 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7910 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7911 (MovDir[x][y] == MV_DOWN ||
7912 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7914 SplashAcid(newx, newy);
7915 Store[x][y] = EL_ACID;
7917 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7919 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7920 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7921 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7922 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7925 TEST_DrawLevelField(x, y);
7927 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7928 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7929 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7931 local_player->friends_still_needed--;
7932 if (!local_player->friends_still_needed &&
7933 !local_player->GameOver && AllPlayersGone)
7934 PlayerWins(local_player);
7938 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7940 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7941 TEST_DrawLevelField(newx, newy);
7943 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7945 else if (!IS_FREE(newx, newy))
7947 GfxAction[x][y] = ACTION_WAITING;
7949 if (IS_PLAYER(x, y))
7950 DrawPlayerField(x, y);
7952 TEST_DrawLevelField(x, y);
7957 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7959 if (IS_FOOD_PIG(Feld[newx][newy]))
7961 if (IS_MOVING(newx, newy))
7962 RemoveMovingField(newx, newy);
7965 Feld[newx][newy] = EL_EMPTY;
7966 TEST_DrawLevelField(newx, newy);
7969 PlayLevelSound(x, y, SND_PIG_DIGGING);
7971 else if (!IS_FREE(newx, newy))
7973 if (IS_PLAYER(x, y))
7974 DrawPlayerField(x, y);
7976 TEST_DrawLevelField(x, y);
7981 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7983 if (Store[x][y] != EL_EMPTY)
7985 boolean can_clone = FALSE;
7988 /* check if element to clone is still there */
7989 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7991 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7999 /* cannot clone or target field not free anymore -- do not clone */
8000 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8001 Store[x][y] = EL_EMPTY;
8004 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8006 if (IS_MV_DIAGONAL(MovDir[x][y]))
8008 int diagonal_move_dir = MovDir[x][y];
8009 int stored = Store[x][y];
8010 int change_delay = 8;
8013 /* android is moving diagonally */
8015 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8017 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8018 GfxElement[x][y] = EL_EMC_ANDROID;
8019 GfxAction[x][y] = ACTION_SHRINKING;
8020 GfxDir[x][y] = diagonal_move_dir;
8021 ChangeDelay[x][y] = change_delay;
8023 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8026 DrawLevelGraphicAnimation(x, y, graphic);
8027 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8029 if (Feld[newx][newy] == EL_ACID)
8031 SplashAcid(newx, newy);
8036 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8038 Store[newx][newy] = EL_EMC_ANDROID;
8039 GfxElement[newx][newy] = EL_EMC_ANDROID;
8040 GfxAction[newx][newy] = ACTION_GROWING;
8041 GfxDir[newx][newy] = diagonal_move_dir;
8042 ChangeDelay[newx][newy] = change_delay;
8044 graphic = el_act_dir2img(GfxElement[newx][newy],
8045 GfxAction[newx][newy], GfxDir[newx][newy]);
8047 DrawLevelGraphicAnimation(newx, newy, graphic);
8048 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8054 Feld[newx][newy] = EL_EMPTY;
8055 TEST_DrawLevelField(newx, newy);
8057 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8060 else if (!IS_FREE(newx, newy))
8065 else if (IS_CUSTOM_ELEMENT(element) &&
8066 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8068 if (!DigFieldByCE(newx, newy, element))
8071 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8073 RunnerVisit[x][y] = FrameCounter;
8074 PlayerVisit[x][y] /= 8; /* expire player visit path */
8077 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8079 if (!IS_FREE(newx, newy))
8081 if (IS_PLAYER(x, y))
8082 DrawPlayerField(x, y);
8084 TEST_DrawLevelField(x, y);
8090 boolean wanna_flame = !RND(10);
8091 int dx = newx - x, dy = newy - y;
8092 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8093 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8094 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8095 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8096 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8097 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8100 IS_CLASSIC_ENEMY(element1) ||
8101 IS_CLASSIC_ENEMY(element2)) &&
8102 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8103 element1 != EL_FLAMES && element2 != EL_FLAMES)
8105 ResetGfxAnimation(x, y);
8106 GfxAction[x][y] = ACTION_ATTACKING;
8108 if (IS_PLAYER(x, y))
8109 DrawPlayerField(x, y);
8111 TEST_DrawLevelField(x, y);
8113 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8115 MovDelay[x][y] = 50;
8117 Feld[newx][newy] = EL_FLAMES;
8118 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8119 Feld[newx1][newy1] = EL_FLAMES;
8120 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8121 Feld[newx2][newy2] = EL_FLAMES;
8127 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8128 Feld[newx][newy] == EL_DIAMOND)
8130 if (IS_MOVING(newx, newy))
8131 RemoveMovingField(newx, newy);
8134 Feld[newx][newy] = EL_EMPTY;
8135 TEST_DrawLevelField(newx, newy);
8138 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8140 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8141 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8143 if (AmoebaNr[newx][newy])
8145 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8146 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8147 Feld[newx][newy] == EL_BD_AMOEBA)
8148 AmoebaCnt[AmoebaNr[newx][newy]]--;
8151 if (IS_MOVING(newx, newy))
8153 RemoveMovingField(newx, newy);
8157 Feld[newx][newy] = EL_EMPTY;
8158 TEST_DrawLevelField(newx, newy);
8161 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8163 else if ((element == EL_PACMAN || element == EL_MOLE)
8164 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8166 if (AmoebaNr[newx][newy])
8168 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8169 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8170 Feld[newx][newy] == EL_BD_AMOEBA)
8171 AmoebaCnt[AmoebaNr[newx][newy]]--;
8174 if (element == EL_MOLE)
8176 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8177 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8179 ResetGfxAnimation(x, y);
8180 GfxAction[x][y] = ACTION_DIGGING;
8181 TEST_DrawLevelField(x, y);
8183 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8185 return; /* wait for shrinking amoeba */
8187 else /* element == EL_PACMAN */
8189 Feld[newx][newy] = EL_EMPTY;
8190 TEST_DrawLevelField(newx, newy);
8191 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8194 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8195 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8196 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8198 /* wait for shrinking amoeba to completely disappear */
8201 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8203 /* object was running against a wall */
8207 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8208 DrawLevelElementAnimation(x, y, element);
8210 if (DONT_TOUCH(element))
8211 TestIfBadThingTouchesPlayer(x, y);
8216 InitMovingField(x, y, MovDir[x][y]);
8218 PlayLevelSoundAction(x, y, ACTION_MOVING);
8222 ContinueMoving(x, y);
8225 void ContinueMoving(int x, int y)
8227 int element = Feld[x][y];
8228 struct ElementInfo *ei = &element_info[element];
8229 int direction = MovDir[x][y];
8230 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8231 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8232 int newx = x + dx, newy = y + dy;
8233 int stored = Store[x][y];
8234 int stored_new = Store[newx][newy];
8235 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8236 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8237 boolean last_line = (newy == lev_fieldy - 1);
8239 MovPos[x][y] += getElementMoveStepsize(x, y);
8241 if (pushed_by_player) /* special case: moving object pushed by player */
8242 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8244 if (ABS(MovPos[x][y]) < TILEX)
8246 TEST_DrawLevelField(x, y);
8248 return; /* element is still moving */
8251 /* element reached destination field */
8253 Feld[x][y] = EL_EMPTY;
8254 Feld[newx][newy] = element;
8255 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8257 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8259 element = Feld[newx][newy] = EL_ACID;
8261 else if (element == EL_MOLE)
8263 Feld[x][y] = EL_SAND;
8265 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8267 else if (element == EL_QUICKSAND_FILLING)
8269 element = Feld[newx][newy] = get_next_element(element);
8270 Store[newx][newy] = Store[x][y];
8272 else if (element == EL_QUICKSAND_EMPTYING)
8274 Feld[x][y] = get_next_element(element);
8275 element = Feld[newx][newy] = Store[x][y];
8277 else if (element == EL_QUICKSAND_FAST_FILLING)
8279 element = Feld[newx][newy] = get_next_element(element);
8280 Store[newx][newy] = Store[x][y];
8282 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8284 Feld[x][y] = get_next_element(element);
8285 element = Feld[newx][newy] = Store[x][y];
8287 else if (element == EL_MAGIC_WALL_FILLING)
8289 element = Feld[newx][newy] = get_next_element(element);
8290 if (!game.magic_wall_active)
8291 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8292 Store[newx][newy] = Store[x][y];
8294 else if (element == EL_MAGIC_WALL_EMPTYING)
8296 Feld[x][y] = get_next_element(element);
8297 if (!game.magic_wall_active)
8298 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8299 element = Feld[newx][newy] = Store[x][y];
8301 InitField(newx, newy, FALSE);
8303 else if (element == EL_BD_MAGIC_WALL_FILLING)
8305 element = Feld[newx][newy] = get_next_element(element);
8306 if (!game.magic_wall_active)
8307 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8308 Store[newx][newy] = Store[x][y];
8310 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8312 Feld[x][y] = get_next_element(element);
8313 if (!game.magic_wall_active)
8314 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8315 element = Feld[newx][newy] = Store[x][y];
8317 InitField(newx, newy, FALSE);
8319 else if (element == EL_DC_MAGIC_WALL_FILLING)
8321 element = Feld[newx][newy] = get_next_element(element);
8322 if (!game.magic_wall_active)
8323 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8324 Store[newx][newy] = Store[x][y];
8326 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8328 Feld[x][y] = get_next_element(element);
8329 if (!game.magic_wall_active)
8330 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8331 element = Feld[newx][newy] = Store[x][y];
8333 InitField(newx, newy, FALSE);
8335 else if (element == EL_AMOEBA_DROPPING)
8337 Feld[x][y] = get_next_element(element);
8338 element = Feld[newx][newy] = Store[x][y];
8340 else if (element == EL_SOKOBAN_OBJECT)
8343 Feld[x][y] = Back[x][y];
8345 if (Back[newx][newy])
8346 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8348 Back[x][y] = Back[newx][newy] = 0;
8351 Store[x][y] = EL_EMPTY;
8356 MovDelay[newx][newy] = 0;
8358 if (CAN_CHANGE_OR_HAS_ACTION(element))
8360 /* copy element change control values to new field */
8361 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8362 ChangePage[newx][newy] = ChangePage[x][y];
8363 ChangeCount[newx][newy] = ChangeCount[x][y];
8364 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8367 CustomValue[newx][newy] = CustomValue[x][y];
8369 ChangeDelay[x][y] = 0;
8370 ChangePage[x][y] = -1;
8371 ChangeCount[x][y] = 0;
8372 ChangeEvent[x][y] = -1;
8374 CustomValue[x][y] = 0;
8376 /* copy animation control values to new field */
8377 GfxFrame[newx][newy] = GfxFrame[x][y];
8378 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8379 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8380 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8382 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8384 /* some elements can leave other elements behind after moving */
8385 if (ei->move_leave_element != EL_EMPTY &&
8386 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8387 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8389 int move_leave_element = ei->move_leave_element;
8391 /* this makes it possible to leave the removed element again */
8392 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8393 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8395 Feld[x][y] = move_leave_element;
8397 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8398 MovDir[x][y] = direction;
8400 InitField(x, y, FALSE);
8402 if (GFX_CRUMBLED(Feld[x][y]))
8403 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8405 if (ELEM_IS_PLAYER(move_leave_element))
8406 RelocatePlayer(x, y, move_leave_element);
8409 /* do this after checking for left-behind element */
8410 ResetGfxAnimation(x, y); /* reset animation values for old field */
8412 if (!CAN_MOVE(element) ||
8413 (CAN_FALL(element) && direction == MV_DOWN &&
8414 (element == EL_SPRING ||
8415 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8416 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8417 GfxDir[x][y] = MovDir[newx][newy] = 0;
8419 TEST_DrawLevelField(x, y);
8420 TEST_DrawLevelField(newx, newy);
8422 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8424 /* prevent pushed element from moving on in pushed direction */
8425 if (pushed_by_player && CAN_MOVE(element) &&
8426 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8427 !(element_info[element].move_pattern & direction))
8428 TurnRound(newx, newy);
8430 /* prevent elements on conveyor belt from moving on in last direction */
8431 if (pushed_by_conveyor && CAN_FALL(element) &&
8432 direction & MV_HORIZONTAL)
8433 MovDir[newx][newy] = 0;
8435 if (!pushed_by_player)
8437 int nextx = newx + dx, nexty = newy + dy;
8438 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8440 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8442 if (CAN_FALL(element) && direction == MV_DOWN)
8443 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8445 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8446 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8448 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8449 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8452 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8454 TestIfBadThingTouchesPlayer(newx, newy);
8455 TestIfBadThingTouchesFriend(newx, newy);
8457 if (!IS_CUSTOM_ELEMENT(element))
8458 TestIfBadThingTouchesOtherBadThing(newx, newy);
8460 else if (element == EL_PENGUIN)
8461 TestIfFriendTouchesBadThing(newx, newy);
8463 if (DONT_GET_HIT_BY(element))
8465 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8468 /* give the player one last chance (one more frame) to move away */
8469 if (CAN_FALL(element) && direction == MV_DOWN &&
8470 (last_line || (!IS_FREE(x, newy + 1) &&
8471 (!IS_PLAYER(x, newy + 1) ||
8472 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8475 if (pushed_by_player && !game.use_change_when_pushing_bug)
8477 int push_side = MV_DIR_OPPOSITE(direction);
8478 struct PlayerInfo *player = PLAYERINFO(x, y);
8480 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8481 player->index_bit, push_side);
8482 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8483 player->index_bit, push_side);
8486 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8487 MovDelay[newx][newy] = 1;
8489 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8491 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8492 TestIfElementHitsCustomElement(newx, newy, direction);
8493 TestIfPlayerTouchesCustomElement(newx, newy);
8494 TestIfElementTouchesCustomElement(newx, newy);
8496 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8497 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8498 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8499 MV_DIR_OPPOSITE(direction));
8502 int AmoebeNachbarNr(int ax, int ay)
8505 int element = Feld[ax][ay];
8507 static int xy[4][2] =
8515 for (i = 0; i < NUM_DIRECTIONS; i++)
8517 int x = ax + xy[i][0];
8518 int y = ay + xy[i][1];
8520 if (!IN_LEV_FIELD(x, y))
8523 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8524 group_nr = AmoebaNr[x][y];
8530 void AmoebenVereinigen(int ax, int ay)
8532 int i, x, y, xx, yy;
8533 int new_group_nr = AmoebaNr[ax][ay];
8534 static int xy[4][2] =
8542 if (new_group_nr == 0)
8545 for (i = 0; i < NUM_DIRECTIONS; i++)
8550 if (!IN_LEV_FIELD(x, y))
8553 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8554 Feld[x][y] == EL_BD_AMOEBA ||
8555 Feld[x][y] == EL_AMOEBA_DEAD) &&
8556 AmoebaNr[x][y] != new_group_nr)
8558 int old_group_nr = AmoebaNr[x][y];
8560 if (old_group_nr == 0)
8563 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8564 AmoebaCnt[old_group_nr] = 0;
8565 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8566 AmoebaCnt2[old_group_nr] = 0;
8568 SCAN_PLAYFIELD(xx, yy)
8570 if (AmoebaNr[xx][yy] == old_group_nr)
8571 AmoebaNr[xx][yy] = new_group_nr;
8577 void AmoebeUmwandeln(int ax, int ay)
8581 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8583 int group_nr = AmoebaNr[ax][ay];
8588 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8589 printf("AmoebeUmwandeln(): This should never happen!\n");
8594 SCAN_PLAYFIELD(x, y)
8596 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8599 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8603 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8604 SND_AMOEBA_TURNING_TO_GEM :
8605 SND_AMOEBA_TURNING_TO_ROCK));
8610 static int xy[4][2] =
8618 for (i = 0; i < NUM_DIRECTIONS; i++)
8623 if (!IN_LEV_FIELD(x, y))
8626 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8628 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8629 SND_AMOEBA_TURNING_TO_GEM :
8630 SND_AMOEBA_TURNING_TO_ROCK));
8637 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8640 int group_nr = AmoebaNr[ax][ay];
8641 boolean done = FALSE;
8646 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8647 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8652 SCAN_PLAYFIELD(x, y)
8654 if (AmoebaNr[x][y] == group_nr &&
8655 (Feld[x][y] == EL_AMOEBA_DEAD ||
8656 Feld[x][y] == EL_BD_AMOEBA ||
8657 Feld[x][y] == EL_AMOEBA_GROWING))
8660 Feld[x][y] = new_element;
8661 InitField(x, y, FALSE);
8662 TEST_DrawLevelField(x, y);
8668 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8669 SND_BD_AMOEBA_TURNING_TO_ROCK :
8670 SND_BD_AMOEBA_TURNING_TO_GEM));
8673 void AmoebeWaechst(int x, int y)
8675 static unsigned int sound_delay = 0;
8676 static unsigned int sound_delay_value = 0;
8678 if (!MovDelay[x][y]) /* start new growing cycle */
8682 if (DelayReached(&sound_delay, sound_delay_value))
8684 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8685 sound_delay_value = 30;
8689 if (MovDelay[x][y]) /* wait some time before growing bigger */
8692 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8694 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8695 6 - MovDelay[x][y]);
8697 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8700 if (!MovDelay[x][y])
8702 Feld[x][y] = Store[x][y];
8704 TEST_DrawLevelField(x, y);
8709 void AmoebaDisappearing(int x, int y)
8711 static unsigned int sound_delay = 0;
8712 static unsigned int sound_delay_value = 0;
8714 if (!MovDelay[x][y]) /* start new shrinking cycle */
8718 if (DelayReached(&sound_delay, sound_delay_value))
8719 sound_delay_value = 30;
8722 if (MovDelay[x][y]) /* wait some time before shrinking */
8725 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8727 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8728 6 - MovDelay[x][y]);
8730 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8733 if (!MovDelay[x][y])
8735 Feld[x][y] = EL_EMPTY;
8736 TEST_DrawLevelField(x, y);
8738 /* don't let mole enter this field in this cycle;
8739 (give priority to objects falling to this field from above) */
8745 void AmoebeAbleger(int ax, int ay)
8748 int element = Feld[ax][ay];
8749 int graphic = el2img(element);
8750 int newax = ax, neway = ay;
8751 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8752 static int xy[4][2] =
8760 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8762 Feld[ax][ay] = EL_AMOEBA_DEAD;
8763 TEST_DrawLevelField(ax, ay);
8767 if (IS_ANIMATED(graphic))
8768 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8770 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8771 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8773 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8776 if (MovDelay[ax][ay])
8780 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8783 int x = ax + xy[start][0];
8784 int y = ay + xy[start][1];
8786 if (!IN_LEV_FIELD(x, y))
8789 if (IS_FREE(x, y) ||
8790 CAN_GROW_INTO(Feld[x][y]) ||
8791 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8792 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8798 if (newax == ax && neway == ay)
8801 else /* normal or "filled" (BD style) amoeba */
8804 boolean waiting_for_player = FALSE;
8806 for (i = 0; i < NUM_DIRECTIONS; i++)
8808 int j = (start + i) % 4;
8809 int x = ax + xy[j][0];
8810 int y = ay + xy[j][1];
8812 if (!IN_LEV_FIELD(x, y))
8815 if (IS_FREE(x, y) ||
8816 CAN_GROW_INTO(Feld[x][y]) ||
8817 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8818 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8824 else if (IS_PLAYER(x, y))
8825 waiting_for_player = TRUE;
8828 if (newax == ax && neway == ay) /* amoeba cannot grow */
8830 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8832 Feld[ax][ay] = EL_AMOEBA_DEAD;
8833 TEST_DrawLevelField(ax, ay);
8834 AmoebaCnt[AmoebaNr[ax][ay]]--;
8836 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8838 if (element == EL_AMOEBA_FULL)
8839 AmoebeUmwandeln(ax, ay);
8840 else if (element == EL_BD_AMOEBA)
8841 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8846 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8848 /* amoeba gets larger by growing in some direction */
8850 int new_group_nr = AmoebaNr[ax][ay];
8853 if (new_group_nr == 0)
8855 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8856 printf("AmoebeAbleger(): This should never happen!\n");
8861 AmoebaNr[newax][neway] = new_group_nr;
8862 AmoebaCnt[new_group_nr]++;
8863 AmoebaCnt2[new_group_nr]++;
8865 /* if amoeba touches other amoeba(s) after growing, unify them */
8866 AmoebenVereinigen(newax, neway);
8868 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8870 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8876 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8877 (neway == lev_fieldy - 1 && newax != ax))
8879 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8880 Store[newax][neway] = element;
8882 else if (neway == ay || element == EL_EMC_DRIPPER)
8884 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8886 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8890 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8891 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8892 Store[ax][ay] = EL_AMOEBA_DROP;
8893 ContinueMoving(ax, ay);
8897 TEST_DrawLevelField(newax, neway);
8900 void Life(int ax, int ay)
8904 int element = Feld[ax][ay];
8905 int graphic = el2img(element);
8906 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8908 boolean changed = FALSE;
8910 if (IS_ANIMATED(graphic))
8911 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8916 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8917 MovDelay[ax][ay] = life_time;
8919 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8922 if (MovDelay[ax][ay])
8926 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8928 int xx = ax+x1, yy = ay+y1;
8931 if (!IN_LEV_FIELD(xx, yy))
8934 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8936 int x = xx+x2, y = yy+y2;
8938 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8941 if (((Feld[x][y] == element ||
8942 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8944 (IS_FREE(x, y) && Stop[x][y]))
8948 if (xx == ax && yy == ay) /* field in the middle */
8950 if (nachbarn < life_parameter[0] ||
8951 nachbarn > life_parameter[1])
8953 Feld[xx][yy] = EL_EMPTY;
8955 TEST_DrawLevelField(xx, yy);
8956 Stop[xx][yy] = TRUE;
8960 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8961 { /* free border field */
8962 if (nachbarn >= life_parameter[2] &&
8963 nachbarn <= life_parameter[3])
8965 Feld[xx][yy] = element;
8966 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8968 TEST_DrawLevelField(xx, yy);
8969 Stop[xx][yy] = TRUE;
8976 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8977 SND_GAME_OF_LIFE_GROWING);
8980 static void InitRobotWheel(int x, int y)
8982 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8985 static void RunRobotWheel(int x, int y)
8987 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8990 static void StopRobotWheel(int x, int y)
8992 if (ZX == x && ZY == y)
8996 game.robot_wheel_active = FALSE;
9000 static void InitTimegateWheel(int x, int y)
9002 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9005 static void RunTimegateWheel(int x, int y)
9007 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9010 static void InitMagicBallDelay(int x, int y)
9012 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9015 static void ActivateMagicBall(int bx, int by)
9019 if (level.ball_random)
9021 int pos_border = RND(8); /* select one of the eight border elements */
9022 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9023 int xx = pos_content % 3;
9024 int yy = pos_content / 3;
9029 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9030 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9034 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9036 int xx = x - bx + 1;
9037 int yy = y - by + 1;
9039 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9040 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9044 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9047 void CheckExit(int x, int y)
9049 if (local_player->gems_still_needed > 0 ||
9050 local_player->sokobanfields_still_needed > 0 ||
9051 local_player->lights_still_needed > 0)
9053 int element = Feld[x][y];
9054 int graphic = el2img(element);
9056 if (IS_ANIMATED(graphic))
9057 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9062 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9065 Feld[x][y] = EL_EXIT_OPENING;
9067 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9070 void CheckExitEM(int x, int y)
9072 if (local_player->gems_still_needed > 0 ||
9073 local_player->sokobanfields_still_needed > 0 ||
9074 local_player->lights_still_needed > 0)
9076 int element = Feld[x][y];
9077 int graphic = el2img(element);
9079 if (IS_ANIMATED(graphic))
9080 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9085 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9088 Feld[x][y] = EL_EM_EXIT_OPENING;
9090 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9093 void CheckExitSteel(int x, int y)
9095 if (local_player->gems_still_needed > 0 ||
9096 local_player->sokobanfields_still_needed > 0 ||
9097 local_player->lights_still_needed > 0)
9099 int element = Feld[x][y];
9100 int graphic = el2img(element);
9102 if (IS_ANIMATED(graphic))
9103 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9108 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9111 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9113 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9116 void CheckExitSteelEM(int x, int y)
9118 if (local_player->gems_still_needed > 0 ||
9119 local_player->sokobanfields_still_needed > 0 ||
9120 local_player->lights_still_needed > 0)
9122 int element = Feld[x][y];
9123 int graphic = el2img(element);
9125 if (IS_ANIMATED(graphic))
9126 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9131 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9134 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9136 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9139 void CheckExitSP(int x, int y)
9141 if (local_player->gems_still_needed > 0)
9143 int element = Feld[x][y];
9144 int graphic = el2img(element);
9146 if (IS_ANIMATED(graphic))
9147 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9152 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9155 Feld[x][y] = EL_SP_EXIT_OPENING;
9157 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9160 static void CloseAllOpenTimegates()
9164 SCAN_PLAYFIELD(x, y)
9166 int element = Feld[x][y];
9168 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9170 Feld[x][y] = EL_TIMEGATE_CLOSING;
9172 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9177 void DrawTwinkleOnField(int x, int y)
9179 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9182 if (Feld[x][y] == EL_BD_DIAMOND)
9185 if (MovDelay[x][y] == 0) /* next animation frame */
9186 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9188 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9192 DrawLevelElementAnimation(x, y, Feld[x][y]);
9194 if (MovDelay[x][y] != 0)
9196 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9197 10 - MovDelay[x][y]);
9199 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9204 void MauerWaechst(int x, int y)
9208 if (!MovDelay[x][y]) /* next animation frame */
9209 MovDelay[x][y] = 3 * delay;
9211 if (MovDelay[x][y]) /* wait some time before next frame */
9215 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9217 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9218 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9220 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9223 if (!MovDelay[x][y])
9225 if (MovDir[x][y] == MV_LEFT)
9227 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9228 TEST_DrawLevelField(x - 1, y);
9230 else if (MovDir[x][y] == MV_RIGHT)
9232 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9233 TEST_DrawLevelField(x + 1, y);
9235 else if (MovDir[x][y] == MV_UP)
9237 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9238 TEST_DrawLevelField(x, y - 1);
9242 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9243 TEST_DrawLevelField(x, y + 1);
9246 Feld[x][y] = Store[x][y];
9248 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9249 TEST_DrawLevelField(x, y);
9254 void MauerAbleger(int ax, int ay)
9256 int element = Feld[ax][ay];
9257 int graphic = el2img(element);
9258 boolean oben_frei = FALSE, unten_frei = FALSE;
9259 boolean links_frei = FALSE, rechts_frei = FALSE;
9260 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9261 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9262 boolean new_wall = FALSE;
9264 if (IS_ANIMATED(graphic))
9265 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9267 if (!MovDelay[ax][ay]) /* start building new wall */
9268 MovDelay[ax][ay] = 6;
9270 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9273 if (MovDelay[ax][ay])
9277 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9279 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9281 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9283 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9286 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9287 element == EL_EXPANDABLE_WALL_ANY)
9291 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9292 Store[ax][ay-1] = element;
9293 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9294 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9295 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9296 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9301 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9302 Store[ax][ay+1] = element;
9303 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9304 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9305 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9306 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9311 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9312 element == EL_EXPANDABLE_WALL_ANY ||
9313 element == EL_EXPANDABLE_WALL ||
9314 element == EL_BD_EXPANDABLE_WALL)
9318 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9319 Store[ax-1][ay] = element;
9320 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9321 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9322 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9323 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9329 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9330 Store[ax+1][ay] = element;
9331 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9332 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9333 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9334 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9339 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9340 TEST_DrawLevelField(ax, ay);
9342 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9344 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9345 unten_massiv = TRUE;
9346 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9347 links_massiv = TRUE;
9348 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9349 rechts_massiv = TRUE;
9351 if (((oben_massiv && unten_massiv) ||
9352 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9353 element == EL_EXPANDABLE_WALL) &&
9354 ((links_massiv && rechts_massiv) ||
9355 element == EL_EXPANDABLE_WALL_VERTICAL))
9356 Feld[ax][ay] = EL_WALL;
9359 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9362 void MauerAblegerStahl(int ax, int ay)
9364 int element = Feld[ax][ay];
9365 int graphic = el2img(element);
9366 boolean oben_frei = FALSE, unten_frei = FALSE;
9367 boolean links_frei = FALSE, rechts_frei = FALSE;
9368 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9369 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9370 boolean new_wall = FALSE;
9372 if (IS_ANIMATED(graphic))
9373 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9375 if (!MovDelay[ax][ay]) /* start building new wall */
9376 MovDelay[ax][ay] = 6;
9378 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9381 if (MovDelay[ax][ay])
9385 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9387 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9389 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9391 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9394 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9395 element == EL_EXPANDABLE_STEELWALL_ANY)
9399 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9400 Store[ax][ay-1] = element;
9401 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9402 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9403 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9404 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9409 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9410 Store[ax][ay+1] = element;
9411 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9412 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9413 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9414 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9419 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9420 element == EL_EXPANDABLE_STEELWALL_ANY)
9424 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9425 Store[ax-1][ay] = element;
9426 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9427 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9428 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9429 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9435 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9436 Store[ax+1][ay] = element;
9437 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9438 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9439 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9440 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9445 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9447 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9448 unten_massiv = TRUE;
9449 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9450 links_massiv = TRUE;
9451 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9452 rechts_massiv = TRUE;
9454 if (((oben_massiv && unten_massiv) ||
9455 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9456 ((links_massiv && rechts_massiv) ||
9457 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9458 Feld[ax][ay] = EL_STEELWALL;
9461 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9464 void CheckForDragon(int x, int y)
9467 boolean dragon_found = FALSE;
9468 static int xy[4][2] =
9476 for (i = 0; i < NUM_DIRECTIONS; i++)
9478 for (j = 0; j < 4; j++)
9480 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9482 if (IN_LEV_FIELD(xx, yy) &&
9483 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9485 if (Feld[xx][yy] == EL_DRAGON)
9486 dragon_found = TRUE;
9495 for (i = 0; i < NUM_DIRECTIONS; i++)
9497 for (j = 0; j < 3; j++)
9499 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9501 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9503 Feld[xx][yy] = EL_EMPTY;
9504 TEST_DrawLevelField(xx, yy);
9513 static void InitBuggyBase(int x, int y)
9515 int element = Feld[x][y];
9516 int activating_delay = FRAMES_PER_SECOND / 4;
9519 (element == EL_SP_BUGGY_BASE ?
9520 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9521 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9523 element == EL_SP_BUGGY_BASE_ACTIVE ?
9524 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9527 static void WarnBuggyBase(int x, int y)
9530 static int xy[4][2] =
9538 for (i = 0; i < NUM_DIRECTIONS; i++)
9540 int xx = x + xy[i][0];
9541 int yy = y + xy[i][1];
9543 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9545 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9552 static void InitTrap(int x, int y)
9554 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9557 static void ActivateTrap(int x, int y)
9559 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9562 static void ChangeActiveTrap(int x, int y)
9564 int graphic = IMG_TRAP_ACTIVE;
9566 /* if new animation frame was drawn, correct crumbled sand border */
9567 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9568 TEST_DrawLevelFieldCrumbled(x, y);
9571 static int getSpecialActionElement(int element, int number, int base_element)
9573 return (element != EL_EMPTY ? element :
9574 number != -1 ? base_element + number - 1 :
9578 static int getModifiedActionNumber(int value_old, int operator, int operand,
9579 int value_min, int value_max)
9581 int value_new = (operator == CA_MODE_SET ? operand :
9582 operator == CA_MODE_ADD ? value_old + operand :
9583 operator == CA_MODE_SUBTRACT ? value_old - operand :
9584 operator == CA_MODE_MULTIPLY ? value_old * operand :
9585 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9586 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9589 return (value_new < value_min ? value_min :
9590 value_new > value_max ? value_max :
9594 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9596 struct ElementInfo *ei = &element_info[element];
9597 struct ElementChangeInfo *change = &ei->change_page[page];
9598 int target_element = change->target_element;
9599 int action_type = change->action_type;
9600 int action_mode = change->action_mode;
9601 int action_arg = change->action_arg;
9602 int action_element = change->action_element;
9605 if (!change->has_action)
9608 /* ---------- determine action paramater values -------------------------- */
9610 int level_time_value =
9611 (level.time > 0 ? TimeLeft :
9614 int action_arg_element_raw =
9615 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9616 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9617 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9618 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9619 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9620 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9621 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9623 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9625 int action_arg_direction =
9626 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9627 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9628 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9629 change->actual_trigger_side :
9630 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9631 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9634 int action_arg_number_min =
9635 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9638 int action_arg_number_max =
9639 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9640 action_type == CA_SET_LEVEL_GEMS ? 999 :
9641 action_type == CA_SET_LEVEL_TIME ? 9999 :
9642 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9643 action_type == CA_SET_CE_VALUE ? 9999 :
9644 action_type == CA_SET_CE_SCORE ? 9999 :
9647 int action_arg_number_reset =
9648 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9649 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9650 action_type == CA_SET_LEVEL_TIME ? level.time :
9651 action_type == CA_SET_LEVEL_SCORE ? 0 :
9652 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9653 action_type == CA_SET_CE_SCORE ? 0 :
9656 int action_arg_number =
9657 (action_arg <= CA_ARG_MAX ? action_arg :
9658 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9659 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9660 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9661 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9662 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9663 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9664 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9665 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9666 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9667 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9668 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9669 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9670 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9671 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9672 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9673 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9674 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9675 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9676 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9677 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9678 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9681 int action_arg_number_old =
9682 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9683 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9684 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9685 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9686 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9689 int action_arg_number_new =
9690 getModifiedActionNumber(action_arg_number_old,
9691 action_mode, action_arg_number,
9692 action_arg_number_min, action_arg_number_max);
9694 int trigger_player_bits =
9695 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9696 change->actual_trigger_player_bits : change->trigger_player);
9698 int action_arg_player_bits =
9699 (action_arg >= CA_ARG_PLAYER_1 &&
9700 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9701 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9702 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9705 /* ---------- execute action -------------------------------------------- */
9707 switch (action_type)
9714 /* ---------- level actions ------------------------------------------- */
9716 case CA_RESTART_LEVEL:
9718 game.restart_level = TRUE;
9723 case CA_SHOW_ENVELOPE:
9725 int element = getSpecialActionElement(action_arg_element,
9726 action_arg_number, EL_ENVELOPE_1);
9728 if (IS_ENVELOPE(element))
9729 local_player->show_envelope = element;
9734 case CA_SET_LEVEL_TIME:
9736 if (level.time > 0) /* only modify limited time value */
9738 TimeLeft = action_arg_number_new;
9740 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9742 DisplayGameControlValues();
9744 if (!TimeLeft && setup.time_limit)
9745 for (i = 0; i < MAX_PLAYERS; i++)
9746 KillPlayer(&stored_player[i]);
9752 case CA_SET_LEVEL_SCORE:
9754 local_player->score = action_arg_number_new;
9756 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9758 DisplayGameControlValues();
9763 case CA_SET_LEVEL_GEMS:
9765 local_player->gems_still_needed = action_arg_number_new;
9767 game.snapshot.collected_item = TRUE;
9769 game_panel_controls[GAME_PANEL_GEMS].value =
9770 local_player->gems_still_needed;
9772 DisplayGameControlValues();
9777 case CA_SET_LEVEL_WIND:
9779 game.wind_direction = action_arg_direction;
9784 case CA_SET_LEVEL_RANDOM_SEED:
9786 /* ensure that setting a new random seed while playing is predictable */
9787 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9792 /* ---------- player actions ------------------------------------------ */
9794 case CA_MOVE_PLAYER:
9796 /* automatically move to the next field in specified direction */
9797 for (i = 0; i < MAX_PLAYERS; i++)
9798 if (trigger_player_bits & (1 << i))
9799 stored_player[i].programmed_action = action_arg_direction;
9804 case CA_EXIT_PLAYER:
9806 for (i = 0; i < MAX_PLAYERS; i++)
9807 if (action_arg_player_bits & (1 << i))
9808 PlayerWins(&stored_player[i]);
9813 case CA_KILL_PLAYER:
9815 for (i = 0; i < MAX_PLAYERS; i++)
9816 if (action_arg_player_bits & (1 << i))
9817 KillPlayer(&stored_player[i]);
9822 case CA_SET_PLAYER_KEYS:
9824 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9825 int element = getSpecialActionElement(action_arg_element,
9826 action_arg_number, EL_KEY_1);
9828 if (IS_KEY(element))
9830 for (i = 0; i < MAX_PLAYERS; i++)
9832 if (trigger_player_bits & (1 << i))
9834 stored_player[i].key[KEY_NR(element)] = key_state;
9836 DrawGameDoorValues();
9844 case CA_SET_PLAYER_SPEED:
9846 for (i = 0; i < MAX_PLAYERS; i++)
9848 if (trigger_player_bits & (1 << i))
9850 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9852 if (action_arg == CA_ARG_SPEED_FASTER &&
9853 stored_player[i].cannot_move)
9855 action_arg_number = STEPSIZE_VERY_SLOW;
9857 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9858 action_arg == CA_ARG_SPEED_FASTER)
9860 action_arg_number = 2;
9861 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9864 else if (action_arg == CA_ARG_NUMBER_RESET)
9866 action_arg_number = level.initial_player_stepsize[i];
9870 getModifiedActionNumber(move_stepsize,
9873 action_arg_number_min,
9874 action_arg_number_max);
9876 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9883 case CA_SET_PLAYER_SHIELD:
9885 for (i = 0; i < MAX_PLAYERS; i++)
9887 if (trigger_player_bits & (1 << i))
9889 if (action_arg == CA_ARG_SHIELD_OFF)
9891 stored_player[i].shield_normal_time_left = 0;
9892 stored_player[i].shield_deadly_time_left = 0;
9894 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9896 stored_player[i].shield_normal_time_left = 999999;
9898 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9900 stored_player[i].shield_normal_time_left = 999999;
9901 stored_player[i].shield_deadly_time_left = 999999;
9909 case CA_SET_PLAYER_GRAVITY:
9911 for (i = 0; i < MAX_PLAYERS; i++)
9913 if (trigger_player_bits & (1 << i))
9915 stored_player[i].gravity =
9916 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9917 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9918 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9919 stored_player[i].gravity);
9926 case CA_SET_PLAYER_ARTWORK:
9928 for (i = 0; i < MAX_PLAYERS; i++)
9930 if (trigger_player_bits & (1 << i))
9932 int artwork_element = action_arg_element;
9934 if (action_arg == CA_ARG_ELEMENT_RESET)
9936 (level.use_artwork_element[i] ? level.artwork_element[i] :
9937 stored_player[i].element_nr);
9939 if (stored_player[i].artwork_element != artwork_element)
9940 stored_player[i].Frame = 0;
9942 stored_player[i].artwork_element = artwork_element;
9944 SetPlayerWaiting(&stored_player[i], FALSE);
9946 /* set number of special actions for bored and sleeping animation */
9947 stored_player[i].num_special_action_bored =
9948 get_num_special_action(artwork_element,
9949 ACTION_BORING_1, ACTION_BORING_LAST);
9950 stored_player[i].num_special_action_sleeping =
9951 get_num_special_action(artwork_element,
9952 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9959 case CA_SET_PLAYER_INVENTORY:
9961 for (i = 0; i < MAX_PLAYERS; i++)
9963 struct PlayerInfo *player = &stored_player[i];
9966 if (trigger_player_bits & (1 << i))
9968 int inventory_element = action_arg_element;
9970 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9971 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9972 action_arg == CA_ARG_ELEMENT_ACTION)
9974 int element = inventory_element;
9975 int collect_count = element_info[element].collect_count_initial;
9977 if (!IS_CUSTOM_ELEMENT(element))
9980 if (collect_count == 0)
9981 player->inventory_infinite_element = element;
9983 for (k = 0; k < collect_count; k++)
9984 if (player->inventory_size < MAX_INVENTORY_SIZE)
9985 player->inventory_element[player->inventory_size++] =
9988 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9989 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9990 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9992 if (player->inventory_infinite_element != EL_UNDEFINED &&
9993 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9994 action_arg_element_raw))
9995 player->inventory_infinite_element = EL_UNDEFINED;
9997 for (k = 0, j = 0; j < player->inventory_size; j++)
9999 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10000 action_arg_element_raw))
10001 player->inventory_element[k++] = player->inventory_element[j];
10004 player->inventory_size = k;
10006 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10008 if (player->inventory_size > 0)
10010 for (j = 0; j < player->inventory_size - 1; j++)
10011 player->inventory_element[j] = player->inventory_element[j + 1];
10013 player->inventory_size--;
10016 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10018 if (player->inventory_size > 0)
10019 player->inventory_size--;
10021 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10023 player->inventory_infinite_element = EL_UNDEFINED;
10024 player->inventory_size = 0;
10026 else if (action_arg == CA_ARG_INVENTORY_RESET)
10028 player->inventory_infinite_element = EL_UNDEFINED;
10029 player->inventory_size = 0;
10031 if (level.use_initial_inventory[i])
10033 for (j = 0; j < level.initial_inventory_size[i]; j++)
10035 int element = level.initial_inventory_content[i][j];
10036 int collect_count = element_info[element].collect_count_initial;
10038 if (!IS_CUSTOM_ELEMENT(element))
10041 if (collect_count == 0)
10042 player->inventory_infinite_element = element;
10044 for (k = 0; k < collect_count; k++)
10045 if (player->inventory_size < MAX_INVENTORY_SIZE)
10046 player->inventory_element[player->inventory_size++] =
10057 /* ---------- CE actions ---------------------------------------------- */
10059 case CA_SET_CE_VALUE:
10061 int last_ce_value = CustomValue[x][y];
10063 CustomValue[x][y] = action_arg_number_new;
10065 if (CustomValue[x][y] != last_ce_value)
10067 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10068 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10070 if (CustomValue[x][y] == 0)
10072 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10073 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10080 case CA_SET_CE_SCORE:
10082 int last_ce_score = ei->collect_score;
10084 ei->collect_score = action_arg_number_new;
10086 if (ei->collect_score != last_ce_score)
10088 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10089 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10091 if (ei->collect_score == 0)
10095 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10096 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10099 This is a very special case that seems to be a mixture between
10100 CheckElementChange() and CheckTriggeredElementChange(): while
10101 the first one only affects single elements that are triggered
10102 directly, the second one affects multiple elements in the playfield
10103 that are triggered indirectly by another element. This is a third
10104 case: Changing the CE score always affects multiple identical CEs,
10105 so every affected CE must be checked, not only the single CE for
10106 which the CE score was changed in the first place (as every instance
10107 of that CE shares the same CE score, and therefore also can change)!
10109 SCAN_PLAYFIELD(xx, yy)
10111 if (Feld[xx][yy] == element)
10112 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10113 CE_SCORE_GETS_ZERO);
10121 case CA_SET_CE_ARTWORK:
10123 int artwork_element = action_arg_element;
10124 boolean reset_frame = FALSE;
10127 if (action_arg == CA_ARG_ELEMENT_RESET)
10128 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10131 if (ei->gfx_element != artwork_element)
10132 reset_frame = TRUE;
10134 ei->gfx_element = artwork_element;
10136 SCAN_PLAYFIELD(xx, yy)
10138 if (Feld[xx][yy] == element)
10142 ResetGfxAnimation(xx, yy);
10143 ResetRandomAnimationValue(xx, yy);
10146 TEST_DrawLevelField(xx, yy);
10153 /* ---------- engine actions ------------------------------------------ */
10155 case CA_SET_ENGINE_SCAN_MODE:
10157 InitPlayfieldScanMode(action_arg);
10167 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10169 int old_element = Feld[x][y];
10170 int new_element = GetElementFromGroupElement(element);
10171 int previous_move_direction = MovDir[x][y];
10172 int last_ce_value = CustomValue[x][y];
10173 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10174 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10175 boolean add_player_onto_element = (new_element_is_player &&
10176 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10177 IS_WALKABLE(old_element));
10179 if (!add_player_onto_element)
10181 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10182 RemoveMovingField(x, y);
10186 Feld[x][y] = new_element;
10188 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10189 MovDir[x][y] = previous_move_direction;
10191 if (element_info[new_element].use_last_ce_value)
10192 CustomValue[x][y] = last_ce_value;
10194 InitField_WithBug1(x, y, FALSE);
10196 new_element = Feld[x][y]; /* element may have changed */
10198 ResetGfxAnimation(x, y);
10199 ResetRandomAnimationValue(x, y);
10201 TEST_DrawLevelField(x, y);
10203 if (GFX_CRUMBLED(new_element))
10204 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10207 /* check if element under the player changes from accessible to unaccessible
10208 (needed for special case of dropping element which then changes) */
10209 /* (must be checked after creating new element for walkable group elements) */
10210 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10211 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10218 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10219 if (new_element_is_player)
10220 RelocatePlayer(x, y, new_element);
10223 ChangeCount[x][y]++; /* count number of changes in the same frame */
10225 TestIfBadThingTouchesPlayer(x, y);
10226 TestIfPlayerTouchesCustomElement(x, y);
10227 TestIfElementTouchesCustomElement(x, y);
10230 static void CreateField(int x, int y, int element)
10232 CreateFieldExt(x, y, element, FALSE);
10235 static void CreateElementFromChange(int x, int y, int element)
10237 element = GET_VALID_RUNTIME_ELEMENT(element);
10239 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10241 int old_element = Feld[x][y];
10243 /* prevent changed element from moving in same engine frame
10244 unless both old and new element can either fall or move */
10245 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10246 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10250 CreateFieldExt(x, y, element, TRUE);
10253 static boolean ChangeElement(int x, int y, int element, int page)
10255 struct ElementInfo *ei = &element_info[element];
10256 struct ElementChangeInfo *change = &ei->change_page[page];
10257 int ce_value = CustomValue[x][y];
10258 int ce_score = ei->collect_score;
10259 int target_element;
10260 int old_element = Feld[x][y];
10262 /* always use default change event to prevent running into a loop */
10263 if (ChangeEvent[x][y] == -1)
10264 ChangeEvent[x][y] = CE_DELAY;
10266 if (ChangeEvent[x][y] == CE_DELAY)
10268 /* reset actual trigger element, trigger player and action element */
10269 change->actual_trigger_element = EL_EMPTY;
10270 change->actual_trigger_player = EL_EMPTY;
10271 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10272 change->actual_trigger_side = CH_SIDE_NONE;
10273 change->actual_trigger_ce_value = 0;
10274 change->actual_trigger_ce_score = 0;
10277 /* do not change elements more than a specified maximum number of changes */
10278 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10281 ChangeCount[x][y]++; /* count number of changes in the same frame */
10283 if (change->explode)
10290 if (change->use_target_content)
10292 boolean complete_replace = TRUE;
10293 boolean can_replace[3][3];
10296 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10299 boolean is_walkable;
10300 boolean is_diggable;
10301 boolean is_collectible;
10302 boolean is_removable;
10303 boolean is_destructible;
10304 int ex = x + xx - 1;
10305 int ey = y + yy - 1;
10306 int content_element = change->target_content.e[xx][yy];
10309 can_replace[xx][yy] = TRUE;
10311 if (ex == x && ey == y) /* do not check changing element itself */
10314 if (content_element == EL_EMPTY_SPACE)
10316 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10321 if (!IN_LEV_FIELD(ex, ey))
10323 can_replace[xx][yy] = FALSE;
10324 complete_replace = FALSE;
10331 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10332 e = MovingOrBlocked2Element(ex, ey);
10334 is_empty = (IS_FREE(ex, ey) ||
10335 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10337 is_walkable = (is_empty || IS_WALKABLE(e));
10338 is_diggable = (is_empty || IS_DIGGABLE(e));
10339 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10340 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10341 is_removable = (is_diggable || is_collectible);
10343 can_replace[xx][yy] =
10344 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10345 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10346 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10347 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10348 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10349 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10350 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10352 if (!can_replace[xx][yy])
10353 complete_replace = FALSE;
10356 if (!change->only_if_complete || complete_replace)
10358 boolean something_has_changed = FALSE;
10360 if (change->only_if_complete && change->use_random_replace &&
10361 RND(100) < change->random_percentage)
10364 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10366 int ex = x + xx - 1;
10367 int ey = y + yy - 1;
10368 int content_element;
10370 if (can_replace[xx][yy] && (!change->use_random_replace ||
10371 RND(100) < change->random_percentage))
10373 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10374 RemoveMovingField(ex, ey);
10376 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10378 content_element = change->target_content.e[xx][yy];
10379 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10380 ce_value, ce_score);
10382 CreateElementFromChange(ex, ey, target_element);
10384 something_has_changed = TRUE;
10386 /* for symmetry reasons, freeze newly created border elements */
10387 if (ex != x || ey != y)
10388 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10392 if (something_has_changed)
10394 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10395 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10401 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10402 ce_value, ce_score);
10404 if (element == EL_DIAGONAL_GROWING ||
10405 element == EL_DIAGONAL_SHRINKING)
10407 target_element = Store[x][y];
10409 Store[x][y] = EL_EMPTY;
10412 CreateElementFromChange(x, y, target_element);
10414 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10415 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10418 /* this uses direct change before indirect change */
10419 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10424 static void HandleElementChange(int x, int y, int page)
10426 int element = MovingOrBlocked2Element(x, y);
10427 struct ElementInfo *ei = &element_info[element];
10428 struct ElementChangeInfo *change = &ei->change_page[page];
10429 boolean handle_action_before_change = FALSE;
10432 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10433 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10436 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10437 x, y, element, element_info[element].token_name);
10438 printf("HandleElementChange(): This should never happen!\n");
10443 /* this can happen with classic bombs on walkable, changing elements */
10444 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10449 if (ChangeDelay[x][y] == 0) /* initialize element change */
10451 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10453 if (change->can_change)
10455 /* !!! not clear why graphic animation should be reset at all here !!! */
10456 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10457 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10460 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10462 When using an animation frame delay of 1 (this only happens with
10463 "sp_zonk.moving.left/right" in the classic graphics), the default
10464 (non-moving) animation shows wrong animation frames (while the
10465 moving animation, like "sp_zonk.moving.left/right", is correct,
10466 so this graphical bug never shows up with the classic graphics).
10467 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10468 be drawn instead of the correct frames 0,1,2,3. This is caused by
10469 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10470 an element change: First when the change delay ("ChangeDelay[][]")
10471 counter has reached zero after decrementing, then a second time in
10472 the next frame (after "GfxFrame[][]" was already incremented) when
10473 "ChangeDelay[][]" is reset to the initial delay value again.
10475 This causes frame 0 to be drawn twice, while the last frame won't
10476 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10478 As some animations may already be cleverly designed around this bug
10479 (at least the "Snake Bite" snake tail animation does this), it cannot
10480 simply be fixed here without breaking such existing animations.
10481 Unfortunately, it cannot easily be detected if a graphics set was
10482 designed "before" or "after" the bug was fixed. As a workaround,
10483 a new graphics set option "game.graphics_engine_version" was added
10484 to be able to specify the game's major release version for which the
10485 graphics set was designed, which can then be used to decide if the
10486 bugfix should be used (version 4 and above) or not (version 3 or
10487 below, or if no version was specified at all, as with old sets).
10489 (The wrong/fixed animation frames can be tested with the test level set
10490 "test_gfxframe" and level "000", which contains a specially prepared
10491 custom element at level position (x/y) == (11/9) which uses the zonk
10492 animation mentioned above. Using "game.graphics_engine_version: 4"
10493 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10494 This can also be seen from the debug output for this test element.)
10497 /* when a custom element is about to change (for example by change delay),
10498 do not reset graphic animation when the custom element is moving */
10499 if (game.graphics_engine_version < 4 &&
10502 ResetGfxAnimation(x, y);
10503 ResetRandomAnimationValue(x, y);
10506 if (change->pre_change_function)
10507 change->pre_change_function(x, y);
10511 ChangeDelay[x][y]--;
10513 if (ChangeDelay[x][y] != 0) /* continue element change */
10515 if (change->can_change)
10517 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10519 if (IS_ANIMATED(graphic))
10520 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10522 if (change->change_function)
10523 change->change_function(x, y);
10526 else /* finish element change */
10528 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10530 page = ChangePage[x][y];
10531 ChangePage[x][y] = -1;
10533 change = &ei->change_page[page];
10536 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10538 ChangeDelay[x][y] = 1; /* try change after next move step */
10539 ChangePage[x][y] = page; /* remember page to use for change */
10544 /* special case: set new level random seed before changing element */
10545 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10546 handle_action_before_change = TRUE;
10548 if (change->has_action && handle_action_before_change)
10549 ExecuteCustomElementAction(x, y, element, page);
10551 if (change->can_change)
10553 if (ChangeElement(x, y, element, page))
10555 if (change->post_change_function)
10556 change->post_change_function(x, y);
10560 if (change->has_action && !handle_action_before_change)
10561 ExecuteCustomElementAction(x, y, element, page);
10565 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10566 int trigger_element,
10568 int trigger_player,
10572 boolean change_done_any = FALSE;
10573 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10576 if (!(trigger_events[trigger_element][trigger_event]))
10579 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10581 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10583 int element = EL_CUSTOM_START + i;
10584 boolean change_done = FALSE;
10587 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10588 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10591 for (p = 0; p < element_info[element].num_change_pages; p++)
10593 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10595 if (change->can_change_or_has_action &&
10596 change->has_event[trigger_event] &&
10597 change->trigger_side & trigger_side &&
10598 change->trigger_player & trigger_player &&
10599 change->trigger_page & trigger_page_bits &&
10600 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10602 change->actual_trigger_element = trigger_element;
10603 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10604 change->actual_trigger_player_bits = trigger_player;
10605 change->actual_trigger_side = trigger_side;
10606 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10607 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10609 if ((change->can_change && !change_done) || change->has_action)
10613 SCAN_PLAYFIELD(x, y)
10615 if (Feld[x][y] == element)
10617 if (change->can_change && !change_done)
10619 /* if element already changed in this frame, not only prevent
10620 another element change (checked in ChangeElement()), but
10621 also prevent additional element actions for this element */
10623 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10624 !level.use_action_after_change_bug)
10627 ChangeDelay[x][y] = 1;
10628 ChangeEvent[x][y] = trigger_event;
10630 HandleElementChange(x, y, p);
10632 else if (change->has_action)
10634 /* if element already changed in this frame, not only prevent
10635 another element change (checked in ChangeElement()), but
10636 also prevent additional element actions for this element */
10638 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10639 !level.use_action_after_change_bug)
10642 ExecuteCustomElementAction(x, y, element, p);
10643 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10648 if (change->can_change)
10650 change_done = TRUE;
10651 change_done_any = TRUE;
10658 RECURSION_LOOP_DETECTION_END();
10660 return change_done_any;
10663 static boolean CheckElementChangeExt(int x, int y,
10665 int trigger_element,
10667 int trigger_player,
10670 boolean change_done = FALSE;
10673 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10674 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10677 if (Feld[x][y] == EL_BLOCKED)
10679 Blocked2Moving(x, y, &x, &y);
10680 element = Feld[x][y];
10683 /* check if element has already changed or is about to change after moving */
10684 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10685 Feld[x][y] != element) ||
10687 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10688 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10689 ChangePage[x][y] != -1)))
10692 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10694 for (p = 0; p < element_info[element].num_change_pages; p++)
10696 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10698 /* check trigger element for all events where the element that is checked
10699 for changing interacts with a directly adjacent element -- this is
10700 different to element changes that affect other elements to change on the
10701 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10702 boolean check_trigger_element =
10703 (trigger_event == CE_TOUCHING_X ||
10704 trigger_event == CE_HITTING_X ||
10705 trigger_event == CE_HIT_BY_X ||
10706 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10708 if (change->can_change_or_has_action &&
10709 change->has_event[trigger_event] &&
10710 change->trigger_side & trigger_side &&
10711 change->trigger_player & trigger_player &&
10712 (!check_trigger_element ||
10713 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10715 change->actual_trigger_element = trigger_element;
10716 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10717 change->actual_trigger_player_bits = trigger_player;
10718 change->actual_trigger_side = trigger_side;
10719 change->actual_trigger_ce_value = CustomValue[x][y];
10720 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10722 /* special case: trigger element not at (x,y) position for some events */
10723 if (check_trigger_element)
10735 { 0, 0 }, { 0, 0 }, { 0, 0 },
10739 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10740 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10742 change->actual_trigger_ce_value = CustomValue[xx][yy];
10743 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10746 if (change->can_change && !change_done)
10748 ChangeDelay[x][y] = 1;
10749 ChangeEvent[x][y] = trigger_event;
10751 HandleElementChange(x, y, p);
10753 change_done = TRUE;
10755 else if (change->has_action)
10757 ExecuteCustomElementAction(x, y, element, p);
10758 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10763 RECURSION_LOOP_DETECTION_END();
10765 return change_done;
10768 static void PlayPlayerSound(struct PlayerInfo *player)
10770 int jx = player->jx, jy = player->jy;
10771 int sound_element = player->artwork_element;
10772 int last_action = player->last_action_waiting;
10773 int action = player->action_waiting;
10775 if (player->is_waiting)
10777 if (action != last_action)
10778 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10780 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10784 if (action != last_action)
10785 StopSound(element_info[sound_element].sound[last_action]);
10787 if (last_action == ACTION_SLEEPING)
10788 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10792 static void PlayAllPlayersSound()
10796 for (i = 0; i < MAX_PLAYERS; i++)
10797 if (stored_player[i].active)
10798 PlayPlayerSound(&stored_player[i]);
10801 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10803 boolean last_waiting = player->is_waiting;
10804 int move_dir = player->MovDir;
10806 player->dir_waiting = move_dir;
10807 player->last_action_waiting = player->action_waiting;
10811 if (!last_waiting) /* not waiting -> waiting */
10813 player->is_waiting = TRUE;
10815 player->frame_counter_bored =
10817 game.player_boring_delay_fixed +
10818 GetSimpleRandom(game.player_boring_delay_random);
10819 player->frame_counter_sleeping =
10821 game.player_sleeping_delay_fixed +
10822 GetSimpleRandom(game.player_sleeping_delay_random);
10824 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10827 if (game.player_sleeping_delay_fixed +
10828 game.player_sleeping_delay_random > 0 &&
10829 player->anim_delay_counter == 0 &&
10830 player->post_delay_counter == 0 &&
10831 FrameCounter >= player->frame_counter_sleeping)
10832 player->is_sleeping = TRUE;
10833 else if (game.player_boring_delay_fixed +
10834 game.player_boring_delay_random > 0 &&
10835 FrameCounter >= player->frame_counter_bored)
10836 player->is_bored = TRUE;
10838 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10839 player->is_bored ? ACTION_BORING :
10842 if (player->is_sleeping && player->use_murphy)
10844 /* special case for sleeping Murphy when leaning against non-free tile */
10846 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10847 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10848 !IS_MOVING(player->jx - 1, player->jy)))
10849 move_dir = MV_LEFT;
10850 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10851 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10852 !IS_MOVING(player->jx + 1, player->jy)))
10853 move_dir = MV_RIGHT;
10855 player->is_sleeping = FALSE;
10857 player->dir_waiting = move_dir;
10860 if (player->is_sleeping)
10862 if (player->num_special_action_sleeping > 0)
10864 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10866 int last_special_action = player->special_action_sleeping;
10867 int num_special_action = player->num_special_action_sleeping;
10868 int special_action =
10869 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10870 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10871 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10872 last_special_action + 1 : ACTION_SLEEPING);
10873 int special_graphic =
10874 el_act_dir2img(player->artwork_element, special_action, move_dir);
10876 player->anim_delay_counter =
10877 graphic_info[special_graphic].anim_delay_fixed +
10878 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10879 player->post_delay_counter =
10880 graphic_info[special_graphic].post_delay_fixed +
10881 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10883 player->special_action_sleeping = special_action;
10886 if (player->anim_delay_counter > 0)
10888 player->action_waiting = player->special_action_sleeping;
10889 player->anim_delay_counter--;
10891 else if (player->post_delay_counter > 0)
10893 player->post_delay_counter--;
10897 else if (player->is_bored)
10899 if (player->num_special_action_bored > 0)
10901 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10903 int special_action =
10904 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10905 int special_graphic =
10906 el_act_dir2img(player->artwork_element, special_action, move_dir);
10908 player->anim_delay_counter =
10909 graphic_info[special_graphic].anim_delay_fixed +
10910 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10911 player->post_delay_counter =
10912 graphic_info[special_graphic].post_delay_fixed +
10913 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10915 player->special_action_bored = special_action;
10918 if (player->anim_delay_counter > 0)
10920 player->action_waiting = player->special_action_bored;
10921 player->anim_delay_counter--;
10923 else if (player->post_delay_counter > 0)
10925 player->post_delay_counter--;
10930 else if (last_waiting) /* waiting -> not waiting */
10932 player->is_waiting = FALSE;
10933 player->is_bored = FALSE;
10934 player->is_sleeping = FALSE;
10936 player->frame_counter_bored = -1;
10937 player->frame_counter_sleeping = -1;
10939 player->anim_delay_counter = 0;
10940 player->post_delay_counter = 0;
10942 player->dir_waiting = player->MovDir;
10943 player->action_waiting = ACTION_DEFAULT;
10945 player->special_action_bored = ACTION_DEFAULT;
10946 player->special_action_sleeping = ACTION_DEFAULT;
10950 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10952 if ((!player->is_moving && player->was_moving) ||
10953 (player->MovPos == 0 && player->was_moving) ||
10954 (player->is_snapping && !player->was_snapping) ||
10955 (player->is_dropping && !player->was_dropping))
10957 if (!CheckSaveEngineSnapshotToList())
10960 player->was_moving = FALSE;
10961 player->was_snapping = TRUE;
10962 player->was_dropping = TRUE;
10966 if (player->is_moving)
10967 player->was_moving = TRUE;
10969 if (!player->is_snapping)
10970 player->was_snapping = FALSE;
10972 if (!player->is_dropping)
10973 player->was_dropping = FALSE;
10977 static void CheckSingleStepMode(struct PlayerInfo *player)
10979 if (tape.single_step && tape.recording && !tape.pausing)
10981 /* as it is called "single step mode", just return to pause mode when the
10982 player stopped moving after one tile (or never starts moving at all) */
10983 if (!player->is_moving &&
10984 !player->is_pushing &&
10985 !player->is_dropping_pressed)
10987 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10988 SnapField(player, 0, 0); /* stop snapping */
10992 CheckSaveEngineSnapshot(player);
10995 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10997 int left = player_action & JOY_LEFT;
10998 int right = player_action & JOY_RIGHT;
10999 int up = player_action & JOY_UP;
11000 int down = player_action & JOY_DOWN;
11001 int button1 = player_action & JOY_BUTTON_1;
11002 int button2 = player_action & JOY_BUTTON_2;
11003 int dx = (left ? -1 : right ? 1 : 0);
11004 int dy = (up ? -1 : down ? 1 : 0);
11006 if (!player->active || tape.pausing)
11012 SnapField(player, dx, dy);
11016 DropElement(player);
11018 MovePlayer(player, dx, dy);
11021 CheckSingleStepMode(player);
11023 SetPlayerWaiting(player, FALSE);
11025 return player_action;
11029 /* no actions for this player (no input at player's configured device) */
11031 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11032 SnapField(player, 0, 0);
11033 CheckGravityMovementWhenNotMoving(player);
11035 if (player->MovPos == 0)
11036 SetPlayerWaiting(player, TRUE);
11038 if (player->MovPos == 0) /* needed for tape.playing */
11039 player->is_moving = FALSE;
11041 player->is_dropping = FALSE;
11042 player->is_dropping_pressed = FALSE;
11043 player->drop_pressed_delay = 0;
11045 CheckSingleStepMode(player);
11051 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11054 if (!tape.use_mouse)
11057 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11058 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11059 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11062 static void SetTapeActionFromMouseAction(byte *tape_action,
11063 struct MouseActionInfo *mouse_action)
11065 if (!tape.use_mouse)
11068 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11069 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11070 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11073 static void CheckLevelTime()
11077 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11078 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11080 if (level.native_em_level->lev->home == 0) /* all players at home */
11082 PlayerWins(local_player);
11084 AllPlayersGone = TRUE;
11086 level.native_em_level->lev->home = -1;
11089 if (level.native_em_level->ply[0]->alive == 0 &&
11090 level.native_em_level->ply[1]->alive == 0 &&
11091 level.native_em_level->ply[2]->alive == 0 &&
11092 level.native_em_level->ply[3]->alive == 0) /* all dead */
11093 AllPlayersGone = TRUE;
11095 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11097 if (game_sp.LevelSolved &&
11098 !game_sp.GameOver) /* game won */
11100 PlayerWins(local_player);
11102 game_sp.GameOver = TRUE;
11104 AllPlayersGone = TRUE;
11107 if (game_sp.GameOver) /* game lost */
11108 AllPlayersGone = TRUE;
11110 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11112 if (game_mm.level_solved &&
11113 !game_mm.game_over) /* game won */
11115 PlayerWins(local_player);
11117 game_mm.game_over = TRUE;
11119 AllPlayersGone = TRUE;
11122 if (game_mm.game_over) /* game lost */
11123 AllPlayersGone = TRUE;
11126 if (TimeFrames >= FRAMES_PER_SECOND)
11131 for (i = 0; i < MAX_PLAYERS; i++)
11133 struct PlayerInfo *player = &stored_player[i];
11135 if (SHIELD_ON(player))
11137 player->shield_normal_time_left--;
11139 if (player->shield_deadly_time_left > 0)
11140 player->shield_deadly_time_left--;
11144 if (!local_player->LevelSolved && !level.use_step_counter)
11152 if (TimeLeft <= 10 && setup.time_limit)
11153 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11155 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11156 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11158 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11160 if (!TimeLeft && setup.time_limit)
11162 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11163 level.native_em_level->lev->killed_out_of_time = TRUE;
11165 for (i = 0; i < MAX_PLAYERS; i++)
11166 KillPlayer(&stored_player[i]);
11169 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11171 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11174 level.native_em_level->lev->time =
11175 (game.no_time_limit ? TimePlayed : TimeLeft);
11178 if (tape.recording || tape.playing)
11179 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11182 if (tape.recording || tape.playing)
11183 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11185 UpdateAndDisplayGameControlValues();
11188 void AdvanceFrameAndPlayerCounters(int player_nr)
11192 /* advance frame counters (global frame counter and time frame counter) */
11196 /* advance player counters (counters for move delay, move animation etc.) */
11197 for (i = 0; i < MAX_PLAYERS; i++)
11199 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11200 int move_delay_value = stored_player[i].move_delay_value;
11201 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11203 if (!advance_player_counters) /* not all players may be affected */
11206 if (move_frames == 0) /* less than one move per game frame */
11208 int stepsize = TILEX / move_delay_value;
11209 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11210 int count = (stored_player[i].is_moving ?
11211 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11213 if (count % delay == 0)
11217 stored_player[i].Frame += move_frames;
11219 if (stored_player[i].MovPos != 0)
11220 stored_player[i].StepFrame += move_frames;
11222 if (stored_player[i].move_delay > 0)
11223 stored_player[i].move_delay--;
11225 /* due to bugs in previous versions, counter must count up, not down */
11226 if (stored_player[i].push_delay != -1)
11227 stored_player[i].push_delay++;
11229 if (stored_player[i].drop_delay > 0)
11230 stored_player[i].drop_delay--;
11232 if (stored_player[i].is_dropping_pressed)
11233 stored_player[i].drop_pressed_delay++;
11237 void StartGameActions(boolean init_network_game, boolean record_tape,
11240 unsigned int new_random_seed = InitRND(random_seed);
11243 TapeStartRecording(new_random_seed);
11245 #if defined(NETWORK_AVALIABLE)
11246 if (init_network_game)
11248 SendToServer_StartPlaying();
11257 void GameActionsExt()
11260 static unsigned int game_frame_delay = 0;
11262 unsigned int game_frame_delay_value;
11263 byte *recorded_player_action;
11264 byte summarized_player_action = 0;
11265 byte tape_action[MAX_PLAYERS];
11268 /* detect endless loops, caused by custom element programming */
11269 if (recursion_loop_detected && recursion_loop_depth == 0)
11271 char *message = getStringCat3("Internal Error! Element ",
11272 EL_NAME(recursion_loop_element),
11273 " caused endless loop! Quit the game?");
11275 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11276 EL_NAME(recursion_loop_element));
11278 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11280 recursion_loop_detected = FALSE; /* if game should be continued */
11287 if (game.restart_level)
11288 StartGameActions(options.network, setup.autorecord, level.random_seed);
11290 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11291 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11293 if (level.native_em_level->lev->home == 0) /* all players at home */
11295 PlayerWins(local_player);
11297 AllPlayersGone = TRUE;
11299 level.native_em_level->lev->home = -1;
11302 if (level.native_em_level->ply[0]->alive == 0 &&
11303 level.native_em_level->ply[1]->alive == 0 &&
11304 level.native_em_level->ply[2]->alive == 0 &&
11305 level.native_em_level->ply[3]->alive == 0) /* all dead */
11306 AllPlayersGone = TRUE;
11308 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11310 if (game_sp.LevelSolved &&
11311 !game_sp.GameOver) /* game won */
11313 PlayerWins(local_player);
11315 game_sp.GameOver = TRUE;
11317 AllPlayersGone = TRUE;
11320 if (game_sp.GameOver) /* game lost */
11321 AllPlayersGone = TRUE;
11323 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11325 if (game_mm.level_solved &&
11326 !game_mm.game_over) /* game won */
11328 PlayerWins(local_player);
11330 game_mm.game_over = TRUE;
11332 AllPlayersGone = TRUE;
11335 if (game_mm.game_over) /* game lost */
11336 AllPlayersGone = TRUE;
11339 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11342 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11345 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11348 game_frame_delay_value =
11349 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11351 if (tape.playing && tape.warp_forward && !tape.pausing)
11352 game_frame_delay_value = 0;
11354 SetVideoFrameDelay(game_frame_delay_value);
11358 /* ---------- main game synchronization point ---------- */
11360 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11362 printf("::: skip == %d\n", skip);
11365 /* ---------- main game synchronization point ---------- */
11367 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11371 if (network_playing && !network_player_action_received)
11373 /* try to get network player actions in time */
11375 #if defined(NETWORK_AVALIABLE)
11376 /* last chance to get network player actions without main loop delay */
11377 HandleNetworking();
11380 /* game was quit by network peer */
11381 if (game_status != GAME_MODE_PLAYING)
11384 if (!network_player_action_received)
11385 return; /* failed to get network player actions in time */
11387 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11393 /* at this point we know that we really continue executing the game */
11395 network_player_action_received = FALSE;
11397 /* when playing tape, read previously recorded player input from tape data */
11398 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11400 local_player->effective_mouse_action = local_player->mouse_action;
11402 if (recorded_player_action != NULL)
11403 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11404 recorded_player_action);
11406 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11410 if (tape.set_centered_player)
11412 game.centered_player_nr_next = tape.centered_player_nr_next;
11413 game.set_centered_player = TRUE;
11416 for (i = 0; i < MAX_PLAYERS; i++)
11418 summarized_player_action |= stored_player[i].action;
11420 if (!network_playing && (game.team_mode || tape.playing))
11421 stored_player[i].effective_action = stored_player[i].action;
11424 #if defined(NETWORK_AVALIABLE)
11425 if (network_playing)
11426 SendToServer_MovePlayer(summarized_player_action);
11429 // summarize all actions at local players mapped input device position
11430 // (this allows using different input devices in single player mode)
11431 if (!options.network && !game.team_mode)
11432 stored_player[map_player_action[local_player->index_nr]].effective_action =
11433 summarized_player_action;
11435 if (tape.recording &&
11437 setup.input_on_focus &&
11438 game.centered_player_nr != -1)
11440 for (i = 0; i < MAX_PLAYERS; i++)
11441 stored_player[i].effective_action =
11442 (i == game.centered_player_nr ? summarized_player_action : 0);
11445 if (recorded_player_action != NULL)
11446 for (i = 0; i < MAX_PLAYERS; i++)
11447 stored_player[i].effective_action = recorded_player_action[i];
11449 for (i = 0; i < MAX_PLAYERS; i++)
11451 tape_action[i] = stored_player[i].effective_action;
11453 /* (this may happen in the RND game engine if a player was not present on
11454 the playfield on level start, but appeared later from a custom element */
11455 if (setup.team_mode &&
11458 !tape.player_participates[i])
11459 tape.player_participates[i] = TRUE;
11462 SetTapeActionFromMouseAction(tape_action,
11463 &local_player->effective_mouse_action);
11465 /* only record actions from input devices, but not programmed actions */
11466 if (tape.recording)
11467 TapeRecordAction(tape_action);
11469 #if USE_NEW_PLAYER_ASSIGNMENTS
11470 // !!! also map player actions in single player mode !!!
11471 // if (game.team_mode)
11474 byte mapped_action[MAX_PLAYERS];
11476 #if DEBUG_PLAYER_ACTIONS
11478 for (i = 0; i < MAX_PLAYERS; i++)
11479 printf(" %d, ", stored_player[i].effective_action);
11482 for (i = 0; i < MAX_PLAYERS; i++)
11483 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11485 for (i = 0; i < MAX_PLAYERS; i++)
11486 stored_player[i].effective_action = mapped_action[i];
11488 #if DEBUG_PLAYER_ACTIONS
11490 for (i = 0; i < MAX_PLAYERS; i++)
11491 printf(" %d, ", stored_player[i].effective_action);
11495 #if DEBUG_PLAYER_ACTIONS
11499 for (i = 0; i < MAX_PLAYERS; i++)
11500 printf(" %d, ", stored_player[i].effective_action);
11506 for (i = 0; i < MAX_PLAYERS; i++)
11508 // allow engine snapshot in case of changed movement attempt
11509 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11510 (stored_player[i].effective_action & KEY_MOTION))
11511 game.snapshot.changed_action = TRUE;
11513 // allow engine snapshot in case of snapping/dropping attempt
11514 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11515 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11516 game.snapshot.changed_action = TRUE;
11518 game.snapshot.last_action[i] = stored_player[i].effective_action;
11521 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11523 GameActions_EM_Main();
11525 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11527 GameActions_SP_Main();
11529 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11531 GameActions_MM_Main();
11535 GameActions_RND_Main();
11538 BlitScreenToBitmap(backbuffer);
11542 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11544 if (global.show_frames_per_second)
11546 static unsigned int fps_counter = 0;
11547 static int fps_frames = 0;
11548 unsigned int fps_delay_ms = Counter() - fps_counter;
11552 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11554 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11557 fps_counter = Counter();
11559 /* always draw FPS to screen after FPS value was updated */
11560 redraw_mask |= REDRAW_FPS;
11563 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11564 if (GetDrawDeactivationMask() == REDRAW_NONE)
11565 redraw_mask |= REDRAW_FPS;
11569 static void GameActions_CheckSaveEngineSnapshot()
11571 if (!game.snapshot.save_snapshot)
11574 // clear flag for saving snapshot _before_ saving snapshot
11575 game.snapshot.save_snapshot = FALSE;
11577 SaveEngineSnapshotToList();
11584 GameActions_CheckSaveEngineSnapshot();
11587 void GameActions_EM_Main()
11589 byte effective_action[MAX_PLAYERS];
11590 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11593 for (i = 0; i < MAX_PLAYERS; i++)
11594 effective_action[i] = stored_player[i].effective_action;
11596 GameActions_EM(effective_action, warp_mode);
11599 void GameActions_SP_Main()
11601 byte effective_action[MAX_PLAYERS];
11602 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11605 for (i = 0; i < MAX_PLAYERS; i++)
11606 effective_action[i] = stored_player[i].effective_action;
11608 GameActions_SP(effective_action, warp_mode);
11610 for (i = 0; i < MAX_PLAYERS; i++)
11612 if (stored_player[i].force_dropping)
11613 stored_player[i].action |= KEY_BUTTON_DROP;
11615 stored_player[i].force_dropping = FALSE;
11619 void GameActions_MM_Main()
11621 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11623 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11626 void GameActions_RND_Main()
11631 void GameActions_RND()
11633 int magic_wall_x = 0, magic_wall_y = 0;
11634 int i, x, y, element, graphic, last_gfx_frame;
11636 InitPlayfieldScanModeVars();
11638 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11640 SCAN_PLAYFIELD(x, y)
11642 ChangeCount[x][y] = 0;
11643 ChangeEvent[x][y] = -1;
11647 if (game.set_centered_player)
11649 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11651 /* switching to "all players" only possible if all players fit to screen */
11652 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11654 game.centered_player_nr_next = game.centered_player_nr;
11655 game.set_centered_player = FALSE;
11658 /* do not switch focus to non-existing (or non-active) player */
11659 if (game.centered_player_nr_next >= 0 &&
11660 !stored_player[game.centered_player_nr_next].active)
11662 game.centered_player_nr_next = game.centered_player_nr;
11663 game.set_centered_player = FALSE;
11667 if (game.set_centered_player &&
11668 ScreenMovPos == 0) /* screen currently aligned at tile position */
11672 if (game.centered_player_nr_next == -1)
11674 setScreenCenteredToAllPlayers(&sx, &sy);
11678 sx = stored_player[game.centered_player_nr_next].jx;
11679 sy = stored_player[game.centered_player_nr_next].jy;
11682 game.centered_player_nr = game.centered_player_nr_next;
11683 game.set_centered_player = FALSE;
11685 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11686 DrawGameDoorValues();
11689 for (i = 0; i < MAX_PLAYERS; i++)
11691 int actual_player_action = stored_player[i].effective_action;
11694 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11695 - rnd_equinox_tetrachloride 048
11696 - rnd_equinox_tetrachloride_ii 096
11697 - rnd_emanuel_schmieg 002
11698 - doctor_sloan_ww 001, 020
11700 if (stored_player[i].MovPos == 0)
11701 CheckGravityMovement(&stored_player[i]);
11704 /* overwrite programmed action with tape action */
11705 if (stored_player[i].programmed_action)
11706 actual_player_action = stored_player[i].programmed_action;
11708 PlayerActions(&stored_player[i], actual_player_action);
11710 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11713 ScrollScreen(NULL, SCROLL_GO_ON);
11715 /* for backwards compatibility, the following code emulates a fixed bug that
11716 occured when pushing elements (causing elements that just made their last
11717 pushing step to already (if possible) make their first falling step in the
11718 same game frame, which is bad); this code is also needed to use the famous
11719 "spring push bug" which is used in older levels and might be wanted to be
11720 used also in newer levels, but in this case the buggy pushing code is only
11721 affecting the "spring" element and no other elements */
11723 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11725 for (i = 0; i < MAX_PLAYERS; i++)
11727 struct PlayerInfo *player = &stored_player[i];
11728 int x = player->jx;
11729 int y = player->jy;
11731 if (player->active && player->is_pushing && player->is_moving &&
11733 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11734 Feld[x][y] == EL_SPRING))
11736 ContinueMoving(x, y);
11738 /* continue moving after pushing (this is actually a bug) */
11739 if (!IS_MOVING(x, y))
11740 Stop[x][y] = FALSE;
11745 SCAN_PLAYFIELD(x, y)
11747 ChangeCount[x][y] = 0;
11748 ChangeEvent[x][y] = -1;
11750 /* this must be handled before main playfield loop */
11751 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11754 if (MovDelay[x][y] <= 0)
11758 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11761 if (MovDelay[x][y] <= 0)
11764 TEST_DrawLevelField(x, y);
11766 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11771 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11773 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11774 printf("GameActions(): This should never happen!\n");
11776 ChangePage[x][y] = -1;
11780 Stop[x][y] = FALSE;
11781 if (WasJustMoving[x][y] > 0)
11782 WasJustMoving[x][y]--;
11783 if (WasJustFalling[x][y] > 0)
11784 WasJustFalling[x][y]--;
11785 if (CheckCollision[x][y] > 0)
11786 CheckCollision[x][y]--;
11787 if (CheckImpact[x][y] > 0)
11788 CheckImpact[x][y]--;
11792 /* reset finished pushing action (not done in ContinueMoving() to allow
11793 continuous pushing animation for elements with zero push delay) */
11794 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11796 ResetGfxAnimation(x, y);
11797 TEST_DrawLevelField(x, y);
11801 if (IS_BLOCKED(x, y))
11805 Blocked2Moving(x, y, &oldx, &oldy);
11806 if (!IS_MOVING(oldx, oldy))
11808 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11809 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11810 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11811 printf("GameActions(): This should never happen!\n");
11817 SCAN_PLAYFIELD(x, y)
11819 element = Feld[x][y];
11820 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11821 last_gfx_frame = GfxFrame[x][y];
11823 ResetGfxFrame(x, y);
11825 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11826 DrawLevelGraphicAnimation(x, y, graphic);
11828 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11829 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11830 ResetRandomAnimationValue(x, y);
11832 SetRandomAnimationValue(x, y);
11834 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11836 if (IS_INACTIVE(element))
11838 if (IS_ANIMATED(graphic))
11839 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11844 /* this may take place after moving, so 'element' may have changed */
11845 if (IS_CHANGING(x, y) &&
11846 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11848 int page = element_info[element].event_page_nr[CE_DELAY];
11850 HandleElementChange(x, y, page);
11852 element = Feld[x][y];
11853 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11856 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11860 element = Feld[x][y];
11861 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11863 if (IS_ANIMATED(graphic) &&
11864 !IS_MOVING(x, y) &&
11866 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11868 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11869 TEST_DrawTwinkleOnField(x, y);
11871 else if (element == EL_ACID)
11874 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11876 else if ((element == EL_EXIT_OPEN ||
11877 element == EL_EM_EXIT_OPEN ||
11878 element == EL_SP_EXIT_OPEN ||
11879 element == EL_STEEL_EXIT_OPEN ||
11880 element == EL_EM_STEEL_EXIT_OPEN ||
11881 element == EL_SP_TERMINAL ||
11882 element == EL_SP_TERMINAL_ACTIVE ||
11883 element == EL_EXTRA_TIME ||
11884 element == EL_SHIELD_NORMAL ||
11885 element == EL_SHIELD_DEADLY) &&
11886 IS_ANIMATED(graphic))
11887 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11888 else if (IS_MOVING(x, y))
11889 ContinueMoving(x, y);
11890 else if (IS_ACTIVE_BOMB(element))
11891 CheckDynamite(x, y);
11892 else if (element == EL_AMOEBA_GROWING)
11893 AmoebeWaechst(x, y);
11894 else if (element == EL_AMOEBA_SHRINKING)
11895 AmoebaDisappearing(x, y);
11897 #if !USE_NEW_AMOEBA_CODE
11898 else if (IS_AMOEBALIVE(element))
11899 AmoebeAbleger(x, y);
11902 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11904 else if (element == EL_EXIT_CLOSED)
11906 else if (element == EL_EM_EXIT_CLOSED)
11908 else if (element == EL_STEEL_EXIT_CLOSED)
11909 CheckExitSteel(x, y);
11910 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11911 CheckExitSteelEM(x, y);
11912 else if (element == EL_SP_EXIT_CLOSED)
11914 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11915 element == EL_EXPANDABLE_STEELWALL_GROWING)
11916 MauerWaechst(x, y);
11917 else if (element == EL_EXPANDABLE_WALL ||
11918 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11919 element == EL_EXPANDABLE_WALL_VERTICAL ||
11920 element == EL_EXPANDABLE_WALL_ANY ||
11921 element == EL_BD_EXPANDABLE_WALL)
11922 MauerAbleger(x, y);
11923 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11924 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11925 element == EL_EXPANDABLE_STEELWALL_ANY)
11926 MauerAblegerStahl(x, y);
11927 else if (element == EL_FLAMES)
11928 CheckForDragon(x, y);
11929 else if (element == EL_EXPLOSION)
11930 ; /* drawing of correct explosion animation is handled separately */
11931 else if (element == EL_ELEMENT_SNAPPING ||
11932 element == EL_DIAGONAL_SHRINKING ||
11933 element == EL_DIAGONAL_GROWING)
11935 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11937 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11939 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11940 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11942 if (IS_BELT_ACTIVE(element))
11943 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11945 if (game.magic_wall_active)
11947 int jx = local_player->jx, jy = local_player->jy;
11949 /* play the element sound at the position nearest to the player */
11950 if ((element == EL_MAGIC_WALL_FULL ||
11951 element == EL_MAGIC_WALL_ACTIVE ||
11952 element == EL_MAGIC_WALL_EMPTYING ||
11953 element == EL_BD_MAGIC_WALL_FULL ||
11954 element == EL_BD_MAGIC_WALL_ACTIVE ||
11955 element == EL_BD_MAGIC_WALL_EMPTYING ||
11956 element == EL_DC_MAGIC_WALL_FULL ||
11957 element == EL_DC_MAGIC_WALL_ACTIVE ||
11958 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11959 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11967 #if USE_NEW_AMOEBA_CODE
11968 /* new experimental amoeba growth stuff */
11969 if (!(FrameCounter % 8))
11971 static unsigned int random = 1684108901;
11973 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11975 x = RND(lev_fieldx);
11976 y = RND(lev_fieldy);
11977 element = Feld[x][y];
11979 if (!IS_PLAYER(x,y) &&
11980 (element == EL_EMPTY ||
11981 CAN_GROW_INTO(element) ||
11982 element == EL_QUICKSAND_EMPTY ||
11983 element == EL_QUICKSAND_FAST_EMPTY ||
11984 element == EL_ACID_SPLASH_LEFT ||
11985 element == EL_ACID_SPLASH_RIGHT))
11987 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11988 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11989 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11990 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11991 Feld[x][y] = EL_AMOEBA_DROP;
11994 random = random * 129 + 1;
11999 game.explosions_delayed = FALSE;
12001 SCAN_PLAYFIELD(x, y)
12003 element = Feld[x][y];
12005 if (ExplodeField[x][y])
12006 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12007 else if (element == EL_EXPLOSION)
12008 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12010 ExplodeField[x][y] = EX_TYPE_NONE;
12013 game.explosions_delayed = TRUE;
12015 if (game.magic_wall_active)
12017 if (!(game.magic_wall_time_left % 4))
12019 int element = Feld[magic_wall_x][magic_wall_y];
12021 if (element == EL_BD_MAGIC_WALL_FULL ||
12022 element == EL_BD_MAGIC_WALL_ACTIVE ||
12023 element == EL_BD_MAGIC_WALL_EMPTYING)
12024 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12025 else if (element == EL_DC_MAGIC_WALL_FULL ||
12026 element == EL_DC_MAGIC_WALL_ACTIVE ||
12027 element == EL_DC_MAGIC_WALL_EMPTYING)
12028 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12030 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12033 if (game.magic_wall_time_left > 0)
12035 game.magic_wall_time_left--;
12037 if (!game.magic_wall_time_left)
12039 SCAN_PLAYFIELD(x, y)
12041 element = Feld[x][y];
12043 if (element == EL_MAGIC_WALL_ACTIVE ||
12044 element == EL_MAGIC_WALL_FULL)
12046 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12047 TEST_DrawLevelField(x, y);
12049 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12050 element == EL_BD_MAGIC_WALL_FULL)
12052 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12053 TEST_DrawLevelField(x, y);
12055 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12056 element == EL_DC_MAGIC_WALL_FULL)
12058 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12059 TEST_DrawLevelField(x, y);
12063 game.magic_wall_active = FALSE;
12068 if (game.light_time_left > 0)
12070 game.light_time_left--;
12072 if (game.light_time_left == 0)
12073 RedrawAllLightSwitchesAndInvisibleElements();
12076 if (game.timegate_time_left > 0)
12078 game.timegate_time_left--;
12080 if (game.timegate_time_left == 0)
12081 CloseAllOpenTimegates();
12084 if (game.lenses_time_left > 0)
12086 game.lenses_time_left--;
12088 if (game.lenses_time_left == 0)
12089 RedrawAllInvisibleElementsForLenses();
12092 if (game.magnify_time_left > 0)
12094 game.magnify_time_left--;
12096 if (game.magnify_time_left == 0)
12097 RedrawAllInvisibleElementsForMagnifier();
12100 for (i = 0; i < MAX_PLAYERS; i++)
12102 struct PlayerInfo *player = &stored_player[i];
12104 if (SHIELD_ON(player))
12106 if (player->shield_deadly_time_left)
12107 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12108 else if (player->shield_normal_time_left)
12109 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12113 #if USE_DELAYED_GFX_REDRAW
12114 SCAN_PLAYFIELD(x, y)
12116 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12118 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12119 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12121 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12122 DrawLevelField(x, y);
12124 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12125 DrawLevelFieldCrumbled(x, y);
12127 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12128 DrawLevelFieldCrumbledNeighbours(x, y);
12130 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12131 DrawTwinkleOnField(x, y);
12134 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12139 PlayAllPlayersSound();
12141 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12143 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12145 local_player->show_envelope = 0;
12148 /* use random number generator in every frame to make it less predictable */
12149 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12153 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12155 int min_x = x, min_y = y, max_x = x, max_y = y;
12158 for (i = 0; i < MAX_PLAYERS; i++)
12160 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12162 if (!stored_player[i].active || &stored_player[i] == player)
12165 min_x = MIN(min_x, jx);
12166 min_y = MIN(min_y, jy);
12167 max_x = MAX(max_x, jx);
12168 max_y = MAX(max_y, jy);
12171 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12174 static boolean AllPlayersInVisibleScreen()
12178 for (i = 0; i < MAX_PLAYERS; i++)
12180 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12182 if (!stored_player[i].active)
12185 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12192 void ScrollLevel(int dx, int dy)
12194 int scroll_offset = 2 * TILEX_VAR;
12197 BlitBitmap(drawto_field, drawto_field,
12198 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12199 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12200 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12201 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12202 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12203 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12207 x = (dx == 1 ? BX1 : BX2);
12208 for (y = BY1; y <= BY2; y++)
12209 DrawScreenField(x, y);
12214 y = (dy == 1 ? BY1 : BY2);
12215 for (x = BX1; x <= BX2; x++)
12216 DrawScreenField(x, y);
12219 redraw_mask |= REDRAW_FIELD;
12222 static boolean canFallDown(struct PlayerInfo *player)
12224 int jx = player->jx, jy = player->jy;
12226 return (IN_LEV_FIELD(jx, jy + 1) &&
12227 (IS_FREE(jx, jy + 1) ||
12228 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12229 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12230 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12233 static boolean canPassField(int x, int y, int move_dir)
12235 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12236 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12237 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12238 int nextx = x + dx;
12239 int nexty = y + dy;
12240 int element = Feld[x][y];
12242 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12243 !CAN_MOVE(element) &&
12244 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12245 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12246 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12249 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12251 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12252 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12253 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12257 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12258 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12259 (IS_DIGGABLE(Feld[newx][newy]) ||
12260 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12261 canPassField(newx, newy, move_dir)));
12264 static void CheckGravityMovement(struct PlayerInfo *player)
12266 if (player->gravity && !player->programmed_action)
12268 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12269 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12270 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12271 int jx = player->jx, jy = player->jy;
12272 boolean player_is_moving_to_valid_field =
12273 (!player_is_snapping &&
12274 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12275 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12276 boolean player_can_fall_down = canFallDown(player);
12278 if (player_can_fall_down &&
12279 !player_is_moving_to_valid_field)
12280 player->programmed_action = MV_DOWN;
12284 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12286 return CheckGravityMovement(player);
12288 if (player->gravity && !player->programmed_action)
12290 int jx = player->jx, jy = player->jy;
12291 boolean field_under_player_is_free =
12292 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12293 boolean player_is_standing_on_valid_field =
12294 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12295 (IS_WALKABLE(Feld[jx][jy]) &&
12296 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12298 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12299 player->programmed_action = MV_DOWN;
12304 MovePlayerOneStep()
12305 -----------------------------------------------------------------------------
12306 dx, dy: direction (non-diagonal) to try to move the player to
12307 real_dx, real_dy: direction as read from input device (can be diagonal)
12310 boolean MovePlayerOneStep(struct PlayerInfo *player,
12311 int dx, int dy, int real_dx, int real_dy)
12313 int jx = player->jx, jy = player->jy;
12314 int new_jx = jx + dx, new_jy = jy + dy;
12316 boolean player_can_move = !player->cannot_move;
12318 if (!player->active || (!dx && !dy))
12319 return MP_NO_ACTION;
12321 player->MovDir = (dx < 0 ? MV_LEFT :
12322 dx > 0 ? MV_RIGHT :
12324 dy > 0 ? MV_DOWN : MV_NONE);
12326 if (!IN_LEV_FIELD(new_jx, new_jy))
12327 return MP_NO_ACTION;
12329 if (!player_can_move)
12331 if (player->MovPos == 0)
12333 player->is_moving = FALSE;
12334 player->is_digging = FALSE;
12335 player->is_collecting = FALSE;
12336 player->is_snapping = FALSE;
12337 player->is_pushing = FALSE;
12341 if (!options.network && game.centered_player_nr == -1 &&
12342 !AllPlayersInSight(player, new_jx, new_jy))
12343 return MP_NO_ACTION;
12345 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12346 if (can_move != MP_MOVING)
12349 /* check if DigField() has caused relocation of the player */
12350 if (player->jx != jx || player->jy != jy)
12351 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12353 StorePlayer[jx][jy] = 0;
12354 player->last_jx = jx;
12355 player->last_jy = jy;
12356 player->jx = new_jx;
12357 player->jy = new_jy;
12358 StorePlayer[new_jx][new_jy] = player->element_nr;
12360 if (player->move_delay_value_next != -1)
12362 player->move_delay_value = player->move_delay_value_next;
12363 player->move_delay_value_next = -1;
12367 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12369 player->step_counter++;
12371 PlayerVisit[jx][jy] = FrameCounter;
12373 player->is_moving = TRUE;
12376 /* should better be called in MovePlayer(), but this breaks some tapes */
12377 ScrollPlayer(player, SCROLL_INIT);
12383 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12385 int jx = player->jx, jy = player->jy;
12386 int old_jx = jx, old_jy = jy;
12387 int moved = MP_NO_ACTION;
12389 if (!player->active)
12394 if (player->MovPos == 0)
12396 player->is_moving = FALSE;
12397 player->is_digging = FALSE;
12398 player->is_collecting = FALSE;
12399 player->is_snapping = FALSE;
12400 player->is_pushing = FALSE;
12406 if (player->move_delay > 0)
12409 player->move_delay = -1; /* set to "uninitialized" value */
12411 /* store if player is automatically moved to next field */
12412 player->is_auto_moving = (player->programmed_action != MV_NONE);
12414 /* remove the last programmed player action */
12415 player->programmed_action = 0;
12417 if (player->MovPos)
12419 /* should only happen if pre-1.2 tape recordings are played */
12420 /* this is only for backward compatibility */
12422 int original_move_delay_value = player->move_delay_value;
12425 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12429 /* scroll remaining steps with finest movement resolution */
12430 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12432 while (player->MovPos)
12434 ScrollPlayer(player, SCROLL_GO_ON);
12435 ScrollScreen(NULL, SCROLL_GO_ON);
12437 AdvanceFrameAndPlayerCounters(player->index_nr);
12440 BackToFront_WithFrameDelay(0);
12443 player->move_delay_value = original_move_delay_value;
12446 player->is_active = FALSE;
12448 if (player->last_move_dir & MV_HORIZONTAL)
12450 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12451 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12455 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12456 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12459 if (!moved && !player->is_active)
12461 player->is_moving = FALSE;
12462 player->is_digging = FALSE;
12463 player->is_collecting = FALSE;
12464 player->is_snapping = FALSE;
12465 player->is_pushing = FALSE;
12471 if (moved & MP_MOVING && !ScreenMovPos &&
12472 (player->index_nr == game.centered_player_nr ||
12473 game.centered_player_nr == -1))
12475 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12476 int offset = game.scroll_delay_value;
12478 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12480 /* actual player has left the screen -- scroll in that direction */
12481 if (jx != old_jx) /* player has moved horizontally */
12482 scroll_x += (jx - old_jx);
12483 else /* player has moved vertically */
12484 scroll_y += (jy - old_jy);
12488 if (jx != old_jx) /* player has moved horizontally */
12490 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12491 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12492 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12494 /* don't scroll over playfield boundaries */
12495 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12496 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12498 /* don't scroll more than one field at a time */
12499 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12501 /* don't scroll against the player's moving direction */
12502 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12503 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12504 scroll_x = old_scroll_x;
12506 else /* player has moved vertically */
12508 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12509 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12510 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12512 /* don't scroll over playfield boundaries */
12513 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12514 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12516 /* don't scroll more than one field at a time */
12517 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12519 /* don't scroll against the player's moving direction */
12520 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12521 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12522 scroll_y = old_scroll_y;
12526 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12528 if (!options.network && game.centered_player_nr == -1 &&
12529 !AllPlayersInVisibleScreen())
12531 scroll_x = old_scroll_x;
12532 scroll_y = old_scroll_y;
12536 ScrollScreen(player, SCROLL_INIT);
12537 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12542 player->StepFrame = 0;
12544 if (moved & MP_MOVING)
12546 if (old_jx != jx && old_jy == jy)
12547 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12548 else if (old_jx == jx && old_jy != jy)
12549 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12551 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12553 player->last_move_dir = player->MovDir;
12554 player->is_moving = TRUE;
12555 player->is_snapping = FALSE;
12556 player->is_switching = FALSE;
12557 player->is_dropping = FALSE;
12558 player->is_dropping_pressed = FALSE;
12559 player->drop_pressed_delay = 0;
12562 /* should better be called here than above, but this breaks some tapes */
12563 ScrollPlayer(player, SCROLL_INIT);
12568 CheckGravityMovementWhenNotMoving(player);
12570 player->is_moving = FALSE;
12572 /* at this point, the player is allowed to move, but cannot move right now
12573 (e.g. because of something blocking the way) -- ensure that the player
12574 is also allowed to move in the next frame (in old versions before 3.1.1,
12575 the player was forced to wait again for eight frames before next try) */
12577 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12578 player->move_delay = 0; /* allow direct movement in the next frame */
12581 if (player->move_delay == -1) /* not yet initialized by DigField() */
12582 player->move_delay = player->move_delay_value;
12584 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12586 TestIfPlayerTouchesBadThing(jx, jy);
12587 TestIfPlayerTouchesCustomElement(jx, jy);
12590 if (!player->active)
12591 RemovePlayer(player);
12596 void ScrollPlayer(struct PlayerInfo *player, int mode)
12598 int jx = player->jx, jy = player->jy;
12599 int last_jx = player->last_jx, last_jy = player->last_jy;
12600 int move_stepsize = TILEX / player->move_delay_value;
12602 if (!player->active)
12605 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12608 if (mode == SCROLL_INIT)
12610 player->actual_frame_counter = FrameCounter;
12611 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12613 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12614 Feld[last_jx][last_jy] == EL_EMPTY)
12616 int last_field_block_delay = 0; /* start with no blocking at all */
12617 int block_delay_adjustment = player->block_delay_adjustment;
12619 /* if player blocks last field, add delay for exactly one move */
12620 if (player->block_last_field)
12622 last_field_block_delay += player->move_delay_value;
12624 /* when blocking enabled, prevent moving up despite gravity */
12625 if (player->gravity && player->MovDir == MV_UP)
12626 block_delay_adjustment = -1;
12629 /* add block delay adjustment (also possible when not blocking) */
12630 last_field_block_delay += block_delay_adjustment;
12632 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12633 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12636 if (player->MovPos != 0) /* player has not yet reached destination */
12639 else if (!FrameReached(&player->actual_frame_counter, 1))
12642 if (player->MovPos != 0)
12644 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12645 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12647 /* before DrawPlayer() to draw correct player graphic for this case */
12648 if (player->MovPos == 0)
12649 CheckGravityMovement(player);
12652 if (player->MovPos == 0) /* player reached destination field */
12654 if (player->move_delay_reset_counter > 0)
12656 player->move_delay_reset_counter--;
12658 if (player->move_delay_reset_counter == 0)
12660 /* continue with normal speed after quickly moving through gate */
12661 HALVE_PLAYER_SPEED(player);
12663 /* be able to make the next move without delay */
12664 player->move_delay = 0;
12668 player->last_jx = jx;
12669 player->last_jy = jy;
12671 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12672 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12673 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12674 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12675 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12676 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12677 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12678 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12680 DrawPlayer(player); /* needed here only to cleanup last field */
12681 RemovePlayer(player);
12683 if (local_player->friends_still_needed == 0 ||
12684 IS_SP_ELEMENT(Feld[jx][jy]))
12685 PlayerWins(player);
12688 /* this breaks one level: "machine", level 000 */
12690 int move_direction = player->MovDir;
12691 int enter_side = MV_DIR_OPPOSITE(move_direction);
12692 int leave_side = move_direction;
12693 int old_jx = last_jx;
12694 int old_jy = last_jy;
12695 int old_element = Feld[old_jx][old_jy];
12696 int new_element = Feld[jx][jy];
12698 if (IS_CUSTOM_ELEMENT(old_element))
12699 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12701 player->index_bit, leave_side);
12703 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12704 CE_PLAYER_LEAVES_X,
12705 player->index_bit, leave_side);
12707 if (IS_CUSTOM_ELEMENT(new_element))
12708 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12709 player->index_bit, enter_side);
12711 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12712 CE_PLAYER_ENTERS_X,
12713 player->index_bit, enter_side);
12715 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12716 CE_MOVE_OF_X, move_direction);
12719 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12721 TestIfPlayerTouchesBadThing(jx, jy);
12722 TestIfPlayerTouchesCustomElement(jx, jy);
12724 /* needed because pushed element has not yet reached its destination,
12725 so it would trigger a change event at its previous field location */
12726 if (!player->is_pushing)
12727 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12729 if (!player->active)
12730 RemovePlayer(player);
12733 if (!local_player->LevelSolved && level.use_step_counter)
12743 if (TimeLeft <= 10 && setup.time_limit)
12744 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12746 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12748 DisplayGameControlValues();
12750 if (!TimeLeft && setup.time_limit)
12751 for (i = 0; i < MAX_PLAYERS; i++)
12752 KillPlayer(&stored_player[i]);
12754 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12756 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12758 DisplayGameControlValues();
12762 if (tape.single_step && tape.recording && !tape.pausing &&
12763 !player->programmed_action)
12764 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12766 if (!player->programmed_action)
12767 CheckSaveEngineSnapshot(player);
12771 void ScrollScreen(struct PlayerInfo *player, int mode)
12773 static unsigned int screen_frame_counter = 0;
12775 if (mode == SCROLL_INIT)
12777 /* set scrolling step size according to actual player's moving speed */
12778 ScrollStepSize = TILEX / player->move_delay_value;
12780 screen_frame_counter = FrameCounter;
12781 ScreenMovDir = player->MovDir;
12782 ScreenMovPos = player->MovPos;
12783 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12786 else if (!FrameReached(&screen_frame_counter, 1))
12791 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12792 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12793 redraw_mask |= REDRAW_FIELD;
12796 ScreenMovDir = MV_NONE;
12799 void TestIfPlayerTouchesCustomElement(int x, int y)
12801 static int xy[4][2] =
12808 static int trigger_sides[4][2] =
12810 /* center side border side */
12811 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12812 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12813 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12814 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12816 static int touch_dir[4] =
12818 MV_LEFT | MV_RIGHT,
12823 int center_element = Feld[x][y]; /* should always be non-moving! */
12826 for (i = 0; i < NUM_DIRECTIONS; i++)
12828 int xx = x + xy[i][0];
12829 int yy = y + xy[i][1];
12830 int center_side = trigger_sides[i][0];
12831 int border_side = trigger_sides[i][1];
12832 int border_element;
12834 if (!IN_LEV_FIELD(xx, yy))
12837 if (IS_PLAYER(x, y)) /* player found at center element */
12839 struct PlayerInfo *player = PLAYERINFO(x, y);
12841 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12842 border_element = Feld[xx][yy]; /* may be moving! */
12843 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12844 border_element = Feld[xx][yy];
12845 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12846 border_element = MovingOrBlocked2Element(xx, yy);
12848 continue; /* center and border element do not touch */
12850 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12851 player->index_bit, border_side);
12852 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12853 CE_PLAYER_TOUCHES_X,
12854 player->index_bit, border_side);
12857 /* use player element that is initially defined in the level playfield,
12858 not the player element that corresponds to the runtime player number
12859 (example: a level that contains EL_PLAYER_3 as the only player would
12860 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12861 int player_element = PLAYERINFO(x, y)->initial_element;
12863 CheckElementChangeBySide(xx, yy, border_element, player_element,
12864 CE_TOUCHING_X, border_side);
12867 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12869 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12871 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12873 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12874 continue; /* center and border element do not touch */
12877 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12878 player->index_bit, center_side);
12879 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12880 CE_PLAYER_TOUCHES_X,
12881 player->index_bit, center_side);
12884 /* use player element that is initially defined in the level playfield,
12885 not the player element that corresponds to the runtime player number
12886 (example: a level that contains EL_PLAYER_3 as the only player would
12887 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12888 int player_element = PLAYERINFO(xx, yy)->initial_element;
12890 CheckElementChangeBySide(x, y, center_element, player_element,
12891 CE_TOUCHING_X, center_side);
12899 void TestIfElementTouchesCustomElement(int x, int y)
12901 static int xy[4][2] =
12908 static int trigger_sides[4][2] =
12910 /* center side border side */
12911 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12912 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12913 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12914 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12916 static int touch_dir[4] =
12918 MV_LEFT | MV_RIGHT,
12923 boolean change_center_element = FALSE;
12924 int center_element = Feld[x][y]; /* should always be non-moving! */
12925 int border_element_old[NUM_DIRECTIONS];
12928 for (i = 0; i < NUM_DIRECTIONS; i++)
12930 int xx = x + xy[i][0];
12931 int yy = y + xy[i][1];
12932 int border_element;
12934 border_element_old[i] = -1;
12936 if (!IN_LEV_FIELD(xx, yy))
12939 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12940 border_element = Feld[xx][yy]; /* may be moving! */
12941 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12942 border_element = Feld[xx][yy];
12943 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12944 border_element = MovingOrBlocked2Element(xx, yy);
12946 continue; /* center and border element do not touch */
12948 border_element_old[i] = border_element;
12951 for (i = 0; i < NUM_DIRECTIONS; i++)
12953 int xx = x + xy[i][0];
12954 int yy = y + xy[i][1];
12955 int center_side = trigger_sides[i][0];
12956 int border_element = border_element_old[i];
12958 if (border_element == -1)
12961 /* check for change of border element */
12962 CheckElementChangeBySide(xx, yy, border_element, center_element,
12963 CE_TOUCHING_X, center_side);
12965 /* (center element cannot be player, so we dont have to check this here) */
12968 for (i = 0; i < NUM_DIRECTIONS; i++)
12970 int xx = x + xy[i][0];
12971 int yy = y + xy[i][1];
12972 int border_side = trigger_sides[i][1];
12973 int border_element = border_element_old[i];
12975 if (border_element == -1)
12978 /* check for change of center element (but change it only once) */
12979 if (!change_center_element)
12980 change_center_element =
12981 CheckElementChangeBySide(x, y, center_element, border_element,
12982 CE_TOUCHING_X, border_side);
12984 if (IS_PLAYER(xx, yy))
12986 /* use player element that is initially defined in the level playfield,
12987 not the player element that corresponds to the runtime player number
12988 (example: a level that contains EL_PLAYER_3 as the only player would
12989 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12990 int player_element = PLAYERINFO(xx, yy)->initial_element;
12992 CheckElementChangeBySide(x, y, center_element, player_element,
12993 CE_TOUCHING_X, border_side);
12998 void TestIfElementHitsCustomElement(int x, int y, int direction)
13000 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13001 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13002 int hitx = x + dx, hity = y + dy;
13003 int hitting_element = Feld[x][y];
13004 int touched_element;
13006 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13009 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13010 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13012 if (IN_LEV_FIELD(hitx, hity))
13014 int opposite_direction = MV_DIR_OPPOSITE(direction);
13015 int hitting_side = direction;
13016 int touched_side = opposite_direction;
13017 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13018 MovDir[hitx][hity] != direction ||
13019 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13025 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13026 CE_HITTING_X, touched_side);
13028 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13029 CE_HIT_BY_X, hitting_side);
13031 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13032 CE_HIT_BY_SOMETHING, opposite_direction);
13034 if (IS_PLAYER(hitx, hity))
13036 /* use player element that is initially defined in the level playfield,
13037 not the player element that corresponds to the runtime player number
13038 (example: a level that contains EL_PLAYER_3 as the only player would
13039 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13040 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13042 CheckElementChangeBySide(x, y, hitting_element, player_element,
13043 CE_HITTING_X, touched_side);
13048 /* "hitting something" is also true when hitting the playfield border */
13049 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13050 CE_HITTING_SOMETHING, direction);
13053 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13055 int i, kill_x = -1, kill_y = -1;
13057 int bad_element = -1;
13058 static int test_xy[4][2] =
13065 static int test_dir[4] =
13073 for (i = 0; i < NUM_DIRECTIONS; i++)
13075 int test_x, test_y, test_move_dir, test_element;
13077 test_x = good_x + test_xy[i][0];
13078 test_y = good_y + test_xy[i][1];
13080 if (!IN_LEV_FIELD(test_x, test_y))
13084 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13086 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13088 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13089 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13091 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13092 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13096 bad_element = test_element;
13102 if (kill_x != -1 || kill_y != -1)
13104 if (IS_PLAYER(good_x, good_y))
13106 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13108 if (player->shield_deadly_time_left > 0 &&
13109 !IS_INDESTRUCTIBLE(bad_element))
13110 Bang(kill_x, kill_y);
13111 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13112 KillPlayer(player);
13115 Bang(good_x, good_y);
13119 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13121 int i, kill_x = -1, kill_y = -1;
13122 int bad_element = Feld[bad_x][bad_y];
13123 static int test_xy[4][2] =
13130 static int touch_dir[4] =
13132 MV_LEFT | MV_RIGHT,
13137 static int test_dir[4] =
13145 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13148 for (i = 0; i < NUM_DIRECTIONS; i++)
13150 int test_x, test_y, test_move_dir, test_element;
13152 test_x = bad_x + test_xy[i][0];
13153 test_y = bad_y + test_xy[i][1];
13155 if (!IN_LEV_FIELD(test_x, test_y))
13159 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13161 test_element = Feld[test_x][test_y];
13163 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13164 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13166 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13167 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13169 /* good thing is player or penguin that does not move away */
13170 if (IS_PLAYER(test_x, test_y))
13172 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13174 if (bad_element == EL_ROBOT && player->is_moving)
13175 continue; /* robot does not kill player if he is moving */
13177 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13179 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13180 continue; /* center and border element do not touch */
13188 else if (test_element == EL_PENGUIN)
13198 if (kill_x != -1 || kill_y != -1)
13200 if (IS_PLAYER(kill_x, kill_y))
13202 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13204 if (player->shield_deadly_time_left > 0 &&
13205 !IS_INDESTRUCTIBLE(bad_element))
13206 Bang(bad_x, bad_y);
13207 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13208 KillPlayer(player);
13211 Bang(kill_x, kill_y);
13215 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13217 int bad_element = Feld[bad_x][bad_y];
13218 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13219 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13220 int test_x = bad_x + dx, test_y = bad_y + dy;
13221 int test_move_dir, test_element;
13222 int kill_x = -1, kill_y = -1;
13224 if (!IN_LEV_FIELD(test_x, test_y))
13228 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13230 test_element = Feld[test_x][test_y];
13232 if (test_move_dir != bad_move_dir)
13234 /* good thing can be player or penguin that does not move away */
13235 if (IS_PLAYER(test_x, test_y))
13237 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13239 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13240 player as being hit when he is moving towards the bad thing, because
13241 the "get hit by" condition would be lost after the player stops) */
13242 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13243 return; /* player moves away from bad thing */
13248 else if (test_element == EL_PENGUIN)
13255 if (kill_x != -1 || kill_y != -1)
13257 if (IS_PLAYER(kill_x, kill_y))
13259 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13261 if (player->shield_deadly_time_left > 0 &&
13262 !IS_INDESTRUCTIBLE(bad_element))
13263 Bang(bad_x, bad_y);
13264 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13265 KillPlayer(player);
13268 Bang(kill_x, kill_y);
13272 void TestIfPlayerTouchesBadThing(int x, int y)
13274 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13277 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13279 TestIfGoodThingHitsBadThing(x, y, move_dir);
13282 void TestIfBadThingTouchesPlayer(int x, int y)
13284 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13287 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13289 TestIfBadThingHitsGoodThing(x, y, move_dir);
13292 void TestIfFriendTouchesBadThing(int x, int y)
13294 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13297 void TestIfBadThingTouchesFriend(int x, int y)
13299 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13302 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13304 int i, kill_x = bad_x, kill_y = bad_y;
13305 static int xy[4][2] =
13313 for (i = 0; i < NUM_DIRECTIONS; i++)
13317 x = bad_x + xy[i][0];
13318 y = bad_y + xy[i][1];
13319 if (!IN_LEV_FIELD(x, y))
13322 element = Feld[x][y];
13323 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13324 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13332 if (kill_x != bad_x || kill_y != bad_y)
13333 Bang(bad_x, bad_y);
13336 void KillPlayer(struct PlayerInfo *player)
13338 int jx = player->jx, jy = player->jy;
13340 if (!player->active)
13344 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13345 player->killed, player->active, player->reanimated);
13348 /* the following code was introduced to prevent an infinite loop when calling
13350 -> CheckTriggeredElementChangeExt()
13351 -> ExecuteCustomElementAction()
13353 -> (infinitely repeating the above sequence of function calls)
13354 which occurs when killing the player while having a CE with the setting
13355 "kill player X when explosion of <player X>"; the solution using a new
13356 field "player->killed" was chosen for backwards compatibility, although
13357 clever use of the fields "player->active" etc. would probably also work */
13359 if (player->killed)
13363 player->killed = TRUE;
13365 /* remove accessible field at the player's position */
13366 Feld[jx][jy] = EL_EMPTY;
13368 /* deactivate shield (else Bang()/Explode() would not work right) */
13369 player->shield_normal_time_left = 0;
13370 player->shield_deadly_time_left = 0;
13373 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13374 player->killed, player->active, player->reanimated);
13380 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13381 player->killed, player->active, player->reanimated);
13384 if (player->reanimated) /* killed player may have been reanimated */
13385 player->killed = player->reanimated = FALSE;
13387 BuryPlayer(player);
13390 static void KillPlayerUnlessEnemyProtected(int x, int y)
13392 if (!PLAYER_ENEMY_PROTECTED(x, y))
13393 KillPlayer(PLAYERINFO(x, y));
13396 static void KillPlayerUnlessExplosionProtected(int x, int y)
13398 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13399 KillPlayer(PLAYERINFO(x, y));
13402 void BuryPlayer(struct PlayerInfo *player)
13404 int jx = player->jx, jy = player->jy;
13406 if (!player->active)
13409 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13410 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13412 player->GameOver = TRUE;
13413 RemovePlayer(player);
13416 void RemovePlayer(struct PlayerInfo *player)
13418 int jx = player->jx, jy = player->jy;
13419 int i, found = FALSE;
13421 player->present = FALSE;
13422 player->active = FALSE;
13424 if (!ExplodeField[jx][jy])
13425 StorePlayer[jx][jy] = 0;
13427 if (player->is_moving)
13428 TEST_DrawLevelField(player->last_jx, player->last_jy);
13430 for (i = 0; i < MAX_PLAYERS; i++)
13431 if (stored_player[i].active)
13435 AllPlayersGone = TRUE;
13441 static void setFieldForSnapping(int x, int y, int element, int direction)
13443 struct ElementInfo *ei = &element_info[element];
13444 int direction_bit = MV_DIR_TO_BIT(direction);
13445 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13446 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13447 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13449 Feld[x][y] = EL_ELEMENT_SNAPPING;
13450 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13452 ResetGfxAnimation(x, y);
13454 GfxElement[x][y] = element;
13455 GfxAction[x][y] = action;
13456 GfxDir[x][y] = direction;
13457 GfxFrame[x][y] = -1;
13461 =============================================================================
13462 checkDiagonalPushing()
13463 -----------------------------------------------------------------------------
13464 check if diagonal input device direction results in pushing of object
13465 (by checking if the alternative direction is walkable, diggable, ...)
13466 =============================================================================
13469 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13470 int x, int y, int real_dx, int real_dy)
13472 int jx, jy, dx, dy, xx, yy;
13474 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13477 /* diagonal direction: check alternative direction */
13482 xx = jx + (dx == 0 ? real_dx : 0);
13483 yy = jy + (dy == 0 ? real_dy : 0);
13485 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13489 =============================================================================
13491 -----------------------------------------------------------------------------
13492 x, y: field next to player (non-diagonal) to try to dig to
13493 real_dx, real_dy: direction as read from input device (can be diagonal)
13494 =============================================================================
13497 static int DigField(struct PlayerInfo *player,
13498 int oldx, int oldy, int x, int y,
13499 int real_dx, int real_dy, int mode)
13501 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13502 boolean player_was_pushing = player->is_pushing;
13503 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13504 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13505 int jx = oldx, jy = oldy;
13506 int dx = x - jx, dy = y - jy;
13507 int nextx = x + dx, nexty = y + dy;
13508 int move_direction = (dx == -1 ? MV_LEFT :
13509 dx == +1 ? MV_RIGHT :
13511 dy == +1 ? MV_DOWN : MV_NONE);
13512 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13513 int dig_side = MV_DIR_OPPOSITE(move_direction);
13514 int old_element = Feld[jx][jy];
13515 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13518 if (is_player) /* function can also be called by EL_PENGUIN */
13520 if (player->MovPos == 0)
13522 player->is_digging = FALSE;
13523 player->is_collecting = FALSE;
13526 if (player->MovPos == 0) /* last pushing move finished */
13527 player->is_pushing = FALSE;
13529 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13531 player->is_switching = FALSE;
13532 player->push_delay = -1;
13534 return MP_NO_ACTION;
13538 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13539 old_element = Back[jx][jy];
13541 /* in case of element dropped at player position, check background */
13542 else if (Back[jx][jy] != EL_EMPTY &&
13543 game.engine_version >= VERSION_IDENT(2,2,0,0))
13544 old_element = Back[jx][jy];
13546 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13547 return MP_NO_ACTION; /* field has no opening in this direction */
13549 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13550 return MP_NO_ACTION; /* field has no opening in this direction */
13552 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13556 Feld[jx][jy] = player->artwork_element;
13557 InitMovingField(jx, jy, MV_DOWN);
13558 Store[jx][jy] = EL_ACID;
13559 ContinueMoving(jx, jy);
13560 BuryPlayer(player);
13562 return MP_DONT_RUN_INTO;
13565 if (player_can_move && DONT_RUN_INTO(element))
13567 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13569 return MP_DONT_RUN_INTO;
13572 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13573 return MP_NO_ACTION;
13575 collect_count = element_info[element].collect_count_initial;
13577 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13578 return MP_NO_ACTION;
13580 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13581 player_can_move = player_can_move_or_snap;
13583 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13584 game.engine_version >= VERSION_IDENT(2,2,0,0))
13586 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13587 player->index_bit, dig_side);
13588 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13589 player->index_bit, dig_side);
13591 if (element == EL_DC_LANDMINE)
13594 if (Feld[x][y] != element) /* field changed by snapping */
13597 return MP_NO_ACTION;
13600 if (player->gravity && is_player && !player->is_auto_moving &&
13601 canFallDown(player) && move_direction != MV_DOWN &&
13602 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13603 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13605 if (player_can_move &&
13606 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13608 int sound_element = SND_ELEMENT(element);
13609 int sound_action = ACTION_WALKING;
13611 if (IS_RND_GATE(element))
13613 if (!player->key[RND_GATE_NR(element)])
13614 return MP_NO_ACTION;
13616 else if (IS_RND_GATE_GRAY(element))
13618 if (!player->key[RND_GATE_GRAY_NR(element)])
13619 return MP_NO_ACTION;
13621 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13623 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13624 return MP_NO_ACTION;
13626 else if (element == EL_EXIT_OPEN ||
13627 element == EL_EM_EXIT_OPEN ||
13628 element == EL_EM_EXIT_OPENING ||
13629 element == EL_STEEL_EXIT_OPEN ||
13630 element == EL_EM_STEEL_EXIT_OPEN ||
13631 element == EL_EM_STEEL_EXIT_OPENING ||
13632 element == EL_SP_EXIT_OPEN ||
13633 element == EL_SP_EXIT_OPENING)
13635 sound_action = ACTION_PASSING; /* player is passing exit */
13637 else if (element == EL_EMPTY)
13639 sound_action = ACTION_MOVING; /* nothing to walk on */
13642 /* play sound from background or player, whatever is available */
13643 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13644 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13646 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13648 else if (player_can_move &&
13649 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13651 if (!ACCESS_FROM(element, opposite_direction))
13652 return MP_NO_ACTION; /* field not accessible from this direction */
13654 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13655 return MP_NO_ACTION;
13657 if (IS_EM_GATE(element))
13659 if (!player->key[EM_GATE_NR(element)])
13660 return MP_NO_ACTION;
13662 else if (IS_EM_GATE_GRAY(element))
13664 if (!player->key[EM_GATE_GRAY_NR(element)])
13665 return MP_NO_ACTION;
13667 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13669 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13670 return MP_NO_ACTION;
13672 else if (IS_EMC_GATE(element))
13674 if (!player->key[EMC_GATE_NR(element)])
13675 return MP_NO_ACTION;
13677 else if (IS_EMC_GATE_GRAY(element))
13679 if (!player->key[EMC_GATE_GRAY_NR(element)])
13680 return MP_NO_ACTION;
13682 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13684 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13685 return MP_NO_ACTION;
13687 else if (element == EL_DC_GATE_WHITE ||
13688 element == EL_DC_GATE_WHITE_GRAY ||
13689 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13691 if (player->num_white_keys == 0)
13692 return MP_NO_ACTION;
13694 player->num_white_keys--;
13696 else if (IS_SP_PORT(element))
13698 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13699 element == EL_SP_GRAVITY_PORT_RIGHT ||
13700 element == EL_SP_GRAVITY_PORT_UP ||
13701 element == EL_SP_GRAVITY_PORT_DOWN)
13702 player->gravity = !player->gravity;
13703 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13704 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13705 element == EL_SP_GRAVITY_ON_PORT_UP ||
13706 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13707 player->gravity = TRUE;
13708 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13709 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13710 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13711 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13712 player->gravity = FALSE;
13715 /* automatically move to the next field with double speed */
13716 player->programmed_action = move_direction;
13718 if (player->move_delay_reset_counter == 0)
13720 player->move_delay_reset_counter = 2; /* two double speed steps */
13722 DOUBLE_PLAYER_SPEED(player);
13725 PlayLevelSoundAction(x, y, ACTION_PASSING);
13727 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13731 if (mode != DF_SNAP)
13733 GfxElement[x][y] = GFX_ELEMENT(element);
13734 player->is_digging = TRUE;
13737 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13739 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13740 player->index_bit, dig_side);
13742 if (mode == DF_SNAP)
13744 if (level.block_snap_field)
13745 setFieldForSnapping(x, y, element, move_direction);
13747 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13749 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13750 player->index_bit, dig_side);
13753 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13757 if (is_player && mode != DF_SNAP)
13759 GfxElement[x][y] = element;
13760 player->is_collecting = TRUE;
13763 if (element == EL_SPEED_PILL)
13765 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13767 else if (element == EL_EXTRA_TIME && level.time > 0)
13769 TimeLeft += level.extra_time;
13771 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13773 DisplayGameControlValues();
13775 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13777 player->shield_normal_time_left += level.shield_normal_time;
13778 if (element == EL_SHIELD_DEADLY)
13779 player->shield_deadly_time_left += level.shield_deadly_time;
13781 else if (element == EL_DYNAMITE ||
13782 element == EL_EM_DYNAMITE ||
13783 element == EL_SP_DISK_RED)
13785 if (player->inventory_size < MAX_INVENTORY_SIZE)
13786 player->inventory_element[player->inventory_size++] = element;
13788 DrawGameDoorValues();
13790 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13792 player->dynabomb_count++;
13793 player->dynabombs_left++;
13795 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13797 player->dynabomb_size++;
13799 else if (element == EL_DYNABOMB_INCREASE_POWER)
13801 player->dynabomb_xl = TRUE;
13803 else if (IS_KEY(element))
13805 player->key[KEY_NR(element)] = TRUE;
13807 DrawGameDoorValues();
13809 else if (element == EL_DC_KEY_WHITE)
13811 player->num_white_keys++;
13813 /* display white keys? */
13814 /* DrawGameDoorValues(); */
13816 else if (IS_ENVELOPE(element))
13818 player->show_envelope = element;
13820 else if (element == EL_EMC_LENSES)
13822 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13824 RedrawAllInvisibleElementsForLenses();
13826 else if (element == EL_EMC_MAGNIFIER)
13828 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13830 RedrawAllInvisibleElementsForMagnifier();
13832 else if (IS_DROPPABLE(element) ||
13833 IS_THROWABLE(element)) /* can be collected and dropped */
13837 if (collect_count == 0)
13838 player->inventory_infinite_element = element;
13840 for (i = 0; i < collect_count; i++)
13841 if (player->inventory_size < MAX_INVENTORY_SIZE)
13842 player->inventory_element[player->inventory_size++] = element;
13844 DrawGameDoorValues();
13846 else if (collect_count > 0)
13848 local_player->gems_still_needed -= collect_count;
13849 if (local_player->gems_still_needed < 0)
13850 local_player->gems_still_needed = 0;
13852 game.snapshot.collected_item = TRUE;
13854 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13856 DisplayGameControlValues();
13859 RaiseScoreElement(element);
13860 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13863 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13864 player->index_bit, dig_side);
13866 if (mode == DF_SNAP)
13868 if (level.block_snap_field)
13869 setFieldForSnapping(x, y, element, move_direction);
13871 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13873 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13874 player->index_bit, dig_side);
13877 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13879 if (mode == DF_SNAP && element != EL_BD_ROCK)
13880 return MP_NO_ACTION;
13882 if (CAN_FALL(element) && dy)
13883 return MP_NO_ACTION;
13885 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13886 !(element == EL_SPRING && level.use_spring_bug))
13887 return MP_NO_ACTION;
13889 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13890 ((move_direction & MV_VERTICAL &&
13891 ((element_info[element].move_pattern & MV_LEFT &&
13892 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13893 (element_info[element].move_pattern & MV_RIGHT &&
13894 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13895 (move_direction & MV_HORIZONTAL &&
13896 ((element_info[element].move_pattern & MV_UP &&
13897 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13898 (element_info[element].move_pattern & MV_DOWN &&
13899 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13900 return MP_NO_ACTION;
13902 /* do not push elements already moving away faster than player */
13903 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13904 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13905 return MP_NO_ACTION;
13907 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13909 if (player->push_delay_value == -1 || !player_was_pushing)
13910 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13912 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13914 if (player->push_delay_value == -1)
13915 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13917 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13919 if (!player->is_pushing)
13920 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13923 player->is_pushing = TRUE;
13924 player->is_active = TRUE;
13926 if (!(IN_LEV_FIELD(nextx, nexty) &&
13927 (IS_FREE(nextx, nexty) ||
13928 (IS_SB_ELEMENT(element) &&
13929 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13930 (IS_CUSTOM_ELEMENT(element) &&
13931 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13932 return MP_NO_ACTION;
13934 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13935 return MP_NO_ACTION;
13937 if (player->push_delay == -1) /* new pushing; restart delay */
13938 player->push_delay = 0;
13940 if (player->push_delay < player->push_delay_value &&
13941 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13942 element != EL_SPRING && element != EL_BALLOON)
13944 /* make sure that there is no move delay before next try to push */
13945 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13946 player->move_delay = 0;
13948 return MP_NO_ACTION;
13951 if (IS_CUSTOM_ELEMENT(element) &&
13952 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13954 if (!DigFieldByCE(nextx, nexty, element))
13955 return MP_NO_ACTION;
13958 if (IS_SB_ELEMENT(element))
13960 if (element == EL_SOKOBAN_FIELD_FULL)
13962 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13963 local_player->sokobanfields_still_needed++;
13966 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13968 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13969 local_player->sokobanfields_still_needed--;
13972 Feld[x][y] = EL_SOKOBAN_OBJECT;
13974 if (Back[x][y] == Back[nextx][nexty])
13975 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13976 else if (Back[x][y] != 0)
13977 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13980 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13983 if (local_player->sokobanfields_still_needed == 0 &&
13984 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13986 PlayerWins(player);
13988 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13992 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13994 InitMovingField(x, y, move_direction);
13995 GfxAction[x][y] = ACTION_PUSHING;
13997 if (mode == DF_SNAP)
13998 ContinueMoving(x, y);
14000 MovPos[x][y] = (dx != 0 ? dx : dy);
14002 Pushed[x][y] = TRUE;
14003 Pushed[nextx][nexty] = TRUE;
14005 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14006 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14008 player->push_delay_value = -1; /* get new value later */
14010 /* check for element change _after_ element has been pushed */
14011 if (game.use_change_when_pushing_bug)
14013 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14014 player->index_bit, dig_side);
14015 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14016 player->index_bit, dig_side);
14019 else if (IS_SWITCHABLE(element))
14021 if (PLAYER_SWITCHING(player, x, y))
14023 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14024 player->index_bit, dig_side);
14029 player->is_switching = TRUE;
14030 player->switch_x = x;
14031 player->switch_y = y;
14033 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14035 if (element == EL_ROBOT_WHEEL)
14037 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14041 game.robot_wheel_active = TRUE;
14043 TEST_DrawLevelField(x, y);
14045 else if (element == EL_SP_TERMINAL)
14049 SCAN_PLAYFIELD(xx, yy)
14051 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14055 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14057 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14059 ResetGfxAnimation(xx, yy);
14060 TEST_DrawLevelField(xx, yy);
14064 else if (IS_BELT_SWITCH(element))
14066 ToggleBeltSwitch(x, y);
14068 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14069 element == EL_SWITCHGATE_SWITCH_DOWN ||
14070 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14071 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14073 ToggleSwitchgateSwitch(x, y);
14075 else if (element == EL_LIGHT_SWITCH ||
14076 element == EL_LIGHT_SWITCH_ACTIVE)
14078 ToggleLightSwitch(x, y);
14080 else if (element == EL_TIMEGATE_SWITCH ||
14081 element == EL_DC_TIMEGATE_SWITCH)
14083 ActivateTimegateSwitch(x, y);
14085 else if (element == EL_BALLOON_SWITCH_LEFT ||
14086 element == EL_BALLOON_SWITCH_RIGHT ||
14087 element == EL_BALLOON_SWITCH_UP ||
14088 element == EL_BALLOON_SWITCH_DOWN ||
14089 element == EL_BALLOON_SWITCH_NONE ||
14090 element == EL_BALLOON_SWITCH_ANY)
14092 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14093 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14094 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14095 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14096 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14099 else if (element == EL_LAMP)
14101 Feld[x][y] = EL_LAMP_ACTIVE;
14102 local_player->lights_still_needed--;
14104 ResetGfxAnimation(x, y);
14105 TEST_DrawLevelField(x, y);
14107 else if (element == EL_TIME_ORB_FULL)
14109 Feld[x][y] = EL_TIME_ORB_EMPTY;
14111 if (level.time > 0 || level.use_time_orb_bug)
14113 TimeLeft += level.time_orb_time;
14114 game.no_time_limit = FALSE;
14116 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14118 DisplayGameControlValues();
14121 ResetGfxAnimation(x, y);
14122 TEST_DrawLevelField(x, y);
14124 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14125 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14129 game.ball_state = !game.ball_state;
14131 SCAN_PLAYFIELD(xx, yy)
14133 int e = Feld[xx][yy];
14135 if (game.ball_state)
14137 if (e == EL_EMC_MAGIC_BALL)
14138 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14139 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14140 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14144 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14145 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14146 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14147 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14152 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14153 player->index_bit, dig_side);
14155 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14156 player->index_bit, dig_side);
14158 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14159 player->index_bit, dig_side);
14165 if (!PLAYER_SWITCHING(player, x, y))
14167 player->is_switching = TRUE;
14168 player->switch_x = x;
14169 player->switch_y = y;
14171 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14172 player->index_bit, dig_side);
14173 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14174 player->index_bit, dig_side);
14176 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14177 player->index_bit, dig_side);
14178 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14179 player->index_bit, dig_side);
14182 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14183 player->index_bit, dig_side);
14184 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14185 player->index_bit, dig_side);
14187 return MP_NO_ACTION;
14190 player->push_delay = -1;
14192 if (is_player) /* function can also be called by EL_PENGUIN */
14194 if (Feld[x][y] != element) /* really digged/collected something */
14196 player->is_collecting = !player->is_digging;
14197 player->is_active = TRUE;
14204 static boolean DigFieldByCE(int x, int y, int digging_element)
14206 int element = Feld[x][y];
14208 if (!IS_FREE(x, y))
14210 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14211 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14214 /* no element can dig solid indestructible elements */
14215 if (IS_INDESTRUCTIBLE(element) &&
14216 !IS_DIGGABLE(element) &&
14217 !IS_COLLECTIBLE(element))
14220 if (AmoebaNr[x][y] &&
14221 (element == EL_AMOEBA_FULL ||
14222 element == EL_BD_AMOEBA ||
14223 element == EL_AMOEBA_GROWING))
14225 AmoebaCnt[AmoebaNr[x][y]]--;
14226 AmoebaCnt2[AmoebaNr[x][y]]--;
14229 if (IS_MOVING(x, y))
14230 RemoveMovingField(x, y);
14234 TEST_DrawLevelField(x, y);
14237 /* if digged element was about to explode, prevent the explosion */
14238 ExplodeField[x][y] = EX_TYPE_NONE;
14240 PlayLevelSoundAction(x, y, action);
14243 Store[x][y] = EL_EMPTY;
14245 /* this makes it possible to leave the removed element again */
14246 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14247 Store[x][y] = element;
14252 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14254 int jx = player->jx, jy = player->jy;
14255 int x = jx + dx, y = jy + dy;
14256 int snap_direction = (dx == -1 ? MV_LEFT :
14257 dx == +1 ? MV_RIGHT :
14259 dy == +1 ? MV_DOWN : MV_NONE);
14260 boolean can_continue_snapping = (level.continuous_snapping &&
14261 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14263 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14266 if (!player->active || !IN_LEV_FIELD(x, y))
14274 if (player->MovPos == 0)
14275 player->is_pushing = FALSE;
14277 player->is_snapping = FALSE;
14279 if (player->MovPos == 0)
14281 player->is_moving = FALSE;
14282 player->is_digging = FALSE;
14283 player->is_collecting = FALSE;
14289 /* prevent snapping with already pressed snap key when not allowed */
14290 if (player->is_snapping && !can_continue_snapping)
14293 player->MovDir = snap_direction;
14295 if (player->MovPos == 0)
14297 player->is_moving = FALSE;
14298 player->is_digging = FALSE;
14299 player->is_collecting = FALSE;
14302 player->is_dropping = FALSE;
14303 player->is_dropping_pressed = FALSE;
14304 player->drop_pressed_delay = 0;
14306 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14309 player->is_snapping = TRUE;
14310 player->is_active = TRUE;
14312 if (player->MovPos == 0)
14314 player->is_moving = FALSE;
14315 player->is_digging = FALSE;
14316 player->is_collecting = FALSE;
14319 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14320 TEST_DrawLevelField(player->last_jx, player->last_jy);
14322 TEST_DrawLevelField(x, y);
14327 static boolean DropElement(struct PlayerInfo *player)
14329 int old_element, new_element;
14330 int dropx = player->jx, dropy = player->jy;
14331 int drop_direction = player->MovDir;
14332 int drop_side = drop_direction;
14333 int drop_element = get_next_dropped_element(player);
14335 /* do not drop an element on top of another element; when holding drop key
14336 pressed without moving, dropped element must move away before the next
14337 element can be dropped (this is especially important if the next element
14338 is dynamite, which can be placed on background for historical reasons) */
14339 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14342 if (IS_THROWABLE(drop_element))
14344 dropx += GET_DX_FROM_DIR(drop_direction);
14345 dropy += GET_DY_FROM_DIR(drop_direction);
14347 if (!IN_LEV_FIELD(dropx, dropy))
14351 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14352 new_element = drop_element; /* default: no change when dropping */
14354 /* check if player is active, not moving and ready to drop */
14355 if (!player->active || player->MovPos || player->drop_delay > 0)
14358 /* check if player has anything that can be dropped */
14359 if (new_element == EL_UNDEFINED)
14362 /* only set if player has anything that can be dropped */
14363 player->is_dropping_pressed = TRUE;
14365 /* check if drop key was pressed long enough for EM style dynamite */
14366 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14369 /* check if anything can be dropped at the current position */
14370 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14373 /* collected custom elements can only be dropped on empty fields */
14374 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14377 if (old_element != EL_EMPTY)
14378 Back[dropx][dropy] = old_element; /* store old element on this field */
14380 ResetGfxAnimation(dropx, dropy);
14381 ResetRandomAnimationValue(dropx, dropy);
14383 if (player->inventory_size > 0 ||
14384 player->inventory_infinite_element != EL_UNDEFINED)
14386 if (player->inventory_size > 0)
14388 player->inventory_size--;
14390 DrawGameDoorValues();
14392 if (new_element == EL_DYNAMITE)
14393 new_element = EL_DYNAMITE_ACTIVE;
14394 else if (new_element == EL_EM_DYNAMITE)
14395 new_element = EL_EM_DYNAMITE_ACTIVE;
14396 else if (new_element == EL_SP_DISK_RED)
14397 new_element = EL_SP_DISK_RED_ACTIVE;
14400 Feld[dropx][dropy] = new_element;
14402 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14403 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14404 el2img(Feld[dropx][dropy]), 0);
14406 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14408 /* needed if previous element just changed to "empty" in the last frame */
14409 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14411 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14412 player->index_bit, drop_side);
14413 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14415 player->index_bit, drop_side);
14417 TestIfElementTouchesCustomElement(dropx, dropy);
14419 else /* player is dropping a dyna bomb */
14421 player->dynabombs_left--;
14423 Feld[dropx][dropy] = new_element;
14425 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14426 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14427 el2img(Feld[dropx][dropy]), 0);
14429 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14432 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14433 InitField_WithBug1(dropx, dropy, FALSE);
14435 new_element = Feld[dropx][dropy]; /* element might have changed */
14437 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14438 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14440 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14441 MovDir[dropx][dropy] = drop_direction;
14443 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14445 /* do not cause impact style collision by dropping elements that can fall */
14446 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14449 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14450 player->is_dropping = TRUE;
14452 player->drop_pressed_delay = 0;
14453 player->is_dropping_pressed = FALSE;
14455 player->drop_x = dropx;
14456 player->drop_y = dropy;
14461 /* ------------------------------------------------------------------------- */
14462 /* game sound playing functions */
14463 /* ------------------------------------------------------------------------- */
14465 static int *loop_sound_frame = NULL;
14466 static int *loop_sound_volume = NULL;
14468 void InitPlayLevelSound()
14470 int num_sounds = getSoundListSize();
14472 checked_free(loop_sound_frame);
14473 checked_free(loop_sound_volume);
14475 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14476 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14479 static void PlayLevelSound(int x, int y, int nr)
14481 int sx = SCREENX(x), sy = SCREENY(y);
14482 int volume, stereo_position;
14483 int max_distance = 8;
14484 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14486 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14487 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14490 if (!IN_LEV_FIELD(x, y) ||
14491 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14492 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14495 volume = SOUND_MAX_VOLUME;
14497 if (!IN_SCR_FIELD(sx, sy))
14499 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14500 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14502 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14505 stereo_position = (SOUND_MAX_LEFT +
14506 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14507 (SCR_FIELDX + 2 * max_distance));
14509 if (IS_LOOP_SOUND(nr))
14511 /* This assures that quieter loop sounds do not overwrite louder ones,
14512 while restarting sound volume comparison with each new game frame. */
14514 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14517 loop_sound_volume[nr] = volume;
14518 loop_sound_frame[nr] = FrameCounter;
14521 PlaySoundExt(nr, volume, stereo_position, type);
14524 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14526 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14527 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14528 y < LEVELY(BY1) ? LEVELY(BY1) :
14529 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14533 static void PlayLevelSoundAction(int x, int y, int action)
14535 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14538 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14540 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14542 if (sound_effect != SND_UNDEFINED)
14543 PlayLevelSound(x, y, sound_effect);
14546 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14549 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14551 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14552 PlayLevelSound(x, y, sound_effect);
14555 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14557 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14559 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14560 PlayLevelSound(x, y, sound_effect);
14563 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14565 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14567 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14568 StopSound(sound_effect);
14571 static int getLevelMusicNr()
14573 if (levelset.music[level_nr] != MUS_UNDEFINED)
14574 return levelset.music[level_nr]; /* from config file */
14576 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14579 static void FadeLevelSounds()
14584 static void FadeLevelMusic()
14586 int music_nr = getLevelMusicNr();
14587 char *curr_music = getCurrentlyPlayingMusicFilename();
14588 char *next_music = getMusicInfoEntryFilename(music_nr);
14590 if (!strEqual(curr_music, next_music))
14594 void FadeLevelSoundsAndMusic()
14600 static void PlayLevelMusic()
14602 int music_nr = getLevelMusicNr();
14603 char *curr_music = getCurrentlyPlayingMusicFilename();
14604 char *next_music = getMusicInfoEntryFilename(music_nr);
14606 if (!strEqual(curr_music, next_music))
14607 PlayMusic(music_nr);
14610 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14612 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14613 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14614 int x = xx - 1 - offset;
14615 int y = yy - 1 - offset;
14620 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14624 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14628 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14632 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14636 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14640 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14644 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14647 case SAMPLE_android_clone:
14648 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14651 case SAMPLE_android_move:
14652 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14655 case SAMPLE_spring:
14656 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14660 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14664 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14667 case SAMPLE_eater_eat:
14668 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14672 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14675 case SAMPLE_collect:
14676 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14679 case SAMPLE_diamond:
14680 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14683 case SAMPLE_squash:
14684 /* !!! CHECK THIS !!! */
14686 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14688 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14692 case SAMPLE_wonderfall:
14693 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14697 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14701 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14705 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14709 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14713 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14717 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14720 case SAMPLE_wonder:
14721 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14725 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14728 case SAMPLE_exit_open:
14729 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14732 case SAMPLE_exit_leave:
14733 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14736 case SAMPLE_dynamite:
14737 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14741 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14745 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14749 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14753 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14757 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14761 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14765 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14770 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14772 int element = map_element_SP_to_RND(element_sp);
14773 int action = map_action_SP_to_RND(action_sp);
14774 int offset = (setup.sp_show_border_elements ? 0 : 1);
14775 int x = xx - offset;
14776 int y = yy - offset;
14778 PlayLevelSoundElementAction(x, y, element, action);
14781 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14783 int element = map_element_MM_to_RND(element_mm);
14784 int action = map_action_MM_to_RND(action_mm);
14786 int x = xx - offset;
14787 int y = yy - offset;
14789 if (!IS_MM_ELEMENT(element))
14790 element = EL_MM_DEFAULT;
14792 PlayLevelSoundElementAction(x, y, element, action);
14795 void PlaySound_MM(int sound_mm)
14797 int sound = map_sound_MM_to_RND(sound_mm);
14799 if (sound == SND_UNDEFINED)
14805 void PlaySoundLoop_MM(int sound_mm)
14807 int sound = map_sound_MM_to_RND(sound_mm);
14809 if (sound == SND_UNDEFINED)
14812 PlaySoundLoop(sound);
14815 void StopSound_MM(int sound_mm)
14817 int sound = map_sound_MM_to_RND(sound_mm);
14819 if (sound == SND_UNDEFINED)
14825 void RaiseScore(int value)
14827 local_player->score += value;
14829 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14831 DisplayGameControlValues();
14834 void RaiseScoreElement(int element)
14839 case EL_BD_DIAMOND:
14840 case EL_EMERALD_YELLOW:
14841 case EL_EMERALD_RED:
14842 case EL_EMERALD_PURPLE:
14843 case EL_SP_INFOTRON:
14844 RaiseScore(level.score[SC_EMERALD]);
14847 RaiseScore(level.score[SC_DIAMOND]);
14850 RaiseScore(level.score[SC_CRYSTAL]);
14853 RaiseScore(level.score[SC_PEARL]);
14856 case EL_BD_BUTTERFLY:
14857 case EL_SP_ELECTRON:
14858 RaiseScore(level.score[SC_BUG]);
14861 case EL_BD_FIREFLY:
14862 case EL_SP_SNIKSNAK:
14863 RaiseScore(level.score[SC_SPACESHIP]);
14866 case EL_DARK_YAMYAM:
14867 RaiseScore(level.score[SC_YAMYAM]);
14870 RaiseScore(level.score[SC_ROBOT]);
14873 RaiseScore(level.score[SC_PACMAN]);
14876 RaiseScore(level.score[SC_NUT]);
14879 case EL_EM_DYNAMITE:
14880 case EL_SP_DISK_RED:
14881 case EL_DYNABOMB_INCREASE_NUMBER:
14882 case EL_DYNABOMB_INCREASE_SIZE:
14883 case EL_DYNABOMB_INCREASE_POWER:
14884 RaiseScore(level.score[SC_DYNAMITE]);
14886 case EL_SHIELD_NORMAL:
14887 case EL_SHIELD_DEADLY:
14888 RaiseScore(level.score[SC_SHIELD]);
14890 case EL_EXTRA_TIME:
14891 RaiseScore(level.extra_time_score);
14905 case EL_DC_KEY_WHITE:
14906 RaiseScore(level.score[SC_KEY]);
14909 RaiseScore(element_info[element].collect_score);
14914 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14916 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14918 /* closing door required in case of envelope style request dialogs */
14920 CloseDoor(DOOR_CLOSE_1);
14922 #if defined(NETWORK_AVALIABLE)
14923 if (options.network)
14924 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14929 FadeSkipNextFadeIn();
14931 SetGameStatus(GAME_MODE_MAIN);
14936 else /* continue playing the game */
14938 if (tape.playing && tape.deactivate_display)
14939 TapeDeactivateDisplayOff(TRUE);
14941 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14943 if (tape.playing && tape.deactivate_display)
14944 TapeDeactivateDisplayOn();
14948 void RequestQuitGame(boolean ask_if_really_quit)
14950 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14951 boolean skip_request = AllPlayersGone || quick_quit;
14953 RequestQuitGameExt(skip_request, quick_quit,
14954 "Do you really want to quit the game?");
14958 /* ------------------------------------------------------------------------- */
14959 /* random generator functions */
14960 /* ------------------------------------------------------------------------- */
14962 unsigned int InitEngineRandom_RND(int seed)
14964 game.num_random_calls = 0;
14966 return InitEngineRandom(seed);
14969 unsigned int RND(int max)
14973 game.num_random_calls++;
14975 return GetEngineRandom(max);
14982 /* ------------------------------------------------------------------------- */
14983 /* game engine snapshot handling functions */
14984 /* ------------------------------------------------------------------------- */
14986 struct EngineSnapshotInfo
14988 /* runtime values for custom element collect score */
14989 int collect_score[NUM_CUSTOM_ELEMENTS];
14991 /* runtime values for group element choice position */
14992 int choice_pos[NUM_GROUP_ELEMENTS];
14994 /* runtime values for belt position animations */
14995 int belt_graphic[4][NUM_BELT_PARTS];
14996 int belt_anim_mode[4][NUM_BELT_PARTS];
14999 static struct EngineSnapshotInfo engine_snapshot_rnd;
15000 static char *snapshot_level_identifier = NULL;
15001 static int snapshot_level_nr = -1;
15003 static void SaveEngineSnapshotValues_RND()
15005 static int belt_base_active_element[4] =
15007 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15008 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15009 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15010 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15014 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15016 int element = EL_CUSTOM_START + i;
15018 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15021 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15023 int element = EL_GROUP_START + i;
15025 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15028 for (i = 0; i < 4; i++)
15030 for (j = 0; j < NUM_BELT_PARTS; j++)
15032 int element = belt_base_active_element[i] + j;
15033 int graphic = el2img(element);
15034 int anim_mode = graphic_info[graphic].anim_mode;
15036 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15037 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15042 static void LoadEngineSnapshotValues_RND()
15044 unsigned int num_random_calls = game.num_random_calls;
15047 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15049 int element = EL_CUSTOM_START + i;
15051 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15054 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15056 int element = EL_GROUP_START + i;
15058 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15061 for (i = 0; i < 4; i++)
15063 for (j = 0; j < NUM_BELT_PARTS; j++)
15065 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15066 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15068 graphic_info[graphic].anim_mode = anim_mode;
15072 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15074 InitRND(tape.random_seed);
15075 for (i = 0; i < num_random_calls; i++)
15079 if (game.num_random_calls != num_random_calls)
15081 Error(ERR_INFO, "number of random calls out of sync");
15082 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15083 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15084 Error(ERR_EXIT, "this should not happen -- please debug");
15088 void FreeEngineSnapshotSingle()
15090 FreeSnapshotSingle();
15092 setString(&snapshot_level_identifier, NULL);
15093 snapshot_level_nr = -1;
15096 void FreeEngineSnapshotList()
15098 FreeSnapshotList();
15101 ListNode *SaveEngineSnapshotBuffers()
15103 ListNode *buffers = NULL;
15105 /* copy some special values to a structure better suited for the snapshot */
15107 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15108 SaveEngineSnapshotValues_RND();
15109 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15110 SaveEngineSnapshotValues_EM();
15111 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15112 SaveEngineSnapshotValues_SP(&buffers);
15113 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15114 SaveEngineSnapshotValues_MM(&buffers);
15116 /* save values stored in special snapshot structure */
15118 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15119 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15120 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15121 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15122 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15123 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15124 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15125 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15127 /* save further RND engine values */
15129 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15130 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15131 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15133 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15134 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15135 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15136 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15138 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15139 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15140 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15141 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15142 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15144 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15145 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15146 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15148 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15150 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15152 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15153 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15155 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15156 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15158 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15159 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15161 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15164 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15165 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15168 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15170 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15171 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15174 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15175 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15177 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15178 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15181 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15182 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15184 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15185 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15186 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15187 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15188 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15190 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15191 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15194 ListNode *node = engine_snapshot_list_rnd;
15197 while (node != NULL)
15199 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15204 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15210 void SaveEngineSnapshotSingle()
15212 ListNode *buffers = SaveEngineSnapshotBuffers();
15214 /* finally save all snapshot buffers to single snapshot */
15215 SaveSnapshotSingle(buffers);
15217 /* save level identification information */
15218 setString(&snapshot_level_identifier, leveldir_current->identifier);
15219 snapshot_level_nr = level_nr;
15222 boolean CheckSaveEngineSnapshotToList()
15224 boolean save_snapshot =
15225 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15226 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15227 game.snapshot.changed_action) ||
15228 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15229 game.snapshot.collected_item));
15231 game.snapshot.changed_action = FALSE;
15232 game.snapshot.collected_item = FALSE;
15233 game.snapshot.save_snapshot = save_snapshot;
15235 return save_snapshot;
15238 void SaveEngineSnapshotToList()
15240 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15244 ListNode *buffers = SaveEngineSnapshotBuffers();
15246 /* finally save all snapshot buffers to snapshot list */
15247 SaveSnapshotToList(buffers);
15250 void SaveEngineSnapshotToListInitial()
15252 FreeEngineSnapshotList();
15254 SaveEngineSnapshotToList();
15257 void LoadEngineSnapshotValues()
15259 /* restore special values from snapshot structure */
15261 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15262 LoadEngineSnapshotValues_RND();
15263 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15264 LoadEngineSnapshotValues_EM();
15265 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15266 LoadEngineSnapshotValues_SP();
15267 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15268 LoadEngineSnapshotValues_MM();
15271 void LoadEngineSnapshotSingle()
15273 LoadSnapshotSingle();
15275 LoadEngineSnapshotValues();
15278 void LoadEngineSnapshot_Undo(int steps)
15280 LoadSnapshotFromList_Older(steps);
15282 LoadEngineSnapshotValues();
15285 void LoadEngineSnapshot_Redo(int steps)
15287 LoadSnapshotFromList_Newer(steps);
15289 LoadEngineSnapshotValues();
15292 boolean CheckEngineSnapshotSingle()
15294 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15295 snapshot_level_nr == level_nr);
15298 boolean CheckEngineSnapshotList()
15300 return CheckSnapshotList();
15304 /* ---------- new game button stuff ---------------------------------------- */
15312 } gamebutton_info[NUM_GAME_BUTTONS] =
15315 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15316 GAME_CTRL_ID_STOP, "stop game"
15319 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15320 GAME_CTRL_ID_PAUSE, "pause game"
15323 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15324 GAME_CTRL_ID_PLAY, "play game"
15327 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15328 GAME_CTRL_ID_UNDO, "undo step"
15331 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15332 GAME_CTRL_ID_REDO, "redo step"
15335 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15336 GAME_CTRL_ID_SAVE, "save game"
15339 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15340 GAME_CTRL_ID_PAUSE2, "pause game"
15343 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15344 GAME_CTRL_ID_LOAD, "load game"
15347 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15348 SOUND_CTRL_ID_MUSIC, "background music on/off"
15351 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15352 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
15355 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15356 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
15360 void CreateGameButtons()
15364 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15366 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15367 struct XY *pos = gamebutton_info[i].pos;
15368 struct GadgetInfo *gi;
15371 unsigned int event_mask;
15372 int base_x = (tape.show_game_buttons ? VX : DX);
15373 int base_y = (tape.show_game_buttons ? VY : DY);
15374 int gd_x = gfx->src_x;
15375 int gd_y = gfx->src_y;
15376 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15377 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15378 int gd_xa = gfx->src_x + gfx->active_xoffset;
15379 int gd_ya = gfx->src_y + gfx->active_yoffset;
15380 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15381 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15384 if (gfx->bitmap == NULL)
15386 game_gadget[id] = NULL;
15391 if (id == GAME_CTRL_ID_STOP ||
15392 id == GAME_CTRL_ID_PLAY ||
15393 id == GAME_CTRL_ID_SAVE ||
15394 id == GAME_CTRL_ID_LOAD)
15396 button_type = GD_TYPE_NORMAL_BUTTON;
15398 event_mask = GD_EVENT_RELEASED;
15400 else if (id == GAME_CTRL_ID_UNDO ||
15401 id == GAME_CTRL_ID_REDO)
15403 button_type = GD_TYPE_NORMAL_BUTTON;
15405 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15409 button_type = GD_TYPE_CHECK_BUTTON;
15411 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15412 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15413 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15414 event_mask = GD_EVENT_PRESSED;
15417 gi = CreateGadget(GDI_CUSTOM_ID, id,
15418 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15419 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15420 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15421 GDI_WIDTH, gfx->width,
15422 GDI_HEIGHT, gfx->height,
15423 GDI_TYPE, button_type,
15424 GDI_STATE, GD_BUTTON_UNPRESSED,
15425 GDI_CHECKED, checked,
15426 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15427 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15428 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15429 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15430 GDI_DIRECT_DRAW, FALSE,
15431 GDI_EVENT_MASK, event_mask,
15432 GDI_CALLBACK_ACTION, HandleGameButtons,
15436 Error(ERR_EXIT, "cannot create gadget");
15438 game_gadget[id] = gi;
15442 void FreeGameButtons()
15446 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15447 FreeGadget(game_gadget[i]);
15450 static void UnmapGameButtonsAtSamePosition(int id)
15454 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15456 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15457 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15458 UnmapGadget(game_gadget[i]);
15461 static void UnmapGameButtonsAtSamePosition_All()
15463 if (setup.show_snapshot_buttons)
15465 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15466 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15467 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15471 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15472 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15473 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15477 static void MapGameButtonsAtSamePosition(int id)
15481 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15483 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15484 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15485 MapGadget(game_gadget[i]);
15487 UnmapGameButtonsAtSamePosition_All();
15490 void MapUndoRedoButtons()
15492 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15493 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15495 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15496 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15498 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15501 void UnmapUndoRedoButtons()
15503 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15504 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15506 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15507 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15509 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15512 void MapGameButtons()
15516 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15517 if (i != GAME_CTRL_ID_UNDO &&
15518 i != GAME_CTRL_ID_REDO)
15519 MapGadget(game_gadget[i]);
15521 UnmapGameButtonsAtSamePosition_All();
15523 RedrawGameButtons();
15526 void UnmapGameButtons()
15530 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15531 UnmapGadget(game_gadget[i]);
15534 void RedrawGameButtons()
15538 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15539 RedrawGadget(game_gadget[i]);
15541 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15542 redraw_mask &= ~REDRAW_ALL;
15545 void GameUndoRedoExt()
15547 ClearPlayerAction();
15549 tape.pausing = TRUE;
15552 UpdateAndDisplayGameControlValues();
15554 DrawCompleteVideoDisplay();
15555 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15556 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15557 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15562 void GameUndo(int steps)
15564 if (!CheckEngineSnapshotList())
15567 LoadEngineSnapshot_Undo(steps);
15572 void GameRedo(int steps)
15574 if (!CheckEngineSnapshotList())
15577 LoadEngineSnapshot_Redo(steps);
15582 static void HandleGameButtonsExt(int id, int button)
15584 static boolean game_undo_executed = FALSE;
15585 int steps = BUTTON_STEPSIZE(button);
15586 boolean handle_game_buttons =
15587 (game_status == GAME_MODE_PLAYING ||
15588 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15590 if (!handle_game_buttons)
15595 case GAME_CTRL_ID_STOP:
15596 if (game_status == GAME_MODE_MAIN)
15602 RequestQuitGame(TRUE);
15606 case GAME_CTRL_ID_PAUSE:
15607 case GAME_CTRL_ID_PAUSE2:
15608 if (options.network && game_status == GAME_MODE_PLAYING)
15610 #if defined(NETWORK_AVALIABLE)
15612 SendToServer_ContinuePlaying();
15614 SendToServer_PausePlaying();
15618 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15620 game_undo_executed = FALSE;
15624 case GAME_CTRL_ID_PLAY:
15625 if (game_status == GAME_MODE_MAIN)
15627 StartGameActions(options.network, setup.autorecord, level.random_seed);
15629 else if (tape.pausing)
15631 #if defined(NETWORK_AVALIABLE)
15632 if (options.network)
15633 SendToServer_ContinuePlaying();
15636 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15640 case GAME_CTRL_ID_UNDO:
15641 // Important: When using "save snapshot when collecting an item" mode,
15642 // load last (current) snapshot for first "undo" after pressing "pause"
15643 // (else the last-but-one snapshot would be loaded, because the snapshot
15644 // pointer already points to the last snapshot when pressing "pause",
15645 // which is fine for "every step/move" mode, but not for "every collect")
15646 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15647 !game_undo_executed)
15650 game_undo_executed = TRUE;
15655 case GAME_CTRL_ID_REDO:
15659 case GAME_CTRL_ID_SAVE:
15663 case GAME_CTRL_ID_LOAD:
15667 case SOUND_CTRL_ID_MUSIC:
15668 if (setup.sound_music)
15670 setup.sound_music = FALSE;
15674 else if (audio.music_available)
15676 setup.sound = setup.sound_music = TRUE;
15678 SetAudioMode(setup.sound);
15684 case SOUND_CTRL_ID_LOOPS:
15685 if (setup.sound_loops)
15686 setup.sound_loops = FALSE;
15687 else if (audio.loops_available)
15689 setup.sound = setup.sound_loops = TRUE;
15691 SetAudioMode(setup.sound);
15695 case SOUND_CTRL_ID_SIMPLE:
15696 if (setup.sound_simple)
15697 setup.sound_simple = FALSE;
15698 else if (audio.sound_available)
15700 setup.sound = setup.sound_simple = TRUE;
15702 SetAudioMode(setup.sound);
15711 static void HandleGameButtons(struct GadgetInfo *gi)
15713 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15716 void HandleSoundButtonKeys(Key key)
15719 if (key == setup.shortcut.sound_simple)
15720 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15721 else if (key == setup.shortcut.sound_loops)
15722 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15723 else if (key == setup.shortcut.sound_music)
15724 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);