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 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP 0
1007 #define GAME_CTRL_ID_PAUSE 1
1008 #define GAME_CTRL_ID_PLAY 2
1009 #define GAME_CTRL_ID_UNDO 3
1010 #define GAME_CTRL_ID_REDO 4
1011 #define GAME_CTRL_ID_SAVE 5
1012 #define GAME_CTRL_ID_PAUSE2 6
1013 #define GAME_CTRL_ID_LOAD 7
1014 #define SOUND_CTRL_ID_MUSIC 8
1015 #define SOUND_CTRL_ID_LOOPS 9
1016 #define SOUND_CTRL_ID_SIMPLE 10
1018 #define NUM_GAME_BUTTONS 11
1021 /* forward declaration for internal use */
1023 static void CreateField(int, int, int);
1025 static void ResetGfxAnimation(int, int);
1027 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1028 static void AdvanceFrameAndPlayerCounters(int);
1030 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1031 static boolean MovePlayer(struct PlayerInfo *, int, int);
1032 static void ScrollPlayer(struct PlayerInfo *, int);
1033 static void ScrollScreen(struct PlayerInfo *, int);
1035 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1036 static boolean DigFieldByCE(int, int, int);
1037 static boolean SnapField(struct PlayerInfo *, int, int);
1038 static boolean DropElement(struct PlayerInfo *);
1040 static void InitBeltMovement(void);
1041 static void CloseAllOpenTimegates(void);
1042 static void CheckGravityMovement(struct PlayerInfo *);
1043 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1044 static void KillPlayerUnlessEnemyProtected(int, int);
1045 static void KillPlayerUnlessExplosionProtected(int, int);
1047 static void TestIfPlayerTouchesCustomElement(int, int);
1048 static void TestIfElementTouchesCustomElement(int, int);
1049 static void TestIfElementHitsCustomElement(int, int, int);
1051 static void HandleElementChange(int, int, int);
1052 static void ExecuteCustomElementAction(int, int, int, int);
1053 static boolean ChangeElement(int, int, int, int);
1055 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1056 #define CheckTriggeredElementChange(x, y, e, ev) \
1057 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1058 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1059 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1060 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1061 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1062 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1063 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1065 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1066 #define CheckElementChange(x, y, e, te, ev) \
1067 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1068 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1069 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1070 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1071 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1073 static void PlayLevelSound(int, int, int);
1074 static void PlayLevelSoundNearest(int, int, int);
1075 static void PlayLevelSoundAction(int, int, int);
1076 static void PlayLevelSoundElementAction(int, int, int, int);
1077 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1078 static void PlayLevelSoundActionIfLoop(int, int, int);
1079 static void StopLevelSoundActionIfLoop(int, int, int);
1080 static void PlayLevelMusic();
1081 static void FadeLevelSoundsAndMusic();
1083 static void HandleGameButtons(struct GadgetInfo *);
1085 int AmoebeNachbarNr(int, int);
1086 void AmoebeUmwandeln(int, int);
1087 void ContinueMoving(int, int);
1088 void Bang(int, int);
1089 void InitMovDir(int, int);
1090 void InitAmoebaNr(int, int);
1091 int NewHiScore(void);
1093 void TestIfGoodThingHitsBadThing(int, int, int);
1094 void TestIfBadThingHitsGoodThing(int, int, int);
1095 void TestIfPlayerTouchesBadThing(int, int);
1096 void TestIfPlayerRunsIntoBadThing(int, int, int);
1097 void TestIfBadThingTouchesPlayer(int, int);
1098 void TestIfBadThingRunsIntoPlayer(int, int, int);
1099 void TestIfFriendTouchesBadThing(int, int);
1100 void TestIfBadThingTouchesFriend(int, int);
1101 void TestIfBadThingTouchesOtherBadThing(int, int);
1102 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1104 void KillPlayer(struct PlayerInfo *);
1105 void BuryPlayer(struct PlayerInfo *);
1106 void RemovePlayer(struct PlayerInfo *);
1108 static int getInvisibleActiveFromInvisibleElement(int);
1109 static int getInvisibleFromInvisibleActiveElement(int);
1111 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1113 /* for detection of endless loops, caused by custom element programming */
1114 /* (using maximal playfield width x 10 is just a rough approximation) */
1115 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1117 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1119 if (recursion_loop_detected) \
1122 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1124 recursion_loop_detected = TRUE; \
1125 recursion_loop_element = (e); \
1128 recursion_loop_depth++; \
1131 #define RECURSION_LOOP_DETECTION_END() \
1133 recursion_loop_depth--; \
1136 static int recursion_loop_depth;
1137 static boolean recursion_loop_detected;
1138 static boolean recursion_loop_element;
1140 static int map_player_action[MAX_PLAYERS];
1143 /* ------------------------------------------------------------------------- */
1144 /* definition of elements that automatically change to other elements after */
1145 /* a specified time, eventually calling a function when changing */
1146 /* ------------------------------------------------------------------------- */
1148 /* forward declaration for changer functions */
1149 static void InitBuggyBase(int, int);
1150 static void WarnBuggyBase(int, int);
1152 static void InitTrap(int, int);
1153 static void ActivateTrap(int, int);
1154 static void ChangeActiveTrap(int, int);
1156 static void InitRobotWheel(int, int);
1157 static void RunRobotWheel(int, int);
1158 static void StopRobotWheel(int, int);
1160 static void InitTimegateWheel(int, int);
1161 static void RunTimegateWheel(int, int);
1163 static void InitMagicBallDelay(int, int);
1164 static void ActivateMagicBall(int, int);
1166 struct ChangingElementInfo
1171 void (*pre_change_function)(int x, int y);
1172 void (*change_function)(int x, int y);
1173 void (*post_change_function)(int x, int y);
1176 static struct ChangingElementInfo change_delay_list[] =
1211 EL_STEEL_EXIT_OPENING,
1219 EL_STEEL_EXIT_CLOSING,
1220 EL_STEEL_EXIT_CLOSED,
1243 EL_EM_STEEL_EXIT_OPENING,
1244 EL_EM_STEEL_EXIT_OPEN,
1251 EL_EM_STEEL_EXIT_CLOSING,
1275 EL_SWITCHGATE_OPENING,
1283 EL_SWITCHGATE_CLOSING,
1284 EL_SWITCHGATE_CLOSED,
1291 EL_TIMEGATE_OPENING,
1299 EL_TIMEGATE_CLOSING,
1308 EL_ACID_SPLASH_LEFT,
1316 EL_ACID_SPLASH_RIGHT,
1325 EL_SP_BUGGY_BASE_ACTIVATING,
1332 EL_SP_BUGGY_BASE_ACTIVATING,
1333 EL_SP_BUGGY_BASE_ACTIVE,
1340 EL_SP_BUGGY_BASE_ACTIVE,
1364 EL_ROBOT_WHEEL_ACTIVE,
1372 EL_TIMEGATE_SWITCH_ACTIVE,
1380 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1381 EL_DC_TIMEGATE_SWITCH,
1388 EL_EMC_MAGIC_BALL_ACTIVE,
1389 EL_EMC_MAGIC_BALL_ACTIVE,
1396 EL_EMC_SPRING_BUMPER_ACTIVE,
1397 EL_EMC_SPRING_BUMPER,
1404 EL_DIAGONAL_SHRINKING,
1412 EL_DIAGONAL_GROWING,
1433 int push_delay_fixed, push_delay_random;
1437 { EL_SPRING, 0, 0 },
1438 { EL_BALLOON, 0, 0 },
1440 { EL_SOKOBAN_OBJECT, 2, 0 },
1441 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1442 { EL_SATELLITE, 2, 0 },
1443 { EL_SP_DISK_YELLOW, 2, 0 },
1445 { EL_UNDEFINED, 0, 0 },
1453 move_stepsize_list[] =
1455 { EL_AMOEBA_DROP, 2 },
1456 { EL_AMOEBA_DROPPING, 2 },
1457 { EL_QUICKSAND_FILLING, 1 },
1458 { EL_QUICKSAND_EMPTYING, 1 },
1459 { EL_QUICKSAND_FAST_FILLING, 2 },
1460 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1461 { EL_MAGIC_WALL_FILLING, 2 },
1462 { EL_MAGIC_WALL_EMPTYING, 2 },
1463 { EL_BD_MAGIC_WALL_FILLING, 2 },
1464 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1465 { EL_DC_MAGIC_WALL_FILLING, 2 },
1466 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1468 { EL_UNDEFINED, 0 },
1476 collect_count_list[] =
1479 { EL_BD_DIAMOND, 1 },
1480 { EL_EMERALD_YELLOW, 1 },
1481 { EL_EMERALD_RED, 1 },
1482 { EL_EMERALD_PURPLE, 1 },
1484 { EL_SP_INFOTRON, 1 },
1488 { EL_UNDEFINED, 0 },
1496 access_direction_list[] =
1498 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1499 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1500 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1501 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1502 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1503 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1504 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1505 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1506 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1507 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1508 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1510 { EL_SP_PORT_LEFT, MV_RIGHT },
1511 { EL_SP_PORT_RIGHT, MV_LEFT },
1512 { EL_SP_PORT_UP, MV_DOWN },
1513 { EL_SP_PORT_DOWN, MV_UP },
1514 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1515 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1516 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1517 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1518 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1519 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1520 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1521 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1522 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1523 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1524 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1525 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1526 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1527 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1528 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1530 { EL_UNDEFINED, MV_NONE }
1533 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1535 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1536 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1537 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1538 IS_JUST_CHANGING(x, y))
1540 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1542 /* static variables for playfield scan mode (scanning forward or backward) */
1543 static int playfield_scan_start_x = 0;
1544 static int playfield_scan_start_y = 0;
1545 static int playfield_scan_delta_x = 1;
1546 static int playfield_scan_delta_y = 1;
1548 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1549 (y) >= 0 && (y) <= lev_fieldy - 1; \
1550 (y) += playfield_scan_delta_y) \
1551 for ((x) = playfield_scan_start_x; \
1552 (x) >= 0 && (x) <= lev_fieldx - 1; \
1553 (x) += playfield_scan_delta_x)
1556 void DEBUG_SetMaximumDynamite()
1560 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1561 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1562 local_player->inventory_element[local_player->inventory_size++] =
1567 static void InitPlayfieldScanModeVars()
1569 if (game.use_reverse_scan_direction)
1571 playfield_scan_start_x = lev_fieldx - 1;
1572 playfield_scan_start_y = lev_fieldy - 1;
1574 playfield_scan_delta_x = -1;
1575 playfield_scan_delta_y = -1;
1579 playfield_scan_start_x = 0;
1580 playfield_scan_start_y = 0;
1582 playfield_scan_delta_x = 1;
1583 playfield_scan_delta_y = 1;
1587 static void InitPlayfieldScanMode(int mode)
1589 game.use_reverse_scan_direction =
1590 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1592 InitPlayfieldScanModeVars();
1595 static int get_move_delay_from_stepsize(int move_stepsize)
1598 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1600 /* make sure that stepsize value is always a power of 2 */
1601 move_stepsize = (1 << log_2(move_stepsize));
1603 return TILEX / move_stepsize;
1606 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1609 int player_nr = player->index_nr;
1610 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1611 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1613 /* do no immediately change move delay -- the player might just be moving */
1614 player->move_delay_value_next = move_delay;
1616 /* information if player can move must be set separately */
1617 player->cannot_move = cannot_move;
1621 player->move_delay = game.initial_move_delay[player_nr];
1622 player->move_delay_value = game.initial_move_delay_value[player_nr];
1624 player->move_delay_value_next = -1;
1626 player->move_delay_reset_counter = 0;
1630 void GetPlayerConfig()
1632 GameFrameDelay = setup.game_frame_delay;
1634 if (!audio.sound_available)
1635 setup.sound_simple = FALSE;
1637 if (!audio.loops_available)
1638 setup.sound_loops = FALSE;
1640 if (!audio.music_available)
1641 setup.sound_music = FALSE;
1643 if (!video.fullscreen_available)
1644 setup.fullscreen = FALSE;
1646 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1648 SetAudioMode(setup.sound);
1651 int GetElementFromGroupElement(int element)
1653 if (IS_GROUP_ELEMENT(element))
1655 struct ElementGroupInfo *group = element_info[element].group;
1656 int last_anim_random_frame = gfx.anim_random_frame;
1659 if (group->choice_mode == ANIM_RANDOM)
1660 gfx.anim_random_frame = RND(group->num_elements_resolved);
1662 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1663 group->choice_mode, 0,
1666 if (group->choice_mode == ANIM_RANDOM)
1667 gfx.anim_random_frame = last_anim_random_frame;
1669 group->choice_pos++;
1671 element = group->element_resolved[element_pos];
1677 static void InitPlayerField(int x, int y, int element, boolean init_game)
1679 if (element == EL_SP_MURPHY)
1683 if (stored_player[0].present)
1685 Feld[x][y] = EL_SP_MURPHY_CLONE;
1691 stored_player[0].initial_element = element;
1692 stored_player[0].use_murphy = TRUE;
1694 if (!level.use_artwork_element[0])
1695 stored_player[0].artwork_element = EL_SP_MURPHY;
1698 Feld[x][y] = EL_PLAYER_1;
1704 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1705 int jx = player->jx, jy = player->jy;
1707 player->present = TRUE;
1709 player->block_last_field = (element == EL_SP_MURPHY ?
1710 level.sp_block_last_field :
1711 level.block_last_field);
1713 /* ---------- initialize player's last field block delay --------------- */
1715 /* always start with reliable default value (no adjustment needed) */
1716 player->block_delay_adjustment = 0;
1718 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1719 if (player->block_last_field && element == EL_SP_MURPHY)
1720 player->block_delay_adjustment = 1;
1722 /* special case 2: in game engines before 3.1.1, blocking was different */
1723 if (game.use_block_last_field_bug)
1724 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1726 if (!options.network || player->connected)
1728 player->active = TRUE;
1730 /* remove potentially duplicate players */
1731 if (StorePlayer[jx][jy] == Feld[x][y])
1732 StorePlayer[jx][jy] = 0;
1734 StorePlayer[x][y] = Feld[x][y];
1736 #if DEBUG_INIT_PLAYER
1739 printf("- player element %d activated", player->element_nr);
1740 printf(" (local player is %d and currently %s)\n",
1741 local_player->element_nr,
1742 local_player->active ? "active" : "not active");
1747 Feld[x][y] = EL_EMPTY;
1749 player->jx = player->last_jx = x;
1750 player->jy = player->last_jy = y;
1755 int player_nr = GET_PLAYER_NR(element);
1756 struct PlayerInfo *player = &stored_player[player_nr];
1758 if (player->active && player->killed)
1759 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1763 static void InitField(int x, int y, boolean init_game)
1765 int element = Feld[x][y];
1774 InitPlayerField(x, y, element, init_game);
1777 case EL_SOKOBAN_FIELD_PLAYER:
1778 element = Feld[x][y] = EL_PLAYER_1;
1779 InitField(x, y, init_game);
1781 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1782 InitField(x, y, init_game);
1785 case EL_SOKOBAN_FIELD_EMPTY:
1786 local_player->sokobanfields_still_needed++;
1790 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1791 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1792 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1793 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1794 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1795 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1796 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1797 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1798 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1799 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808 case EL_SPACESHIP_RIGHT:
1809 case EL_SPACESHIP_UP:
1810 case EL_SPACESHIP_LEFT:
1811 case EL_SPACESHIP_DOWN:
1812 case EL_BD_BUTTERFLY:
1813 case EL_BD_BUTTERFLY_RIGHT:
1814 case EL_BD_BUTTERFLY_UP:
1815 case EL_BD_BUTTERFLY_LEFT:
1816 case EL_BD_BUTTERFLY_DOWN:
1818 case EL_BD_FIREFLY_RIGHT:
1819 case EL_BD_FIREFLY_UP:
1820 case EL_BD_FIREFLY_LEFT:
1821 case EL_BD_FIREFLY_DOWN:
1822 case EL_PACMAN_RIGHT:
1824 case EL_PACMAN_LEFT:
1825 case EL_PACMAN_DOWN:
1827 case EL_YAMYAM_LEFT:
1828 case EL_YAMYAM_RIGHT:
1830 case EL_YAMYAM_DOWN:
1831 case EL_DARK_YAMYAM:
1834 case EL_SP_SNIKSNAK:
1835 case EL_SP_ELECTRON:
1844 case EL_AMOEBA_FULL:
1849 case EL_AMOEBA_DROP:
1850 if (y == lev_fieldy - 1)
1852 Feld[x][y] = EL_AMOEBA_GROWING;
1853 Store[x][y] = EL_AMOEBA_WET;
1857 case EL_DYNAMITE_ACTIVE:
1858 case EL_SP_DISK_RED_ACTIVE:
1859 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1860 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1861 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1862 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1863 MovDelay[x][y] = 96;
1866 case EL_EM_DYNAMITE_ACTIVE:
1867 MovDelay[x][y] = 32;
1871 local_player->lights_still_needed++;
1875 local_player->friends_still_needed++;
1880 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1883 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1884 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1885 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1886 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1887 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1888 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1889 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1890 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1891 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1892 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1893 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1894 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1897 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1898 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1899 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1901 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1903 game.belt_dir[belt_nr] = belt_dir;
1904 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1906 else /* more than one switch -- set it like the first switch */
1908 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1913 case EL_LIGHT_SWITCH_ACTIVE:
1915 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1918 case EL_INVISIBLE_STEELWALL:
1919 case EL_INVISIBLE_WALL:
1920 case EL_INVISIBLE_SAND:
1921 if (game.light_time_left > 0 ||
1922 game.lenses_time_left > 0)
1923 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1926 case EL_EMC_MAGIC_BALL:
1927 if (game.ball_state)
1928 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1931 case EL_EMC_MAGIC_BALL_SWITCH:
1932 if (game.ball_state)
1933 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1936 case EL_TRIGGER_PLAYER:
1937 case EL_TRIGGER_ELEMENT:
1938 case EL_TRIGGER_CE_VALUE:
1939 case EL_TRIGGER_CE_SCORE:
1941 case EL_ANY_ELEMENT:
1942 case EL_CURRENT_CE_VALUE:
1943 case EL_CURRENT_CE_SCORE:
1960 /* reference elements should not be used on the playfield */
1961 Feld[x][y] = EL_EMPTY;
1965 if (IS_CUSTOM_ELEMENT(element))
1967 if (CAN_MOVE(element))
1970 if (!element_info[element].use_last_ce_value || init_game)
1971 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1973 else if (IS_GROUP_ELEMENT(element))
1975 Feld[x][y] = GetElementFromGroupElement(element);
1977 InitField(x, y, init_game);
1984 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1987 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1989 InitField(x, y, init_game);
1991 /* not needed to call InitMovDir() -- already done by InitField()! */
1992 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1993 CAN_MOVE(Feld[x][y]))
1997 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1999 int old_element = Feld[x][y];
2001 InitField(x, y, init_game);
2003 /* not needed to call InitMovDir() -- already done by InitField()! */
2004 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2005 CAN_MOVE(old_element) &&
2006 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2009 /* this case is in fact a combination of not less than three bugs:
2010 first, it calls InitMovDir() for elements that can move, although this is
2011 already done by InitField(); then, it checks the element that was at this
2012 field _before_ the call to InitField() (which can change it); lastly, it
2013 was not called for "mole with direction" elements, which were treated as
2014 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2018 static int get_key_element_from_nr(int key_nr)
2020 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2021 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2022 EL_EM_KEY_1 : EL_KEY_1);
2024 return key_base_element + key_nr;
2027 static int get_next_dropped_element(struct PlayerInfo *player)
2029 return (player->inventory_size > 0 ?
2030 player->inventory_element[player->inventory_size - 1] :
2031 player->inventory_infinite_element != EL_UNDEFINED ?
2032 player->inventory_infinite_element :
2033 player->dynabombs_left > 0 ?
2034 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2038 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2040 /* pos >= 0: get element from bottom of the stack;
2041 pos < 0: get element from top of the stack */
2045 int min_inventory_size = -pos;
2046 int inventory_pos = player->inventory_size - min_inventory_size;
2047 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2049 return (player->inventory_size >= min_inventory_size ?
2050 player->inventory_element[inventory_pos] :
2051 player->inventory_infinite_element != EL_UNDEFINED ?
2052 player->inventory_infinite_element :
2053 player->dynabombs_left >= min_dynabombs_left ?
2054 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2059 int min_dynabombs_left = pos + 1;
2060 int min_inventory_size = pos + 1 - player->dynabombs_left;
2061 int inventory_pos = pos - player->dynabombs_left;
2063 return (player->inventory_infinite_element != EL_UNDEFINED ?
2064 player->inventory_infinite_element :
2065 player->dynabombs_left >= min_dynabombs_left ?
2066 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2067 player->inventory_size >= min_inventory_size ?
2068 player->inventory_element[inventory_pos] :
2073 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2075 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2076 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2079 if (gpo1->sort_priority != gpo2->sort_priority)
2080 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2082 compare_result = gpo1->nr - gpo2->nr;
2084 return compare_result;
2087 int getPlayerInventorySize(int player_nr)
2089 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2090 return level.native_em_level->ply[player_nr]->dynamite;
2091 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2092 return level.native_sp_level->game_sp->red_disk_count;
2094 return stored_player[player_nr].inventory_size;
2097 void InitGameControlValues()
2101 for (i = 0; game_panel_controls[i].nr != -1; i++)
2103 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2104 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2105 struct TextPosInfo *pos = gpc->pos;
2107 int type = gpc->type;
2111 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2112 Error(ERR_EXIT, "this should not happen -- please debug");
2115 /* force update of game controls after initialization */
2116 gpc->value = gpc->last_value = -1;
2117 gpc->frame = gpc->last_frame = -1;
2118 gpc->gfx_frame = -1;
2120 /* determine panel value width for later calculation of alignment */
2121 if (type == TYPE_INTEGER || type == TYPE_STRING)
2123 pos->width = pos->size * getFontWidth(pos->font);
2124 pos->height = getFontHeight(pos->font);
2126 else if (type == TYPE_ELEMENT)
2128 pos->width = pos->size;
2129 pos->height = pos->size;
2132 /* fill structure for game panel draw order */
2134 gpo->sort_priority = pos->sort_priority;
2137 /* sort game panel controls according to sort_priority and control number */
2138 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2139 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2142 void UpdatePlayfieldElementCount()
2144 boolean use_element_count = FALSE;
2147 /* first check if it is needed at all to calculate playfield element count */
2148 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2149 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2150 use_element_count = TRUE;
2152 if (!use_element_count)
2155 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2156 element_info[i].element_count = 0;
2158 SCAN_PLAYFIELD(x, y)
2160 element_info[Feld[x][y]].element_count++;
2163 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2164 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2165 if (IS_IN_GROUP(j, i))
2166 element_info[EL_GROUP_START + i].element_count +=
2167 element_info[j].element_count;
2170 void UpdateGameControlValues()
2173 int time = (local_player->LevelSolved ?
2174 local_player->LevelSolved_CountingTime :
2175 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2176 level.native_em_level->lev->time :
2177 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2178 level.native_sp_level->game_sp->time_played :
2179 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2180 game_mm.energy_left :
2181 game.no_time_limit ? TimePlayed : TimeLeft);
2182 int score = (local_player->LevelSolved ?
2183 local_player->LevelSolved_CountingScore :
2184 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185 level.native_em_level->lev->score :
2186 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187 level.native_sp_level->game_sp->score :
2188 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2190 local_player->score);
2191 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2192 level.native_em_level->lev->required :
2193 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2194 level.native_sp_level->game_sp->infotrons_still_needed :
2195 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2196 game_mm.kettles_still_needed :
2197 local_player->gems_still_needed);
2198 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2199 level.native_em_level->lev->required > 0 :
2200 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2201 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2202 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2203 game_mm.kettles_still_needed > 0 ||
2204 game_mm.lights_still_needed > 0 :
2205 local_player->gems_still_needed > 0 ||
2206 local_player->sokobanfields_still_needed > 0 ||
2207 local_player->lights_still_needed > 0);
2208 int health = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2209 MIN(MAX(0, 100 - game_mm.laser_overload_value), 100) : 100);
2211 UpdatePlayfieldElementCount();
2213 /* update game panel control values */
2215 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2216 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2218 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2219 for (i = 0; i < MAX_NUM_KEYS; i++)
2220 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2221 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2222 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2224 if (game.centered_player_nr == -1)
2226 for (i = 0; i < MAX_PLAYERS; i++)
2228 /* only one player in Supaplex game engine */
2229 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2232 for (k = 0; k < MAX_NUM_KEYS; k++)
2234 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2236 if (level.native_em_level->ply[i]->keys & (1 << k))
2237 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2238 get_key_element_from_nr(k);
2240 else if (stored_player[i].key[k])
2241 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2242 get_key_element_from_nr(k);
2245 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2246 getPlayerInventorySize(i);
2248 if (stored_player[i].num_white_keys > 0)
2249 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2252 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2253 stored_player[i].num_white_keys;
2258 int player_nr = game.centered_player_nr;
2260 for (k = 0; k < MAX_NUM_KEYS; k++)
2262 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2264 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2265 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266 get_key_element_from_nr(k);
2268 else if (stored_player[player_nr].key[k])
2269 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2270 get_key_element_from_nr(k);
2273 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274 getPlayerInventorySize(player_nr);
2276 if (stored_player[player_nr].num_white_keys > 0)
2277 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2279 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2280 stored_player[player_nr].num_white_keys;
2283 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2285 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2286 get_inventory_element_from_pos(local_player, i);
2287 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2288 get_inventory_element_from_pos(local_player, -i - 1);
2291 game_panel_controls[GAME_PANEL_SCORE].value = score;
2292 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2294 game_panel_controls[GAME_PANEL_TIME].value = time;
2296 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2297 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2298 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2300 if (game.no_time_limit)
2301 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2303 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2305 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2306 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2308 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2310 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2311 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2313 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2314 local_player->shield_normal_time_left;
2315 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2316 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2318 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2319 local_player->shield_deadly_time_left;
2321 game_panel_controls[GAME_PANEL_EXIT].value =
2322 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2324 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2325 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2326 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2327 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2328 EL_EMC_MAGIC_BALL_SWITCH);
2330 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2331 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2332 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2333 game.light_time_left;
2335 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2336 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2337 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2338 game.timegate_time_left;
2340 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2341 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2343 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2344 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2345 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2346 game.lenses_time_left;
2348 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2349 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2350 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2351 game.magnify_time_left;
2353 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2354 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2355 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2356 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2357 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2358 EL_BALLOON_SWITCH_NONE);
2360 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2361 local_player->dynabomb_count;
2362 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2363 local_player->dynabomb_size;
2364 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2365 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2367 game_panel_controls[GAME_PANEL_PENGUINS].value =
2368 local_player->friends_still_needed;
2370 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2371 local_player->sokobanfields_still_needed;
2372 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2373 local_player->sokobanfields_still_needed;
2375 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2376 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2378 for (i = 0; i < NUM_BELTS; i++)
2380 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2381 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2382 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2383 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2384 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2387 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2388 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2389 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2390 game.magic_wall_time_left;
2392 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2393 local_player->gravity;
2395 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2396 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2398 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2399 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2400 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2401 game.panel.element[i].id : EL_UNDEFINED);
2403 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2404 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2405 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2406 element_info[game.panel.element_count[i].id].element_count : 0);
2408 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2409 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2410 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2411 element_info[game.panel.ce_score[i].id].collect_score : 0);
2413 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2414 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2415 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2416 element_info[game.panel.ce_score_element[i].id].collect_score :
2419 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2420 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2421 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2423 /* update game panel control frames */
2425 for (i = 0; game_panel_controls[i].nr != -1; i++)
2427 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2429 if (gpc->type == TYPE_ELEMENT)
2431 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2433 int last_anim_random_frame = gfx.anim_random_frame;
2434 int element = gpc->value;
2435 int graphic = el2panelimg(element);
2437 if (gpc->value != gpc->last_value)
2440 gpc->gfx_random = INIT_GFX_RANDOM();
2446 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2447 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2448 gpc->gfx_random = INIT_GFX_RANDOM();
2451 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2452 gfx.anim_random_frame = gpc->gfx_random;
2454 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2455 gpc->gfx_frame = element_info[element].collect_score;
2457 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2460 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2461 gfx.anim_random_frame = last_anim_random_frame;
2464 else if (gpc->type == TYPE_GRAPHIC)
2466 if (gpc->graphic != IMG_UNDEFINED)
2468 int last_anim_random_frame = gfx.anim_random_frame;
2469 int graphic = gpc->graphic;
2471 if (gpc->value != gpc->last_value)
2474 gpc->gfx_random = INIT_GFX_RANDOM();
2480 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2481 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2482 gpc->gfx_random = INIT_GFX_RANDOM();
2485 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2486 gfx.anim_random_frame = gpc->gfx_random;
2488 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2490 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2491 gfx.anim_random_frame = last_anim_random_frame;
2497 void DisplayGameControlValues()
2499 boolean redraw_panel = FALSE;
2502 for (i = 0; game_panel_controls[i].nr != -1; i++)
2504 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2506 if (PANEL_DEACTIVATED(gpc->pos))
2509 if (gpc->value == gpc->last_value &&
2510 gpc->frame == gpc->last_frame)
2513 redraw_panel = TRUE;
2519 /* copy default game door content to main double buffer */
2521 /* !!! CHECK AGAIN !!! */
2522 SetPanelBackground();
2523 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2524 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2526 /* redraw game control buttons */
2527 RedrawGameButtons();
2529 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2531 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2533 int nr = game_panel_order[i].nr;
2534 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2535 struct TextPosInfo *pos = gpc->pos;
2536 int type = gpc->type;
2537 int value = gpc->value;
2538 int frame = gpc->frame;
2539 int size = pos->size;
2540 int font = pos->font;
2541 boolean draw_masked = pos->draw_masked;
2542 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2544 if (PANEL_DEACTIVATED(pos))
2547 gpc->last_value = value;
2548 gpc->last_frame = frame;
2550 if (type == TYPE_INTEGER)
2552 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2553 nr == GAME_PANEL_TIME)
2555 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2557 if (use_dynamic_size) /* use dynamic number of digits */
2559 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2560 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2561 int size2 = size1 + 1;
2562 int font1 = pos->font;
2563 int font2 = pos->font_alt;
2565 size = (value < value_change ? size1 : size2);
2566 font = (value < value_change ? font1 : font2);
2570 /* correct text size if "digits" is zero or less */
2572 size = strlen(int2str(value, size));
2574 /* dynamically correct text alignment */
2575 pos->width = size * getFontWidth(font);
2577 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2578 int2str(value, size), font, mask_mode);
2580 else if (type == TYPE_ELEMENT)
2582 int element, graphic;
2586 int dst_x = PANEL_XPOS(pos);
2587 int dst_y = PANEL_YPOS(pos);
2589 if (value != EL_UNDEFINED && value != EL_EMPTY)
2592 graphic = el2panelimg(value);
2594 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2596 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2599 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2602 width = graphic_info[graphic].width * size / TILESIZE;
2603 height = graphic_info[graphic].height * size / TILESIZE;
2606 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2609 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2613 else if (type == TYPE_GRAPHIC)
2615 int graphic = gpc->graphic;
2616 int graphic_active = gpc->graphic_active;
2620 int dst_x = PANEL_XPOS(pos);
2621 int dst_y = PANEL_YPOS(pos);
2622 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2623 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2625 if (graphic != IMG_UNDEFINED && !skip)
2627 if (pos->style == STYLE_REVERSE)
2628 value = 100 - value;
2630 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2632 if (pos->direction & MV_HORIZONTAL)
2634 width = graphic_info[graphic_active].width * value / 100;
2635 height = graphic_info[graphic_active].height;
2637 if (pos->direction == MV_LEFT)
2639 src_x += graphic_info[graphic_active].width - width;
2640 dst_x += graphic_info[graphic_active].width - width;
2645 width = graphic_info[graphic_active].width;
2646 height = graphic_info[graphic_active].height * value / 100;
2648 if (pos->direction == MV_UP)
2650 src_y += graphic_info[graphic_active].height - height;
2651 dst_y += graphic_info[graphic_active].height - height;
2656 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2659 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2662 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2664 if (pos->direction & MV_HORIZONTAL)
2666 if (pos->direction == MV_RIGHT)
2673 dst_x = PANEL_XPOS(pos);
2676 width = graphic_info[graphic].width - width;
2680 if (pos->direction == MV_DOWN)
2687 dst_y = PANEL_YPOS(pos);
2690 height = graphic_info[graphic].height - height;
2694 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2697 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2701 else if (type == TYPE_STRING)
2703 boolean active = (value != 0);
2704 char *state_normal = "off";
2705 char *state_active = "on";
2706 char *state = (active ? state_active : state_normal);
2707 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2708 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2709 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2710 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2712 if (nr == GAME_PANEL_GRAVITY_STATE)
2714 int font1 = pos->font; /* (used for normal state) */
2715 int font2 = pos->font_alt; /* (used for active state) */
2717 font = (active ? font2 : font1);
2726 /* don't truncate output if "chars" is zero or less */
2729 /* dynamically correct text alignment */
2730 pos->width = size * getFontWidth(font);
2733 s_cut = getStringCopyN(s, size);
2735 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2736 s_cut, font, mask_mode);
2742 redraw_mask |= REDRAW_DOOR_1;
2745 SetGameStatus(GAME_MODE_PLAYING);
2748 void UpdateAndDisplayGameControlValues()
2750 if (tape.deactivate_display)
2753 UpdateGameControlValues();
2754 DisplayGameControlValues();
2757 void UpdateGameDoorValues()
2759 UpdateGameControlValues();
2762 void DrawGameDoorValues()
2764 DisplayGameControlValues();
2769 =============================================================================
2771 -----------------------------------------------------------------------------
2772 initialize game engine due to level / tape version number
2773 =============================================================================
2776 static void InitGameEngine()
2778 int i, j, k, l, x, y;
2780 /* set game engine from tape file when re-playing, else from level file */
2781 game.engine_version = (tape.playing ? tape.engine_version :
2782 level.game_version);
2784 /* set single or multi-player game mode (needed for re-playing tapes) */
2785 game.team_mode = setup.team_mode;
2789 int num_players = 0;
2791 for (i = 0; i < MAX_PLAYERS; i++)
2792 if (tape.player_participates[i])
2795 /* multi-player tapes contain input data for more than one player */
2796 game.team_mode = (num_players > 1);
2799 /* ---------------------------------------------------------------------- */
2800 /* set flags for bugs and changes according to active game engine version */
2801 /* ---------------------------------------------------------------------- */
2804 Summary of bugfix/change:
2805 Fixed handling for custom elements that change when pushed by the player.
2807 Fixed/changed in version:
2811 Before 3.1.0, custom elements that "change when pushing" changed directly
2812 after the player started pushing them (until then handled in "DigField()").
2813 Since 3.1.0, these custom elements are not changed until the "pushing"
2814 move of the element is finished (now handled in "ContinueMoving()").
2816 Affected levels/tapes:
2817 The first condition is generally needed for all levels/tapes before version
2818 3.1.0, which might use the old behaviour before it was changed; known tapes
2819 that are affected are some tapes from the level set "Walpurgis Gardens" by
2821 The second condition is an exception from the above case and is needed for
2822 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2823 above (including some development versions of 3.1.0), but before it was
2824 known that this change would break tapes like the above and was fixed in
2825 3.1.1, so that the changed behaviour was active although the engine version
2826 while recording maybe was before 3.1.0. There is at least one tape that is
2827 affected by this exception, which is the tape for the one-level set "Bug
2828 Machine" by Juergen Bonhagen.
2831 game.use_change_when_pushing_bug =
2832 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2834 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2835 tape.game_version < VERSION_IDENT(3,1,1,0)));
2838 Summary of bugfix/change:
2839 Fixed handling for blocking the field the player leaves when moving.
2841 Fixed/changed in version:
2845 Before 3.1.1, when "block last field when moving" was enabled, the field
2846 the player is leaving when moving was blocked for the time of the move,
2847 and was directly unblocked afterwards. This resulted in the last field
2848 being blocked for exactly one less than the number of frames of one player
2849 move. Additionally, even when blocking was disabled, the last field was
2850 blocked for exactly one frame.
2851 Since 3.1.1, due to changes in player movement handling, the last field
2852 is not blocked at all when blocking is disabled. When blocking is enabled,
2853 the last field is blocked for exactly the number of frames of one player
2854 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2855 last field is blocked for exactly one more than the number of frames of
2858 Affected levels/tapes:
2859 (!!! yet to be determined -- probably many !!!)
2862 game.use_block_last_field_bug =
2863 (game.engine_version < VERSION_IDENT(3,1,1,0));
2865 game_em.use_single_button =
2866 (game.engine_version > VERSION_IDENT(4,0,0,2));
2868 game_em.use_snap_key_bug =
2869 (game.engine_version < VERSION_IDENT(4,0,1,0));
2871 /* ---------------------------------------------------------------------- */
2873 /* set maximal allowed number of custom element changes per game frame */
2874 game.max_num_changes_per_frame = 1;
2876 /* default scan direction: scan playfield from top/left to bottom/right */
2877 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2879 /* dynamically adjust element properties according to game engine version */
2880 InitElementPropertiesEngine(game.engine_version);
2883 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2884 printf(" tape version == %06d [%s] [file: %06d]\n",
2885 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2887 printf(" => game.engine_version == %06d\n", game.engine_version);
2890 /* ---------- initialize player's initial move delay --------------------- */
2892 /* dynamically adjust player properties according to level information */
2893 for (i = 0; i < MAX_PLAYERS; i++)
2894 game.initial_move_delay_value[i] =
2895 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2897 /* dynamically adjust player properties according to game engine version */
2898 for (i = 0; i < MAX_PLAYERS; i++)
2899 game.initial_move_delay[i] =
2900 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2901 game.initial_move_delay_value[i] : 0);
2903 /* ---------- initialize player's initial push delay --------------------- */
2905 /* dynamically adjust player properties according to game engine version */
2906 game.initial_push_delay_value =
2907 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2909 /* ---------- initialize changing elements ------------------------------- */
2911 /* initialize changing elements information */
2912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2914 struct ElementInfo *ei = &element_info[i];
2916 /* this pointer might have been changed in the level editor */
2917 ei->change = &ei->change_page[0];
2919 if (!IS_CUSTOM_ELEMENT(i))
2921 ei->change->target_element = EL_EMPTY_SPACE;
2922 ei->change->delay_fixed = 0;
2923 ei->change->delay_random = 0;
2924 ei->change->delay_frames = 1;
2927 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2929 ei->has_change_event[j] = FALSE;
2931 ei->event_page_nr[j] = 0;
2932 ei->event_page[j] = &ei->change_page[0];
2936 /* add changing elements from pre-defined list */
2937 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2939 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2940 struct ElementInfo *ei = &element_info[ch_delay->element];
2942 ei->change->target_element = ch_delay->target_element;
2943 ei->change->delay_fixed = ch_delay->change_delay;
2945 ei->change->pre_change_function = ch_delay->pre_change_function;
2946 ei->change->change_function = ch_delay->change_function;
2947 ei->change->post_change_function = ch_delay->post_change_function;
2949 ei->change->can_change = TRUE;
2950 ei->change->can_change_or_has_action = TRUE;
2952 ei->has_change_event[CE_DELAY] = TRUE;
2954 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2955 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2958 /* ---------- initialize internal run-time variables --------------------- */
2960 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2962 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2964 for (j = 0; j < ei->num_change_pages; j++)
2966 ei->change_page[j].can_change_or_has_action =
2967 (ei->change_page[j].can_change |
2968 ei->change_page[j].has_action);
2972 /* add change events from custom element configuration */
2973 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2975 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2977 for (j = 0; j < ei->num_change_pages; j++)
2979 if (!ei->change_page[j].can_change_or_has_action)
2982 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2984 /* only add event page for the first page found with this event */
2985 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2987 ei->has_change_event[k] = TRUE;
2989 ei->event_page_nr[k] = j;
2990 ei->event_page[k] = &ei->change_page[j];
2996 /* ---------- initialize reference elements in change conditions --------- */
2998 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3000 int element = EL_CUSTOM_START + i;
3001 struct ElementInfo *ei = &element_info[element];
3003 for (j = 0; j < ei->num_change_pages; j++)
3005 int trigger_element = ei->change_page[j].initial_trigger_element;
3007 if (trigger_element >= EL_PREV_CE_8 &&
3008 trigger_element <= EL_NEXT_CE_8)
3009 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3011 ei->change_page[j].trigger_element = trigger_element;
3015 /* ---------- initialize run-time trigger player and element ------------- */
3017 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3019 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3021 for (j = 0; j < ei->num_change_pages; j++)
3023 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3024 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3025 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3026 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3027 ei->change_page[j].actual_trigger_ce_value = 0;
3028 ei->change_page[j].actual_trigger_ce_score = 0;
3032 /* ---------- initialize trigger events ---------------------------------- */
3034 /* initialize trigger events information */
3035 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3036 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3037 trigger_events[i][j] = FALSE;
3039 /* add trigger events from element change event properties */
3040 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3042 struct ElementInfo *ei = &element_info[i];
3044 for (j = 0; j < ei->num_change_pages; j++)
3046 if (!ei->change_page[j].can_change_or_has_action)
3049 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3051 int trigger_element = ei->change_page[j].trigger_element;
3053 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3055 if (ei->change_page[j].has_event[k])
3057 if (IS_GROUP_ELEMENT(trigger_element))
3059 struct ElementGroupInfo *group =
3060 element_info[trigger_element].group;
3062 for (l = 0; l < group->num_elements_resolved; l++)
3063 trigger_events[group->element_resolved[l]][k] = TRUE;
3065 else if (trigger_element == EL_ANY_ELEMENT)
3066 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3067 trigger_events[l][k] = TRUE;
3069 trigger_events[trigger_element][k] = TRUE;
3076 /* ---------- initialize push delay -------------------------------------- */
3078 /* initialize push delay values to default */
3079 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3081 if (!IS_CUSTOM_ELEMENT(i))
3083 /* set default push delay values (corrected since version 3.0.7-1) */
3084 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3086 element_info[i].push_delay_fixed = 2;
3087 element_info[i].push_delay_random = 8;
3091 element_info[i].push_delay_fixed = 8;
3092 element_info[i].push_delay_random = 8;
3097 /* set push delay value for certain elements from pre-defined list */
3098 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3100 int e = push_delay_list[i].element;
3102 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3103 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3106 /* set push delay value for Supaplex elements for newer engine versions */
3107 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3109 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3111 if (IS_SP_ELEMENT(i))
3113 /* set SP push delay to just enough to push under a falling zonk */
3114 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3116 element_info[i].push_delay_fixed = delay;
3117 element_info[i].push_delay_random = 0;
3122 /* ---------- initialize move stepsize ----------------------------------- */
3124 /* initialize move stepsize values to default */
3125 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3126 if (!IS_CUSTOM_ELEMENT(i))
3127 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3129 /* set move stepsize value for certain elements from pre-defined list */
3130 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3132 int e = move_stepsize_list[i].element;
3134 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3137 /* ---------- initialize collect score ----------------------------------- */
3139 /* initialize collect score values for custom elements from initial value */
3140 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141 if (IS_CUSTOM_ELEMENT(i))
3142 element_info[i].collect_score = element_info[i].collect_score_initial;
3144 /* ---------- initialize collect count ----------------------------------- */
3146 /* initialize collect count values for non-custom elements */
3147 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3148 if (!IS_CUSTOM_ELEMENT(i))
3149 element_info[i].collect_count_initial = 0;
3151 /* add collect count values for all elements from pre-defined list */
3152 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3153 element_info[collect_count_list[i].element].collect_count_initial =
3154 collect_count_list[i].count;
3156 /* ---------- initialize access direction -------------------------------- */
3158 /* initialize access direction values to default (access from every side) */
3159 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3160 if (!IS_CUSTOM_ELEMENT(i))
3161 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3163 /* set access direction value for certain elements from pre-defined list */
3164 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3165 element_info[access_direction_list[i].element].access_direction =
3166 access_direction_list[i].direction;
3168 /* ---------- initialize explosion content ------------------------------- */
3169 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171 if (IS_CUSTOM_ELEMENT(i))
3174 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3176 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3178 element_info[i].content.e[x][y] =
3179 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3180 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3181 i == EL_PLAYER_3 ? EL_EMERALD :
3182 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3183 i == EL_MOLE ? EL_EMERALD_RED :
3184 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3185 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3186 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3187 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3188 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3189 i == EL_WALL_EMERALD ? EL_EMERALD :
3190 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3191 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3192 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3193 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3194 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3195 i == EL_WALL_PEARL ? EL_PEARL :
3196 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3201 /* ---------- initialize recursion detection ------------------------------ */
3202 recursion_loop_depth = 0;
3203 recursion_loop_detected = FALSE;
3204 recursion_loop_element = EL_UNDEFINED;
3206 /* ---------- initialize graphics engine ---------------------------------- */
3207 game.scroll_delay_value =
3208 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3209 setup.scroll_delay ? setup.scroll_delay_value : 0);
3210 game.scroll_delay_value =
3211 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3213 /* ---------- initialize game engine snapshots ---------------------------- */
3214 for (i = 0; i < MAX_PLAYERS; i++)
3215 game.snapshot.last_action[i] = 0;
3216 game.snapshot.changed_action = FALSE;
3217 game.snapshot.collected_item = FALSE;
3218 game.snapshot.mode =
3219 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3220 SNAPSHOT_MODE_EVERY_STEP :
3221 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3222 SNAPSHOT_MODE_EVERY_MOVE :
3223 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3224 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3225 game.snapshot.save_snapshot = FALSE;
3227 /* ---------- initialize level time for Supaplex engine ------------------- */
3228 /* Supaplex levels with time limit currently unsupported -- should be added */
3229 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3233 int get_num_special_action(int element, int action_first, int action_last)
3235 int num_special_action = 0;
3238 for (i = action_first; i <= action_last; i++)
3240 boolean found = FALSE;
3242 for (j = 0; j < NUM_DIRECTIONS; j++)
3243 if (el_act_dir2img(element, i, j) !=
3244 el_act_dir2img(element, ACTION_DEFAULT, j))
3248 num_special_action++;
3253 return num_special_action;
3258 =============================================================================
3260 -----------------------------------------------------------------------------
3261 initialize and start new game
3262 =============================================================================
3267 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3268 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3269 int fade_mask = REDRAW_FIELD;
3271 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3272 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3273 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3274 int initial_move_dir = MV_DOWN;
3277 // required here to update video display before fading (FIX THIS)
3278 DrawMaskedBorder(REDRAW_DOOR_2);
3280 if (!game.restart_level)
3281 CloseDoor(DOOR_CLOSE_1);
3283 SetGameStatus(GAME_MODE_PLAYING);
3285 if (level_editor_test_game)
3286 FadeSkipNextFadeIn();
3288 FadeSetEnterScreen();
3290 if (CheckIfGlobalBorderHasChanged())
3291 fade_mask = REDRAW_ALL;
3293 FadeLevelSoundsAndMusic();
3295 ExpireSoundLoops(TRUE);
3299 /* needed if different viewport properties defined for playing */
3300 ChangeViewportPropertiesIfNeeded();
3304 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3306 DrawCompleteVideoDisplay();
3309 InitGameControlValues();
3311 /* don't play tapes over network */
3312 network_playing = (options.network && !tape.playing);
3314 for (i = 0; i < MAX_PLAYERS; i++)
3316 struct PlayerInfo *player = &stored_player[i];
3318 player->index_nr = i;
3319 player->index_bit = (1 << i);
3320 player->element_nr = EL_PLAYER_1 + i;
3322 player->present = FALSE;
3323 player->active = FALSE;
3324 player->mapped = FALSE;
3326 player->killed = FALSE;
3327 player->reanimated = FALSE;
3330 player->effective_action = 0;
3331 player->programmed_action = 0;
3334 player->score_final = 0;
3336 player->gems_still_needed = level.gems_needed;
3337 player->sokobanfields_still_needed = 0;
3338 player->lights_still_needed = 0;
3339 player->friends_still_needed = 0;
3341 for (j = 0; j < MAX_NUM_KEYS; j++)
3342 player->key[j] = FALSE;
3344 player->num_white_keys = 0;
3346 player->dynabomb_count = 0;
3347 player->dynabomb_size = 1;
3348 player->dynabombs_left = 0;
3349 player->dynabomb_xl = FALSE;
3351 player->MovDir = initial_move_dir;
3354 player->GfxDir = initial_move_dir;
3355 player->GfxAction = ACTION_DEFAULT;
3357 player->StepFrame = 0;
3359 player->initial_element = player->element_nr;
3360 player->artwork_element =
3361 (level.use_artwork_element[i] ? level.artwork_element[i] :
3362 player->element_nr);
3363 player->use_murphy = FALSE;
3365 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3366 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3368 player->gravity = level.initial_player_gravity[i];
3370 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3372 player->actual_frame_counter = 0;
3374 player->step_counter = 0;
3376 player->last_move_dir = initial_move_dir;
3378 player->is_active = FALSE;
3380 player->is_waiting = FALSE;
3381 player->is_moving = FALSE;
3382 player->is_auto_moving = FALSE;
3383 player->is_digging = FALSE;
3384 player->is_snapping = FALSE;
3385 player->is_collecting = FALSE;
3386 player->is_pushing = FALSE;
3387 player->is_switching = FALSE;
3388 player->is_dropping = FALSE;
3389 player->is_dropping_pressed = FALSE;
3391 player->is_bored = FALSE;
3392 player->is_sleeping = FALSE;
3394 player->was_waiting = TRUE;
3395 player->was_moving = FALSE;
3396 player->was_snapping = FALSE;
3397 player->was_dropping = FALSE;
3399 player->force_dropping = FALSE;
3401 player->frame_counter_bored = -1;
3402 player->frame_counter_sleeping = -1;
3404 player->anim_delay_counter = 0;
3405 player->post_delay_counter = 0;
3407 player->dir_waiting = initial_move_dir;
3408 player->action_waiting = ACTION_DEFAULT;
3409 player->last_action_waiting = ACTION_DEFAULT;
3410 player->special_action_bored = ACTION_DEFAULT;
3411 player->special_action_sleeping = ACTION_DEFAULT;
3413 player->switch_x = -1;
3414 player->switch_y = -1;
3416 player->drop_x = -1;
3417 player->drop_y = -1;
3419 player->show_envelope = 0;
3421 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3423 player->push_delay = -1; /* initialized when pushing starts */
3424 player->push_delay_value = game.initial_push_delay_value;
3426 player->drop_delay = 0;
3427 player->drop_pressed_delay = 0;
3429 player->last_jx = -1;
3430 player->last_jy = -1;
3434 player->shield_normal_time_left = 0;
3435 player->shield_deadly_time_left = 0;
3437 player->inventory_infinite_element = EL_UNDEFINED;
3438 player->inventory_size = 0;
3440 if (level.use_initial_inventory[i])
3442 for (j = 0; j < level.initial_inventory_size[i]; j++)
3444 int element = level.initial_inventory_content[i][j];
3445 int collect_count = element_info[element].collect_count_initial;
3448 if (!IS_CUSTOM_ELEMENT(element))
3451 if (collect_count == 0)
3452 player->inventory_infinite_element = element;
3454 for (k = 0; k < collect_count; k++)
3455 if (player->inventory_size < MAX_INVENTORY_SIZE)
3456 player->inventory_element[player->inventory_size++] = element;
3460 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3461 SnapField(player, 0, 0);
3463 player->LevelSolved = FALSE;
3464 player->GameOver = FALSE;
3466 player->LevelSolved_GameWon = FALSE;
3467 player->LevelSolved_GameEnd = FALSE;
3468 player->LevelSolved_PanelOff = FALSE;
3469 player->LevelSolved_SaveTape = FALSE;
3470 player->LevelSolved_SaveScore = FALSE;
3471 player->LevelSolved_CountingTime = 0;
3472 player->LevelSolved_CountingScore = 0;
3474 map_player_action[i] = i;
3477 network_player_action_received = FALSE;
3479 #if defined(NETWORK_AVALIABLE)
3480 /* initial null action */
3481 if (network_playing)
3482 SendToServer_MovePlayer(MV_NONE);
3491 TimeLeft = level.time;
3494 ScreenMovDir = MV_NONE;
3498 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3500 AllPlayersGone = FALSE;
3502 game.no_time_limit = (level.time == 0);
3504 game.yamyam_content_nr = 0;
3505 game.robot_wheel_active = FALSE;
3506 game.magic_wall_active = FALSE;
3507 game.magic_wall_time_left = 0;
3508 game.light_time_left = 0;
3509 game.timegate_time_left = 0;
3510 game.switchgate_pos = 0;
3511 game.wind_direction = level.wind_direction_initial;
3513 game.lenses_time_left = 0;
3514 game.magnify_time_left = 0;
3516 game.ball_state = level.ball_state_initial;
3517 game.ball_content_nr = 0;
3519 game.envelope_active = FALSE;
3521 /* set focus to local player for network games, else to all players */
3522 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3523 game.centered_player_nr_next = game.centered_player_nr;
3524 game.set_centered_player = FALSE;
3526 if (network_playing && tape.recording)
3528 /* store client dependent player focus when recording network games */
3529 tape.centered_player_nr_next = game.centered_player_nr_next;
3530 tape.set_centered_player = TRUE;
3533 for (i = 0; i < NUM_BELTS; i++)
3535 game.belt_dir[i] = MV_NONE;
3536 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3539 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3540 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3542 #if DEBUG_INIT_PLAYER
3545 printf("Player status at level initialization:\n");
3549 SCAN_PLAYFIELD(x, y)
3551 Feld[x][y] = level.field[x][y];
3552 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3553 ChangeDelay[x][y] = 0;
3554 ChangePage[x][y] = -1;
3555 CustomValue[x][y] = 0; /* initialized in InitField() */
3556 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3558 WasJustMoving[x][y] = 0;
3559 WasJustFalling[x][y] = 0;
3560 CheckCollision[x][y] = 0;
3561 CheckImpact[x][y] = 0;
3563 Pushed[x][y] = FALSE;
3565 ChangeCount[x][y] = 0;
3566 ChangeEvent[x][y] = -1;
3568 ExplodePhase[x][y] = 0;
3569 ExplodeDelay[x][y] = 0;
3570 ExplodeField[x][y] = EX_TYPE_NONE;
3572 RunnerVisit[x][y] = 0;
3573 PlayerVisit[x][y] = 0;
3576 GfxRandom[x][y] = INIT_GFX_RANDOM();
3577 GfxElement[x][y] = EL_UNDEFINED;
3578 GfxAction[x][y] = ACTION_DEFAULT;
3579 GfxDir[x][y] = MV_NONE;
3580 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3583 SCAN_PLAYFIELD(x, y)
3585 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3587 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3589 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3592 InitField(x, y, TRUE);
3594 ResetGfxAnimation(x, y);
3599 for (i = 0; i < MAX_PLAYERS; i++)
3601 struct PlayerInfo *player = &stored_player[i];
3603 /* set number of special actions for bored and sleeping animation */
3604 player->num_special_action_bored =
3605 get_num_special_action(player->artwork_element,
3606 ACTION_BORING_1, ACTION_BORING_LAST);
3607 player->num_special_action_sleeping =
3608 get_num_special_action(player->artwork_element,
3609 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3612 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3613 emulate_sb ? EMU_SOKOBAN :
3614 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3616 /* initialize type of slippery elements */
3617 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3619 if (!IS_CUSTOM_ELEMENT(i))
3621 /* default: elements slip down either to the left or right randomly */
3622 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3624 /* SP style elements prefer to slip down on the left side */
3625 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3626 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3628 /* BD style elements prefer to slip down on the left side */
3629 if (game.emulation == EMU_BOULDERDASH)
3630 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3634 /* initialize explosion and ignition delay */
3635 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637 if (!IS_CUSTOM_ELEMENT(i))
3640 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3641 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3642 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3643 int last_phase = (num_phase + 1) * delay;
3644 int half_phase = (num_phase / 2) * delay;
3646 element_info[i].explosion_delay = last_phase - 1;
3647 element_info[i].ignition_delay = half_phase;
3649 if (i == EL_BLACK_ORB)
3650 element_info[i].ignition_delay = 1;
3654 /* correct non-moving belts to start moving left */
3655 for (i = 0; i < NUM_BELTS; i++)
3656 if (game.belt_dir[i] == MV_NONE)
3657 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3659 #if USE_NEW_PLAYER_ASSIGNMENTS
3660 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3661 /* choose default local player */
3662 local_player = &stored_player[0];
3664 for (i = 0; i < MAX_PLAYERS; i++)
3665 stored_player[i].connected = FALSE;
3667 local_player->connected = TRUE;
3668 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3672 for (i = 0; i < MAX_PLAYERS; i++)
3673 stored_player[i].connected = tape.player_participates[i];
3675 else if (game.team_mode && !options.network)
3677 /* try to guess locally connected team mode players (needed for correct
3678 assignment of player figures from level to locally playing players) */
3680 for (i = 0; i < MAX_PLAYERS; i++)
3681 if (setup.input[i].use_joystick ||
3682 setup.input[i].key.left != KSYM_UNDEFINED)
3683 stored_player[i].connected = TRUE;
3686 #if DEBUG_INIT_PLAYER
3689 printf("Player status after level initialization:\n");
3691 for (i = 0; i < MAX_PLAYERS; i++)
3693 struct PlayerInfo *player = &stored_player[i];
3695 printf("- player %d: present == %d, connected == %d, active == %d",
3701 if (local_player == player)
3702 printf(" (local player)");
3709 #if DEBUG_INIT_PLAYER
3711 printf("Reassigning players ...\n");
3714 /* check if any connected player was not found in playfield */
3715 for (i = 0; i < MAX_PLAYERS; i++)
3717 struct PlayerInfo *player = &stored_player[i];
3719 if (player->connected && !player->present)
3721 struct PlayerInfo *field_player = NULL;
3723 #if DEBUG_INIT_PLAYER
3725 printf("- looking for field player for player %d ...\n", i + 1);
3728 /* assign first free player found that is present in the playfield */
3730 /* first try: look for unmapped playfield player that is not connected */
3731 for (j = 0; j < MAX_PLAYERS; j++)
3732 if (field_player == NULL &&
3733 stored_player[j].present &&
3734 !stored_player[j].mapped &&
3735 !stored_player[j].connected)
3736 field_player = &stored_player[j];
3738 /* second try: look for *any* unmapped playfield player */
3739 for (j = 0; j < MAX_PLAYERS; j++)
3740 if (field_player == NULL &&
3741 stored_player[j].present &&
3742 !stored_player[j].mapped)
3743 field_player = &stored_player[j];
3745 if (field_player != NULL)
3747 int jx = field_player->jx, jy = field_player->jy;
3749 #if DEBUG_INIT_PLAYER
3751 printf("- found player %d\n", field_player->index_nr + 1);
3754 player->present = FALSE;
3755 player->active = FALSE;
3757 field_player->present = TRUE;
3758 field_player->active = TRUE;
3761 player->initial_element = field_player->initial_element;
3762 player->artwork_element = field_player->artwork_element;
3764 player->block_last_field = field_player->block_last_field;
3765 player->block_delay_adjustment = field_player->block_delay_adjustment;
3768 StorePlayer[jx][jy] = field_player->element_nr;
3770 field_player->jx = field_player->last_jx = jx;
3771 field_player->jy = field_player->last_jy = jy;
3773 if (local_player == player)
3774 local_player = field_player;
3776 map_player_action[field_player->index_nr] = i;
3778 field_player->mapped = TRUE;
3780 #if DEBUG_INIT_PLAYER
3782 printf("- map_player_action[%d] == %d\n",
3783 field_player->index_nr + 1, i + 1);
3788 if (player->connected && player->present)
3789 player->mapped = TRUE;
3792 #if DEBUG_INIT_PLAYER
3795 printf("Player status after player assignment (first stage):\n");
3797 for (i = 0; i < MAX_PLAYERS; i++)
3799 struct PlayerInfo *player = &stored_player[i];
3801 printf("- player %d: present == %d, connected == %d, active == %d",
3807 if (local_player == player)
3808 printf(" (local player)");
3817 /* check if any connected player was not found in playfield */
3818 for (i = 0; i < MAX_PLAYERS; i++)
3820 struct PlayerInfo *player = &stored_player[i];
3822 if (player->connected && !player->present)
3824 for (j = 0; j < MAX_PLAYERS; j++)
3826 struct PlayerInfo *field_player = &stored_player[j];
3827 int jx = field_player->jx, jy = field_player->jy;
3829 /* assign first free player found that is present in the playfield */
3830 if (field_player->present && !field_player->connected)
3832 player->present = TRUE;
3833 player->active = TRUE;
3835 field_player->present = FALSE;
3836 field_player->active = FALSE;
3838 player->initial_element = field_player->initial_element;
3839 player->artwork_element = field_player->artwork_element;
3841 player->block_last_field = field_player->block_last_field;
3842 player->block_delay_adjustment = field_player->block_delay_adjustment;
3844 StorePlayer[jx][jy] = player->element_nr;
3846 player->jx = player->last_jx = jx;
3847 player->jy = player->last_jy = jy;
3857 printf("::: local_player->present == %d\n", local_player->present);
3862 /* when playing a tape, eliminate all players who do not participate */
3864 #if USE_NEW_PLAYER_ASSIGNMENTS
3866 if (!game.team_mode)
3868 for (i = 0; i < MAX_PLAYERS; i++)
3870 if (stored_player[i].active &&
3871 !tape.player_participates[map_player_action[i]])
3873 struct PlayerInfo *player = &stored_player[i];
3874 int jx = player->jx, jy = player->jy;
3876 #if DEBUG_INIT_PLAYER
3878 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3881 player->active = FALSE;
3882 StorePlayer[jx][jy] = 0;
3883 Feld[jx][jy] = EL_EMPTY;
3890 for (i = 0; i < MAX_PLAYERS; i++)
3892 if (stored_player[i].active &&
3893 !tape.player_participates[i])
3895 struct PlayerInfo *player = &stored_player[i];
3896 int jx = player->jx, jy = player->jy;
3898 player->active = FALSE;
3899 StorePlayer[jx][jy] = 0;
3900 Feld[jx][jy] = EL_EMPTY;
3905 else if (!options.network && !game.team_mode) /* && !tape.playing */
3907 /* when in single player mode, eliminate all but the first active player */
3909 for (i = 0; i < MAX_PLAYERS; i++)
3911 if (stored_player[i].active)
3913 for (j = i + 1; j < MAX_PLAYERS; j++)
3915 if (stored_player[j].active)
3917 struct PlayerInfo *player = &stored_player[j];
3918 int jx = player->jx, jy = player->jy;
3920 player->active = FALSE;
3921 player->present = FALSE;
3923 StorePlayer[jx][jy] = 0;
3924 Feld[jx][jy] = EL_EMPTY;
3931 /* when recording the game, store which players take part in the game */
3934 #if USE_NEW_PLAYER_ASSIGNMENTS
3935 for (i = 0; i < MAX_PLAYERS; i++)
3936 if (stored_player[i].connected)
3937 tape.player_participates[i] = TRUE;
3939 for (i = 0; i < MAX_PLAYERS; i++)
3940 if (stored_player[i].active)
3941 tape.player_participates[i] = TRUE;
3945 #if DEBUG_INIT_PLAYER
3948 printf("Player status after player assignment (final stage):\n");
3950 for (i = 0; i < MAX_PLAYERS; i++)
3952 struct PlayerInfo *player = &stored_player[i];
3954 printf("- player %d: present == %d, connected == %d, active == %d",
3960 if (local_player == player)
3961 printf(" (local player)");
3968 if (BorderElement == EL_EMPTY)
3971 SBX_Right = lev_fieldx - SCR_FIELDX;
3973 SBY_Lower = lev_fieldy - SCR_FIELDY;
3978 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3980 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3983 if (full_lev_fieldx <= SCR_FIELDX)
3984 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3985 if (full_lev_fieldy <= SCR_FIELDY)
3986 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3988 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3990 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3993 /* if local player not found, look for custom element that might create
3994 the player (make some assumptions about the right custom element) */
3995 if (!local_player->present)
3997 int start_x = 0, start_y = 0;
3998 int found_rating = 0;
3999 int found_element = EL_UNDEFINED;
4000 int player_nr = local_player->index_nr;
4002 SCAN_PLAYFIELD(x, y)
4004 int element = Feld[x][y];
4009 if (level.use_start_element[player_nr] &&
4010 level.start_element[player_nr] == element &&
4017 found_element = element;
4020 if (!IS_CUSTOM_ELEMENT(element))
4023 if (CAN_CHANGE(element))
4025 for (i = 0; i < element_info[element].num_change_pages; i++)
4027 /* check for player created from custom element as single target */
4028 content = element_info[element].change_page[i].target_element;
4029 is_player = ELEM_IS_PLAYER(content);
4031 if (is_player && (found_rating < 3 ||
4032 (found_rating == 3 && element < found_element)))
4038 found_element = element;
4043 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4045 /* check for player created from custom element as explosion content */
4046 content = element_info[element].content.e[xx][yy];
4047 is_player = ELEM_IS_PLAYER(content);
4049 if (is_player && (found_rating < 2 ||
4050 (found_rating == 2 && element < found_element)))
4052 start_x = x + xx - 1;
4053 start_y = y + yy - 1;
4056 found_element = element;
4059 if (!CAN_CHANGE(element))
4062 for (i = 0; i < element_info[element].num_change_pages; i++)
4064 /* check for player created from custom element as extended target */
4066 element_info[element].change_page[i].target_content.e[xx][yy];
4068 is_player = ELEM_IS_PLAYER(content);
4070 if (is_player && (found_rating < 1 ||
4071 (found_rating == 1 && element < found_element)))
4073 start_x = x + xx - 1;
4074 start_y = y + yy - 1;
4077 found_element = element;
4083 scroll_x = SCROLL_POSITION_X(start_x);
4084 scroll_y = SCROLL_POSITION_Y(start_y);
4088 scroll_x = SCROLL_POSITION_X(local_player->jx);
4089 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4092 /* !!! FIX THIS (START) !!! */
4093 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4095 InitGameEngine_EM();
4097 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4099 InitGameEngine_SP();
4101 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4103 InitGameEngine_MM();
4107 DrawLevel(REDRAW_FIELD);
4110 /* after drawing the level, correct some elements */
4111 if (game.timegate_time_left == 0)
4112 CloseAllOpenTimegates();
4115 /* blit playfield from scroll buffer to normal back buffer for fading in */
4116 BlitScreenToBitmap(backbuffer);
4117 /* !!! FIX THIS (END) !!! */
4119 DrawMaskedBorder(fade_mask);
4124 // full screen redraw is required at this point in the following cases:
4125 // - special editor door undrawn when game was started from level editor
4126 // - drawing area (playfield) was changed and has to be removed completely
4127 redraw_mask = REDRAW_ALL;
4131 if (!game.restart_level)
4133 /* copy default game door content to main double buffer */
4135 /* !!! CHECK AGAIN !!! */
4136 SetPanelBackground();
4137 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4138 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4141 SetPanelBackground();
4142 SetDrawBackgroundMask(REDRAW_DOOR_1);
4144 UpdateAndDisplayGameControlValues();
4146 if (!game.restart_level)
4152 CreateGameButtons();
4154 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4155 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4156 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4161 /* copy actual game door content to door double buffer for OpenDoor() */
4162 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4164 OpenDoor(DOOR_OPEN_ALL);
4166 PlaySound(SND_GAME_STARTING);
4168 if (setup.sound_music)
4171 KeyboardAutoRepeatOffUnlessAutoplay();
4173 #if DEBUG_INIT_PLAYER
4176 printf("Player status (final):\n");
4178 for (i = 0; i < MAX_PLAYERS; i++)
4180 struct PlayerInfo *player = &stored_player[i];
4182 printf("- player %d: present == %d, connected == %d, active == %d",
4188 if (local_player == player)
4189 printf(" (local player)");
4202 if (!game.restart_level && !tape.playing)
4204 LevelStats_incPlayed(level_nr);
4206 SaveLevelSetup_SeriesInfo();
4209 game.restart_level = FALSE;
4211 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4212 InitGameActions_MM();
4214 SaveEngineSnapshotToListInitial();
4217 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4218 int actual_player_x, int actual_player_y)
4220 /* this is used for non-R'n'D game engines to update certain engine values */
4222 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4224 actual_player_x = correctLevelPosX_EM(actual_player_x);
4225 actual_player_y = correctLevelPosY_EM(actual_player_y);
4228 /* needed to determine if sounds are played within the visible screen area */
4229 scroll_x = actual_scroll_x;
4230 scroll_y = actual_scroll_y;
4232 /* needed to get player position for "follow finger" playing input method */
4233 local_player->jx = actual_player_x;
4234 local_player->jy = actual_player_y;
4237 void InitMovDir(int x, int y)
4239 int i, element = Feld[x][y];
4240 static int xy[4][2] =
4247 static int direction[3][4] =
4249 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4250 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4251 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4260 Feld[x][y] = EL_BUG;
4261 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4264 case EL_SPACESHIP_RIGHT:
4265 case EL_SPACESHIP_UP:
4266 case EL_SPACESHIP_LEFT:
4267 case EL_SPACESHIP_DOWN:
4268 Feld[x][y] = EL_SPACESHIP;
4269 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4272 case EL_BD_BUTTERFLY_RIGHT:
4273 case EL_BD_BUTTERFLY_UP:
4274 case EL_BD_BUTTERFLY_LEFT:
4275 case EL_BD_BUTTERFLY_DOWN:
4276 Feld[x][y] = EL_BD_BUTTERFLY;
4277 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4280 case EL_BD_FIREFLY_RIGHT:
4281 case EL_BD_FIREFLY_UP:
4282 case EL_BD_FIREFLY_LEFT:
4283 case EL_BD_FIREFLY_DOWN:
4284 Feld[x][y] = EL_BD_FIREFLY;
4285 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4288 case EL_PACMAN_RIGHT:
4290 case EL_PACMAN_LEFT:
4291 case EL_PACMAN_DOWN:
4292 Feld[x][y] = EL_PACMAN;
4293 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4296 case EL_YAMYAM_LEFT:
4297 case EL_YAMYAM_RIGHT:
4299 case EL_YAMYAM_DOWN:
4300 Feld[x][y] = EL_YAMYAM;
4301 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4304 case EL_SP_SNIKSNAK:
4305 MovDir[x][y] = MV_UP;
4308 case EL_SP_ELECTRON:
4309 MovDir[x][y] = MV_LEFT;
4316 Feld[x][y] = EL_MOLE;
4317 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4321 if (IS_CUSTOM_ELEMENT(element))
4323 struct ElementInfo *ei = &element_info[element];
4324 int move_direction_initial = ei->move_direction_initial;
4325 int move_pattern = ei->move_pattern;
4327 if (move_direction_initial == MV_START_PREVIOUS)
4329 if (MovDir[x][y] != MV_NONE)
4332 move_direction_initial = MV_START_AUTOMATIC;
4335 if (move_direction_initial == MV_START_RANDOM)
4336 MovDir[x][y] = 1 << RND(4);
4337 else if (move_direction_initial & MV_ANY_DIRECTION)
4338 MovDir[x][y] = move_direction_initial;
4339 else if (move_pattern == MV_ALL_DIRECTIONS ||
4340 move_pattern == MV_TURNING_LEFT ||
4341 move_pattern == MV_TURNING_RIGHT ||
4342 move_pattern == MV_TURNING_LEFT_RIGHT ||
4343 move_pattern == MV_TURNING_RIGHT_LEFT ||
4344 move_pattern == MV_TURNING_RANDOM)
4345 MovDir[x][y] = 1 << RND(4);
4346 else if (move_pattern == MV_HORIZONTAL)
4347 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4348 else if (move_pattern == MV_VERTICAL)
4349 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4350 else if (move_pattern & MV_ANY_DIRECTION)
4351 MovDir[x][y] = element_info[element].move_pattern;
4352 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4353 move_pattern == MV_ALONG_RIGHT_SIDE)
4355 /* use random direction as default start direction */
4356 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4357 MovDir[x][y] = 1 << RND(4);
4359 for (i = 0; i < NUM_DIRECTIONS; i++)
4361 int x1 = x + xy[i][0];
4362 int y1 = y + xy[i][1];
4364 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4366 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4367 MovDir[x][y] = direction[0][i];
4369 MovDir[x][y] = direction[1][i];
4378 MovDir[x][y] = 1 << RND(4);
4380 if (element != EL_BUG &&
4381 element != EL_SPACESHIP &&
4382 element != EL_BD_BUTTERFLY &&
4383 element != EL_BD_FIREFLY)
4386 for (i = 0; i < NUM_DIRECTIONS; i++)
4388 int x1 = x + xy[i][0];
4389 int y1 = y + xy[i][1];
4391 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4393 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4395 MovDir[x][y] = direction[0][i];
4398 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4399 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4401 MovDir[x][y] = direction[1][i];
4410 GfxDir[x][y] = MovDir[x][y];
4413 void InitAmoebaNr(int x, int y)
4416 int group_nr = AmoebeNachbarNr(x, y);
4420 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4422 if (AmoebaCnt[i] == 0)
4430 AmoebaNr[x][y] = group_nr;
4431 AmoebaCnt[group_nr]++;
4432 AmoebaCnt2[group_nr]++;
4435 static void PlayerWins(struct PlayerInfo *player)
4437 player->LevelSolved = TRUE;
4438 player->GameOver = TRUE;
4440 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4441 level.native_em_level->lev->score :
4442 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4446 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4448 player->LevelSolved_CountingScore = player->score_final;
4453 static int time, time_final;
4454 static int score, score_final;
4455 static int game_over_delay_1 = 0;
4456 static int game_over_delay_2 = 0;
4457 int game_over_delay_value_1 = 50;
4458 int game_over_delay_value_2 = 50;
4460 if (!local_player->LevelSolved_GameWon)
4464 /* do not start end game actions before the player stops moving (to exit) */
4465 if (local_player->MovPos)
4468 local_player->LevelSolved_GameWon = TRUE;
4469 local_player->LevelSolved_SaveTape = tape.recording;
4470 local_player->LevelSolved_SaveScore = !tape.playing;
4474 LevelStats_incSolved(level_nr);
4476 SaveLevelSetup_SeriesInfo();
4479 if (tape.auto_play) /* tape might already be stopped here */
4480 tape.auto_play_level_solved = TRUE;
4484 game_over_delay_1 = game_over_delay_value_1;
4485 game_over_delay_2 = game_over_delay_value_2;
4487 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4488 score = score_final = local_player->score_final;
4493 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4495 else if (game.no_time_limit && TimePlayed < 999)
4498 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4501 local_player->score_final = score_final;
4503 if (level_editor_test_game)
4506 score = score_final;
4508 local_player->LevelSolved_CountingTime = time;
4509 local_player->LevelSolved_CountingScore = score;
4511 game_panel_controls[GAME_PANEL_TIME].value = time;
4512 game_panel_controls[GAME_PANEL_SCORE].value = score;
4514 DisplayGameControlValues();
4517 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4519 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4521 /* close exit door after last player */
4522 if ((AllPlayersGone &&
4523 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4524 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4525 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4526 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4527 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4529 int element = Feld[ExitX][ExitY];
4531 Feld[ExitX][ExitY] =
4532 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4533 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4534 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4535 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4536 EL_EM_STEEL_EXIT_CLOSING);
4538 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4541 /* player disappears */
4542 DrawLevelField(ExitX, ExitY);
4545 for (i = 0; i < MAX_PLAYERS; i++)
4547 struct PlayerInfo *player = &stored_player[i];
4549 if (player->present)
4551 RemovePlayer(player);
4553 /* player disappears */
4554 DrawLevelField(player->jx, player->jy);
4559 PlaySound(SND_GAME_WINNING);
4562 if (game_over_delay_1 > 0)
4564 game_over_delay_1--;
4569 if (time != time_final)
4571 int time_to_go = ABS(time_final - time);
4572 int time_count_dir = (time < time_final ? +1 : -1);
4573 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4575 time += time_count_steps * time_count_dir;
4576 score += time_count_steps * level.score[SC_TIME_BONUS];
4578 local_player->LevelSolved_CountingTime = time;
4579 local_player->LevelSolved_CountingScore = score;
4581 game_panel_controls[GAME_PANEL_TIME].value = time;
4582 game_panel_controls[GAME_PANEL_SCORE].value = score;
4584 DisplayGameControlValues();
4586 if (time == time_final)
4587 StopSound(SND_GAME_LEVELTIME_BONUS);
4588 else if (setup.sound_loops)
4589 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4591 PlaySound(SND_GAME_LEVELTIME_BONUS);
4596 local_player->LevelSolved_PanelOff = TRUE;
4598 if (game_over_delay_2 > 0)
4600 game_over_delay_2--;
4611 boolean raise_level = FALSE;
4613 local_player->LevelSolved_GameEnd = TRUE;
4615 if (!global.use_envelope_request)
4616 CloseDoor(DOOR_CLOSE_1);
4618 if (local_player->LevelSolved_SaveTape)
4620 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4623 CloseDoor(DOOR_CLOSE_ALL);
4625 if (level_editor_test_game)
4627 SetGameStatus(GAME_MODE_MAIN);
4634 if (!local_player->LevelSolved_SaveScore)
4636 SetGameStatus(GAME_MODE_MAIN);
4643 if (level_nr == leveldir_current->handicap_level)
4645 leveldir_current->handicap_level++;
4647 SaveLevelSetup_SeriesInfo();
4650 if (setup.increment_levels &&
4651 level_nr < leveldir_current->last_level)
4652 raise_level = TRUE; /* advance to next level */
4654 if ((hi_pos = NewHiScore()) >= 0)
4656 SetGameStatus(GAME_MODE_SCORES);
4658 DrawHallOfFame(hi_pos);
4668 SetGameStatus(GAME_MODE_MAIN);
4684 boolean one_score_entry_per_name = !program.many_scores_per_name;
4686 LoadScore(level_nr);
4688 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4689 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4692 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4694 if (local_player->score_final > highscore[k].Score)
4696 /* player has made it to the hall of fame */
4698 if (k < MAX_SCORE_ENTRIES - 1)
4700 int m = MAX_SCORE_ENTRIES - 1;
4702 if (one_score_entry_per_name)
4704 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4705 if (strEqual(setup.player_name, highscore[l].Name))
4708 if (m == k) /* player's new highscore overwrites his old one */
4712 for (l = m; l > k; l--)
4714 strcpy(highscore[l].Name, highscore[l - 1].Name);
4715 highscore[l].Score = highscore[l - 1].Score;
4721 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4722 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4723 highscore[k].Score = local_player->score_final;
4728 else if (one_score_entry_per_name &&
4729 !strncmp(setup.player_name, highscore[k].Name,
4730 MAX_PLAYER_NAME_LEN))
4731 break; /* player already there with a higher score */
4735 SaveScore(level_nr);
4740 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4742 int element = Feld[x][y];
4743 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4744 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4745 int horiz_move = (dx != 0);
4746 int sign = (horiz_move ? dx : dy);
4747 int step = sign * element_info[element].move_stepsize;
4749 /* special values for move stepsize for spring and things on conveyor belt */
4752 if (CAN_FALL(element) &&
4753 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4754 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4755 else if (element == EL_SPRING)
4756 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4762 inline static int getElementMoveStepsize(int x, int y)
4764 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4767 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4769 if (player->GfxAction != action || player->GfxDir != dir)
4771 player->GfxAction = action;
4772 player->GfxDir = dir;
4774 player->StepFrame = 0;
4778 static void ResetGfxFrame(int x, int y)
4780 // profiling showed that "autotest" spends 10~20% of its time in this function
4781 if (DrawingDeactivatedField())
4784 int element = Feld[x][y];
4785 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4787 if (graphic_info[graphic].anim_global_sync)
4788 GfxFrame[x][y] = FrameCounter;
4789 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4790 GfxFrame[x][y] = CustomValue[x][y];
4791 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4792 GfxFrame[x][y] = element_info[element].collect_score;
4793 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4794 GfxFrame[x][y] = ChangeDelay[x][y];
4797 static void ResetGfxAnimation(int x, int y)
4799 GfxAction[x][y] = ACTION_DEFAULT;
4800 GfxDir[x][y] = MovDir[x][y];
4803 ResetGfxFrame(x, y);
4806 static void ResetRandomAnimationValue(int x, int y)
4808 GfxRandom[x][y] = INIT_GFX_RANDOM();
4811 void InitMovingField(int x, int y, int direction)
4813 int element = Feld[x][y];
4814 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4815 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4818 boolean is_moving_before, is_moving_after;
4820 /* check if element was/is moving or being moved before/after mode change */
4821 is_moving_before = (WasJustMoving[x][y] != 0);
4822 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4824 /* reset animation only for moving elements which change direction of moving
4825 or which just started or stopped moving
4826 (else CEs with property "can move" / "not moving" are reset each frame) */
4827 if (is_moving_before != is_moving_after ||
4828 direction != MovDir[x][y])
4829 ResetGfxAnimation(x, y);
4831 MovDir[x][y] = direction;
4832 GfxDir[x][y] = direction;
4834 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4835 direction == MV_DOWN && CAN_FALL(element) ?
4836 ACTION_FALLING : ACTION_MOVING);
4838 /* this is needed for CEs with property "can move" / "not moving" */
4840 if (is_moving_after)
4842 if (Feld[newx][newy] == EL_EMPTY)
4843 Feld[newx][newy] = EL_BLOCKED;
4845 MovDir[newx][newy] = MovDir[x][y];
4847 CustomValue[newx][newy] = CustomValue[x][y];
4849 GfxFrame[newx][newy] = GfxFrame[x][y];
4850 GfxRandom[newx][newy] = GfxRandom[x][y];
4851 GfxAction[newx][newy] = GfxAction[x][y];
4852 GfxDir[newx][newy] = GfxDir[x][y];
4856 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4858 int direction = MovDir[x][y];
4859 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4860 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4866 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4868 int oldx = x, oldy = y;
4869 int direction = MovDir[x][y];
4871 if (direction == MV_LEFT)
4873 else if (direction == MV_RIGHT)
4875 else if (direction == MV_UP)
4877 else if (direction == MV_DOWN)
4880 *comes_from_x = oldx;
4881 *comes_from_y = oldy;
4884 int MovingOrBlocked2Element(int x, int y)
4886 int element = Feld[x][y];
4888 if (element == EL_BLOCKED)
4892 Blocked2Moving(x, y, &oldx, &oldy);
4893 return Feld[oldx][oldy];
4899 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4901 /* like MovingOrBlocked2Element(), but if element is moving
4902 and (x,y) is the field the moving element is just leaving,
4903 return EL_BLOCKED instead of the element value */
4904 int element = Feld[x][y];
4906 if (IS_MOVING(x, y))
4908 if (element == EL_BLOCKED)
4912 Blocked2Moving(x, y, &oldx, &oldy);
4913 return Feld[oldx][oldy];
4922 static void RemoveField(int x, int y)
4924 Feld[x][y] = EL_EMPTY;
4930 CustomValue[x][y] = 0;
4933 ChangeDelay[x][y] = 0;
4934 ChangePage[x][y] = -1;
4935 Pushed[x][y] = FALSE;
4937 GfxElement[x][y] = EL_UNDEFINED;
4938 GfxAction[x][y] = ACTION_DEFAULT;
4939 GfxDir[x][y] = MV_NONE;
4942 void RemoveMovingField(int x, int y)
4944 int oldx = x, oldy = y, newx = x, newy = y;
4945 int element = Feld[x][y];
4946 int next_element = EL_UNDEFINED;
4948 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4951 if (IS_MOVING(x, y))
4953 Moving2Blocked(x, y, &newx, &newy);
4955 if (Feld[newx][newy] != EL_BLOCKED)
4957 /* element is moving, but target field is not free (blocked), but
4958 already occupied by something different (example: acid pool);
4959 in this case, only remove the moving field, but not the target */
4961 RemoveField(oldx, oldy);
4963 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4965 TEST_DrawLevelField(oldx, oldy);
4970 else if (element == EL_BLOCKED)
4972 Blocked2Moving(x, y, &oldx, &oldy);
4973 if (!IS_MOVING(oldx, oldy))
4977 if (element == EL_BLOCKED &&
4978 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4979 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4980 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4981 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4982 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4983 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4984 next_element = get_next_element(Feld[oldx][oldy]);
4986 RemoveField(oldx, oldy);
4987 RemoveField(newx, newy);
4989 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4991 if (next_element != EL_UNDEFINED)
4992 Feld[oldx][oldy] = next_element;
4994 TEST_DrawLevelField(oldx, oldy);
4995 TEST_DrawLevelField(newx, newy);
4998 void DrawDynamite(int x, int y)
5000 int sx = SCREENX(x), sy = SCREENY(y);
5001 int graphic = el2img(Feld[x][y]);
5004 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5007 if (IS_WALKABLE_INSIDE(Back[x][y]))
5011 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5012 else if (Store[x][y])
5013 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5015 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5017 if (Back[x][y] || Store[x][y])
5018 DrawGraphicThruMask(sx, sy, graphic, frame);
5020 DrawGraphic(sx, sy, graphic, frame);
5023 void CheckDynamite(int x, int y)
5025 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5029 if (MovDelay[x][y] != 0)
5032 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5038 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5043 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5045 boolean num_checked_players = 0;
5048 for (i = 0; i < MAX_PLAYERS; i++)
5050 if (stored_player[i].active)
5052 int sx = stored_player[i].jx;
5053 int sy = stored_player[i].jy;
5055 if (num_checked_players == 0)
5062 *sx1 = MIN(*sx1, sx);
5063 *sy1 = MIN(*sy1, sy);
5064 *sx2 = MAX(*sx2, sx);
5065 *sy2 = MAX(*sy2, sy);
5068 num_checked_players++;
5073 static boolean checkIfAllPlayersFitToScreen_RND()
5075 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5077 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5079 return (sx2 - sx1 < SCR_FIELDX &&
5080 sy2 - sy1 < SCR_FIELDY);
5083 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5085 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5087 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5089 *sx = (sx1 + sx2) / 2;
5090 *sy = (sy1 + sy2) / 2;
5093 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5094 boolean center_screen, boolean quick_relocation)
5096 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5097 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5098 boolean no_delay = (tape.warp_forward);
5099 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5100 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5101 int new_scroll_x, new_scroll_y;
5103 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5105 /* case 1: quick relocation inside visible screen (without scrolling) */
5112 if (!level.shifted_relocation || center_screen)
5114 /* relocation _with_ centering of screen */
5116 new_scroll_x = SCROLL_POSITION_X(x);
5117 new_scroll_y = SCROLL_POSITION_Y(y);
5121 /* relocation _without_ centering of screen */
5123 int center_scroll_x = SCROLL_POSITION_X(old_x);
5124 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5125 int offset_x = x + (scroll_x - center_scroll_x);
5126 int offset_y = y + (scroll_y - center_scroll_y);
5128 /* for new screen position, apply previous offset to center position */
5129 new_scroll_x = SCROLL_POSITION_X(offset_x);
5130 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5133 if (quick_relocation)
5135 /* case 2: quick relocation (redraw without visible scrolling) */
5137 scroll_x = new_scroll_x;
5138 scroll_y = new_scroll_y;
5145 /* case 3: visible relocation (with scrolling to new position) */
5147 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5149 SetVideoFrameDelay(wait_delay_value);
5151 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5154 int fx = FX, fy = FY;
5156 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5157 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5159 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5165 fx += dx * TILEX / 2;
5166 fy += dy * TILEY / 2;
5168 ScrollLevel(dx, dy);
5171 /* scroll in two steps of half tile size to make things smoother */
5172 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5174 /* scroll second step to align at full tile size */
5175 BlitScreenToBitmap(window);
5181 SetVideoFrameDelay(frame_delay_value_old);
5184 void RelocatePlayer(int jx, int jy, int el_player_raw)
5186 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5187 int player_nr = GET_PLAYER_NR(el_player);
5188 struct PlayerInfo *player = &stored_player[player_nr];
5189 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5190 boolean no_delay = (tape.warp_forward);
5191 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5192 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5193 int old_jx = player->jx;
5194 int old_jy = player->jy;
5195 int old_element = Feld[old_jx][old_jy];
5196 int element = Feld[jx][jy];
5197 boolean player_relocated = (old_jx != jx || old_jy != jy);
5199 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5200 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5201 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5202 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5203 int leave_side_horiz = move_dir_horiz;
5204 int leave_side_vert = move_dir_vert;
5205 int enter_side = enter_side_horiz | enter_side_vert;
5206 int leave_side = leave_side_horiz | leave_side_vert;
5208 if (player->GameOver) /* do not reanimate dead player */
5211 if (!player_relocated) /* no need to relocate the player */
5214 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5216 RemoveField(jx, jy); /* temporarily remove newly placed player */
5217 DrawLevelField(jx, jy);
5220 if (player->present)
5222 while (player->MovPos)
5224 ScrollPlayer(player, SCROLL_GO_ON);
5225 ScrollScreen(NULL, SCROLL_GO_ON);
5227 AdvanceFrameAndPlayerCounters(player->index_nr);
5231 BackToFront_WithFrameDelay(wait_delay_value);
5234 DrawPlayer(player); /* needed here only to cleanup last field */
5235 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5237 player->is_moving = FALSE;
5240 if (IS_CUSTOM_ELEMENT(old_element))
5241 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5243 player->index_bit, leave_side);
5245 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5247 player->index_bit, leave_side);
5249 Feld[jx][jy] = el_player;
5250 InitPlayerField(jx, jy, el_player, TRUE);
5252 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5253 possible that the relocation target field did not contain a player element,
5254 but a walkable element, to which the new player was relocated -- in this
5255 case, restore that (already initialized!) element on the player field */
5256 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5258 Feld[jx][jy] = element; /* restore previously existing element */
5261 /* only visually relocate centered player */
5262 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5263 FALSE, level.instant_relocation);
5265 TestIfPlayerTouchesBadThing(jx, jy);
5266 TestIfPlayerTouchesCustomElement(jx, jy);
5268 if (IS_CUSTOM_ELEMENT(element))
5269 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5270 player->index_bit, enter_side);
5272 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5273 player->index_bit, enter_side);
5275 if (player->is_switching)
5277 /* ensure that relocation while still switching an element does not cause
5278 a new element to be treated as also switched directly after relocation
5279 (this is important for teleporter switches that teleport the player to
5280 a place where another teleporter switch is in the same direction, which
5281 would then incorrectly be treated as immediately switched before the
5282 direction key that caused the switch was released) */
5284 player->switch_x += jx - old_jx;
5285 player->switch_y += jy - old_jy;
5289 void Explode(int ex, int ey, int phase, int mode)
5295 /* !!! eliminate this variable !!! */
5296 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5298 if (game.explosions_delayed)
5300 ExplodeField[ex][ey] = mode;
5304 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5306 int center_element = Feld[ex][ey];
5307 int artwork_element, explosion_element; /* set these values later */
5309 /* remove things displayed in background while burning dynamite */
5310 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5313 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5315 /* put moving element to center field (and let it explode there) */
5316 center_element = MovingOrBlocked2Element(ex, ey);
5317 RemoveMovingField(ex, ey);
5318 Feld[ex][ey] = center_element;
5321 /* now "center_element" is finally determined -- set related values now */
5322 artwork_element = center_element; /* for custom player artwork */
5323 explosion_element = center_element; /* for custom player artwork */
5325 if (IS_PLAYER(ex, ey))
5327 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5329 artwork_element = stored_player[player_nr].artwork_element;
5331 if (level.use_explosion_element[player_nr])
5333 explosion_element = level.explosion_element[player_nr];
5334 artwork_element = explosion_element;
5338 if (mode == EX_TYPE_NORMAL ||
5339 mode == EX_TYPE_CENTER ||
5340 mode == EX_TYPE_CROSS)
5341 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5343 last_phase = element_info[explosion_element].explosion_delay + 1;
5345 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5347 int xx = x - ex + 1;
5348 int yy = y - ey + 1;
5351 if (!IN_LEV_FIELD(x, y) ||
5352 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5353 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5356 element = Feld[x][y];
5358 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5360 element = MovingOrBlocked2Element(x, y);
5362 if (!IS_EXPLOSION_PROOF(element))
5363 RemoveMovingField(x, y);
5366 /* indestructible elements can only explode in center (but not flames) */
5367 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5368 mode == EX_TYPE_BORDER)) ||
5369 element == EL_FLAMES)
5372 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5373 behaviour, for example when touching a yamyam that explodes to rocks
5374 with active deadly shield, a rock is created under the player !!! */
5375 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5377 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5378 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5379 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5381 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5384 if (IS_ACTIVE_BOMB(element))
5386 /* re-activate things under the bomb like gate or penguin */
5387 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5394 /* save walkable background elements while explosion on same tile */
5395 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5396 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5397 Back[x][y] = element;
5399 /* ignite explodable elements reached by other explosion */
5400 if (element == EL_EXPLOSION)
5401 element = Store2[x][y];
5403 if (AmoebaNr[x][y] &&
5404 (element == EL_AMOEBA_FULL ||
5405 element == EL_BD_AMOEBA ||
5406 element == EL_AMOEBA_GROWING))
5408 AmoebaCnt[AmoebaNr[x][y]]--;
5409 AmoebaCnt2[AmoebaNr[x][y]]--;
5414 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5416 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5418 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5420 if (PLAYERINFO(ex, ey)->use_murphy)
5421 Store[x][y] = EL_EMPTY;
5424 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5425 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5426 else if (ELEM_IS_PLAYER(center_element))
5427 Store[x][y] = EL_EMPTY;
5428 else if (center_element == EL_YAMYAM)
5429 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5430 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5431 Store[x][y] = element_info[center_element].content.e[xx][yy];
5433 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5434 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5435 otherwise) -- FIX THIS !!! */
5436 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5437 Store[x][y] = element_info[element].content.e[1][1];
5439 else if (!CAN_EXPLODE(element))
5440 Store[x][y] = element_info[element].content.e[1][1];
5443 Store[x][y] = EL_EMPTY;
5445 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5446 center_element == EL_AMOEBA_TO_DIAMOND)
5447 Store2[x][y] = element;
5449 Feld[x][y] = EL_EXPLOSION;
5450 GfxElement[x][y] = artwork_element;
5452 ExplodePhase[x][y] = 1;
5453 ExplodeDelay[x][y] = last_phase;
5458 if (center_element == EL_YAMYAM)
5459 game.yamyam_content_nr =
5460 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5472 GfxFrame[x][y] = 0; /* restart explosion animation */
5474 last_phase = ExplodeDelay[x][y];
5476 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5478 /* this can happen if the player leaves an explosion just in time */
5479 if (GfxElement[x][y] == EL_UNDEFINED)
5480 GfxElement[x][y] = EL_EMPTY;
5482 border_element = Store2[x][y];
5483 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5484 border_element = StorePlayer[x][y];
5486 if (phase == element_info[border_element].ignition_delay ||
5487 phase == last_phase)
5489 boolean border_explosion = FALSE;
5491 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5492 !PLAYER_EXPLOSION_PROTECTED(x, y))
5494 KillPlayerUnlessExplosionProtected(x, y);
5495 border_explosion = TRUE;
5497 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5499 Feld[x][y] = Store2[x][y];
5502 border_explosion = TRUE;
5504 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5506 AmoebeUmwandeln(x, y);
5508 border_explosion = TRUE;
5511 /* if an element just explodes due to another explosion (chain-reaction),
5512 do not immediately end the new explosion when it was the last frame of
5513 the explosion (as it would be done in the following "if"-statement!) */
5514 if (border_explosion && phase == last_phase)
5518 if (phase == last_phase)
5522 element = Feld[x][y] = Store[x][y];
5523 Store[x][y] = Store2[x][y] = 0;
5524 GfxElement[x][y] = EL_UNDEFINED;
5526 /* player can escape from explosions and might therefore be still alive */
5527 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5528 element <= EL_PLAYER_IS_EXPLODING_4)
5530 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5531 int explosion_element = EL_PLAYER_1 + player_nr;
5532 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5533 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5535 if (level.use_explosion_element[player_nr])
5536 explosion_element = level.explosion_element[player_nr];
5538 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5539 element_info[explosion_element].content.e[xx][yy]);
5542 /* restore probably existing indestructible background element */
5543 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5544 element = Feld[x][y] = Back[x][y];
5547 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5548 GfxDir[x][y] = MV_NONE;
5549 ChangeDelay[x][y] = 0;
5550 ChangePage[x][y] = -1;
5552 CustomValue[x][y] = 0;
5554 InitField_WithBug2(x, y, FALSE);
5556 TEST_DrawLevelField(x, y);
5558 TestIfElementTouchesCustomElement(x, y);
5560 if (GFX_CRUMBLED(element))
5561 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5563 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5564 StorePlayer[x][y] = 0;
5566 if (ELEM_IS_PLAYER(element))
5567 RelocatePlayer(x, y, element);
5569 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5571 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5572 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5575 TEST_DrawLevelFieldCrumbled(x, y);
5577 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5579 DrawLevelElement(x, y, Back[x][y]);
5580 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5582 else if (IS_WALKABLE_UNDER(Back[x][y]))
5584 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5585 DrawLevelElementThruMask(x, y, Back[x][y]);
5587 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5588 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5592 void DynaExplode(int ex, int ey)
5595 int dynabomb_element = Feld[ex][ey];
5596 int dynabomb_size = 1;
5597 boolean dynabomb_xl = FALSE;
5598 struct PlayerInfo *player;
5599 static int xy[4][2] =
5607 if (IS_ACTIVE_BOMB(dynabomb_element))
5609 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5610 dynabomb_size = player->dynabomb_size;
5611 dynabomb_xl = player->dynabomb_xl;
5612 player->dynabombs_left++;
5615 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5617 for (i = 0; i < NUM_DIRECTIONS; i++)
5619 for (j = 1; j <= dynabomb_size; j++)
5621 int x = ex + j * xy[i][0];
5622 int y = ey + j * xy[i][1];
5625 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5628 element = Feld[x][y];
5630 /* do not restart explosions of fields with active bombs */
5631 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5634 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5636 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5637 !IS_DIGGABLE(element) && !dynabomb_xl)
5643 void Bang(int x, int y)
5645 int element = MovingOrBlocked2Element(x, y);
5646 int explosion_type = EX_TYPE_NORMAL;
5648 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5650 struct PlayerInfo *player = PLAYERINFO(x, y);
5652 element = Feld[x][y] = player->initial_element;
5654 if (level.use_explosion_element[player->index_nr])
5656 int explosion_element = level.explosion_element[player->index_nr];
5658 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5659 explosion_type = EX_TYPE_CROSS;
5660 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5661 explosion_type = EX_TYPE_CENTER;
5669 case EL_BD_BUTTERFLY:
5672 case EL_DARK_YAMYAM:
5676 RaiseScoreElement(element);
5679 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5680 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5681 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5682 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5683 case EL_DYNABOMB_INCREASE_NUMBER:
5684 case EL_DYNABOMB_INCREASE_SIZE:
5685 case EL_DYNABOMB_INCREASE_POWER:
5686 explosion_type = EX_TYPE_DYNA;
5689 case EL_DC_LANDMINE:
5690 explosion_type = EX_TYPE_CENTER;
5695 case EL_LAMP_ACTIVE:
5696 case EL_AMOEBA_TO_DIAMOND:
5697 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5698 explosion_type = EX_TYPE_CENTER;
5702 if (element_info[element].explosion_type == EXPLODES_CROSS)
5703 explosion_type = EX_TYPE_CROSS;
5704 else if (element_info[element].explosion_type == EXPLODES_1X1)
5705 explosion_type = EX_TYPE_CENTER;
5709 if (explosion_type == EX_TYPE_DYNA)
5712 Explode(x, y, EX_PHASE_START, explosion_type);
5714 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5717 void SplashAcid(int x, int y)
5719 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5720 (!IN_LEV_FIELD(x - 1, y - 2) ||
5721 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5722 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5724 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5725 (!IN_LEV_FIELD(x + 1, y - 2) ||
5726 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5727 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5729 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5732 static void InitBeltMovement()
5734 static int belt_base_element[4] =
5736 EL_CONVEYOR_BELT_1_LEFT,
5737 EL_CONVEYOR_BELT_2_LEFT,
5738 EL_CONVEYOR_BELT_3_LEFT,
5739 EL_CONVEYOR_BELT_4_LEFT
5741 static int belt_base_active_element[4] =
5743 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5744 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5745 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5746 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5751 /* set frame order for belt animation graphic according to belt direction */
5752 for (i = 0; i < NUM_BELTS; i++)
5756 for (j = 0; j < NUM_BELT_PARTS; j++)
5758 int element = belt_base_active_element[belt_nr] + j;
5759 int graphic_1 = el2img(element);
5760 int graphic_2 = el2panelimg(element);
5762 if (game.belt_dir[i] == MV_LEFT)
5764 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5765 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5769 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5770 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5775 SCAN_PLAYFIELD(x, y)
5777 int element = Feld[x][y];
5779 for (i = 0; i < NUM_BELTS; i++)
5781 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5783 int e_belt_nr = getBeltNrFromBeltElement(element);
5786 if (e_belt_nr == belt_nr)
5788 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5790 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5797 static void ToggleBeltSwitch(int x, int y)
5799 static int belt_base_element[4] =
5801 EL_CONVEYOR_BELT_1_LEFT,
5802 EL_CONVEYOR_BELT_2_LEFT,
5803 EL_CONVEYOR_BELT_3_LEFT,
5804 EL_CONVEYOR_BELT_4_LEFT
5806 static int belt_base_active_element[4] =
5808 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5809 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5810 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5811 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5813 static int belt_base_switch_element[4] =
5815 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5816 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5817 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5818 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5820 static int belt_move_dir[4] =
5828 int element = Feld[x][y];
5829 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5830 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5831 int belt_dir = belt_move_dir[belt_dir_nr];
5834 if (!IS_BELT_SWITCH(element))
5837 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5838 game.belt_dir[belt_nr] = belt_dir;
5840 if (belt_dir_nr == 3)
5843 /* set frame order for belt animation graphic according to belt direction */
5844 for (i = 0; i < NUM_BELT_PARTS; i++)
5846 int element = belt_base_active_element[belt_nr] + i;
5847 int graphic_1 = el2img(element);
5848 int graphic_2 = el2panelimg(element);
5850 if (belt_dir == MV_LEFT)
5852 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5853 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5857 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5858 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5862 SCAN_PLAYFIELD(xx, yy)
5864 int element = Feld[xx][yy];
5866 if (IS_BELT_SWITCH(element))
5868 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5870 if (e_belt_nr == belt_nr)
5872 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5873 TEST_DrawLevelField(xx, yy);
5876 else if (IS_BELT(element) && belt_dir != MV_NONE)
5878 int e_belt_nr = getBeltNrFromBeltElement(element);
5880 if (e_belt_nr == belt_nr)
5882 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5884 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5885 TEST_DrawLevelField(xx, yy);
5888 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5890 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5892 if (e_belt_nr == belt_nr)
5894 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5896 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5897 TEST_DrawLevelField(xx, yy);
5903 static void ToggleSwitchgateSwitch(int x, int y)
5907 game.switchgate_pos = !game.switchgate_pos;
5909 SCAN_PLAYFIELD(xx, yy)
5911 int element = Feld[xx][yy];
5913 if (element == EL_SWITCHGATE_SWITCH_UP)
5915 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5916 TEST_DrawLevelField(xx, yy);
5918 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5920 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5921 TEST_DrawLevelField(xx, yy);
5923 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5925 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5926 TEST_DrawLevelField(xx, yy);
5928 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5930 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5931 TEST_DrawLevelField(xx, yy);
5933 else if (element == EL_SWITCHGATE_OPEN ||
5934 element == EL_SWITCHGATE_OPENING)
5936 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5938 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5940 else if (element == EL_SWITCHGATE_CLOSED ||
5941 element == EL_SWITCHGATE_CLOSING)
5943 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5945 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5950 static int getInvisibleActiveFromInvisibleElement(int element)
5952 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5953 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5954 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5958 static int getInvisibleFromInvisibleActiveElement(int element)
5960 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5961 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5962 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5966 static void RedrawAllLightSwitchesAndInvisibleElements()
5970 SCAN_PLAYFIELD(x, y)
5972 int element = Feld[x][y];
5974 if (element == EL_LIGHT_SWITCH &&
5975 game.light_time_left > 0)
5977 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5978 TEST_DrawLevelField(x, y);
5980 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5981 game.light_time_left == 0)
5983 Feld[x][y] = EL_LIGHT_SWITCH;
5984 TEST_DrawLevelField(x, y);
5986 else if (element == EL_EMC_DRIPPER &&
5987 game.light_time_left > 0)
5989 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5990 TEST_DrawLevelField(x, y);
5992 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5993 game.light_time_left == 0)
5995 Feld[x][y] = EL_EMC_DRIPPER;
5996 TEST_DrawLevelField(x, y);
5998 else if (element == EL_INVISIBLE_STEELWALL ||
5999 element == EL_INVISIBLE_WALL ||
6000 element == EL_INVISIBLE_SAND)
6002 if (game.light_time_left > 0)
6003 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6005 TEST_DrawLevelField(x, y);
6007 /* uncrumble neighbour fields, if needed */
6008 if (element == EL_INVISIBLE_SAND)
6009 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6011 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6012 element == EL_INVISIBLE_WALL_ACTIVE ||
6013 element == EL_INVISIBLE_SAND_ACTIVE)
6015 if (game.light_time_left == 0)
6016 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6018 TEST_DrawLevelField(x, y);
6020 /* re-crumble neighbour fields, if needed */
6021 if (element == EL_INVISIBLE_SAND)
6022 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6027 static void RedrawAllInvisibleElementsForLenses()
6031 SCAN_PLAYFIELD(x, y)
6033 int element = Feld[x][y];
6035 if (element == EL_EMC_DRIPPER &&
6036 game.lenses_time_left > 0)
6038 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6039 TEST_DrawLevelField(x, y);
6041 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6042 game.lenses_time_left == 0)
6044 Feld[x][y] = EL_EMC_DRIPPER;
6045 TEST_DrawLevelField(x, y);
6047 else if (element == EL_INVISIBLE_STEELWALL ||
6048 element == EL_INVISIBLE_WALL ||
6049 element == EL_INVISIBLE_SAND)
6051 if (game.lenses_time_left > 0)
6052 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6054 TEST_DrawLevelField(x, y);
6056 /* uncrumble neighbour fields, if needed */
6057 if (element == EL_INVISIBLE_SAND)
6058 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6060 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6061 element == EL_INVISIBLE_WALL_ACTIVE ||
6062 element == EL_INVISIBLE_SAND_ACTIVE)
6064 if (game.lenses_time_left == 0)
6065 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6067 TEST_DrawLevelField(x, y);
6069 /* re-crumble neighbour fields, if needed */
6070 if (element == EL_INVISIBLE_SAND)
6071 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6076 static void RedrawAllInvisibleElementsForMagnifier()
6080 SCAN_PLAYFIELD(x, y)
6082 int element = Feld[x][y];
6084 if (element == EL_EMC_FAKE_GRASS &&
6085 game.magnify_time_left > 0)
6087 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6088 TEST_DrawLevelField(x, y);
6090 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6091 game.magnify_time_left == 0)
6093 Feld[x][y] = EL_EMC_FAKE_GRASS;
6094 TEST_DrawLevelField(x, y);
6096 else if (IS_GATE_GRAY(element) &&
6097 game.magnify_time_left > 0)
6099 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6100 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6101 IS_EM_GATE_GRAY(element) ?
6102 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6103 IS_EMC_GATE_GRAY(element) ?
6104 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6105 IS_DC_GATE_GRAY(element) ?
6106 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6108 TEST_DrawLevelField(x, y);
6110 else if (IS_GATE_GRAY_ACTIVE(element) &&
6111 game.magnify_time_left == 0)
6113 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6114 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6115 IS_EM_GATE_GRAY_ACTIVE(element) ?
6116 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6117 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6118 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6119 IS_DC_GATE_GRAY_ACTIVE(element) ?
6120 EL_DC_GATE_WHITE_GRAY :
6122 TEST_DrawLevelField(x, y);
6127 static void ToggleLightSwitch(int x, int y)
6129 int element = Feld[x][y];
6131 game.light_time_left =
6132 (element == EL_LIGHT_SWITCH ?
6133 level.time_light * FRAMES_PER_SECOND : 0);
6135 RedrawAllLightSwitchesAndInvisibleElements();
6138 static void ActivateTimegateSwitch(int x, int y)
6142 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6144 SCAN_PLAYFIELD(xx, yy)
6146 int element = Feld[xx][yy];
6148 if (element == EL_TIMEGATE_CLOSED ||
6149 element == EL_TIMEGATE_CLOSING)
6151 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6152 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6156 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6158 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6159 TEST_DrawLevelField(xx, yy);
6165 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6166 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6169 void Impact(int x, int y)
6171 boolean last_line = (y == lev_fieldy - 1);
6172 boolean object_hit = FALSE;
6173 boolean impact = (last_line || object_hit);
6174 int element = Feld[x][y];
6175 int smashed = EL_STEELWALL;
6177 if (!last_line) /* check if element below was hit */
6179 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6182 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6183 MovDir[x][y + 1] != MV_DOWN ||
6184 MovPos[x][y + 1] <= TILEY / 2));
6186 /* do not smash moving elements that left the smashed field in time */
6187 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6188 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6191 #if USE_QUICKSAND_IMPACT_BUGFIX
6192 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6194 RemoveMovingField(x, y + 1);
6195 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6196 Feld[x][y + 2] = EL_ROCK;
6197 TEST_DrawLevelField(x, y + 2);
6202 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6204 RemoveMovingField(x, y + 1);
6205 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6206 Feld[x][y + 2] = EL_ROCK;
6207 TEST_DrawLevelField(x, y + 2);
6214 smashed = MovingOrBlocked2Element(x, y + 1);
6216 impact = (last_line || object_hit);
6219 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6221 SplashAcid(x, y + 1);
6225 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6226 /* only reset graphic animation if graphic really changes after impact */
6228 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6230 ResetGfxAnimation(x, y);
6231 TEST_DrawLevelField(x, y);
6234 if (impact && CAN_EXPLODE_IMPACT(element))
6239 else if (impact && element == EL_PEARL &&
6240 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6242 ResetGfxAnimation(x, y);
6244 Feld[x][y] = EL_PEARL_BREAKING;
6245 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6248 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6250 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6255 if (impact && element == EL_AMOEBA_DROP)
6257 if (object_hit && IS_PLAYER(x, y + 1))
6258 KillPlayerUnlessEnemyProtected(x, y + 1);
6259 else if (object_hit && smashed == EL_PENGUIN)
6263 Feld[x][y] = EL_AMOEBA_GROWING;
6264 Store[x][y] = EL_AMOEBA_WET;
6266 ResetRandomAnimationValue(x, y);
6271 if (object_hit) /* check which object was hit */
6273 if ((CAN_PASS_MAGIC_WALL(element) &&
6274 (smashed == EL_MAGIC_WALL ||
6275 smashed == EL_BD_MAGIC_WALL)) ||
6276 (CAN_PASS_DC_MAGIC_WALL(element) &&
6277 smashed == EL_DC_MAGIC_WALL))
6280 int activated_magic_wall =
6281 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6282 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6283 EL_DC_MAGIC_WALL_ACTIVE);
6285 /* activate magic wall / mill */
6286 SCAN_PLAYFIELD(xx, yy)
6288 if (Feld[xx][yy] == smashed)
6289 Feld[xx][yy] = activated_magic_wall;
6292 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6293 game.magic_wall_active = TRUE;
6295 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6296 SND_MAGIC_WALL_ACTIVATING :
6297 smashed == EL_BD_MAGIC_WALL ?
6298 SND_BD_MAGIC_WALL_ACTIVATING :
6299 SND_DC_MAGIC_WALL_ACTIVATING));
6302 if (IS_PLAYER(x, y + 1))
6304 if (CAN_SMASH_PLAYER(element))
6306 KillPlayerUnlessEnemyProtected(x, y + 1);
6310 else if (smashed == EL_PENGUIN)
6312 if (CAN_SMASH_PLAYER(element))
6318 else if (element == EL_BD_DIAMOND)
6320 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6326 else if (((element == EL_SP_INFOTRON ||
6327 element == EL_SP_ZONK) &&
6328 (smashed == EL_SP_SNIKSNAK ||
6329 smashed == EL_SP_ELECTRON ||
6330 smashed == EL_SP_DISK_ORANGE)) ||
6331 (element == EL_SP_INFOTRON &&
6332 smashed == EL_SP_DISK_YELLOW))
6337 else if (CAN_SMASH_EVERYTHING(element))
6339 if (IS_CLASSIC_ENEMY(smashed) ||
6340 CAN_EXPLODE_SMASHED(smashed))
6345 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6347 if (smashed == EL_LAMP ||
6348 smashed == EL_LAMP_ACTIVE)
6353 else if (smashed == EL_NUT)
6355 Feld[x][y + 1] = EL_NUT_BREAKING;
6356 PlayLevelSound(x, y, SND_NUT_BREAKING);
6357 RaiseScoreElement(EL_NUT);
6360 else if (smashed == EL_PEARL)
6362 ResetGfxAnimation(x, y);
6364 Feld[x][y + 1] = EL_PEARL_BREAKING;
6365 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6368 else if (smashed == EL_DIAMOND)
6370 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6371 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6374 else if (IS_BELT_SWITCH(smashed))
6376 ToggleBeltSwitch(x, y + 1);
6378 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6379 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6380 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6381 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6383 ToggleSwitchgateSwitch(x, y + 1);
6385 else if (smashed == EL_LIGHT_SWITCH ||
6386 smashed == EL_LIGHT_SWITCH_ACTIVE)
6388 ToggleLightSwitch(x, y + 1);
6392 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6394 CheckElementChangeBySide(x, y + 1, smashed, element,
6395 CE_SWITCHED, CH_SIDE_TOP);
6396 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6402 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6407 /* play sound of magic wall / mill */
6409 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6410 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6411 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6413 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6414 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6415 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6416 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6417 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6418 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6423 /* play sound of object that hits the ground */
6424 if (last_line || object_hit)
6425 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6428 inline static void TurnRoundExt(int x, int y)
6440 { 0, 0 }, { 0, 0 }, { 0, 0 },
6445 int left, right, back;
6449 { MV_DOWN, MV_UP, MV_RIGHT },
6450 { MV_UP, MV_DOWN, MV_LEFT },
6452 { MV_LEFT, MV_RIGHT, MV_DOWN },
6456 { MV_RIGHT, MV_LEFT, MV_UP }
6459 int element = Feld[x][y];
6460 int move_pattern = element_info[element].move_pattern;
6462 int old_move_dir = MovDir[x][y];
6463 int left_dir = turn[old_move_dir].left;
6464 int right_dir = turn[old_move_dir].right;
6465 int back_dir = turn[old_move_dir].back;
6467 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6468 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6469 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6470 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6472 int left_x = x + left_dx, left_y = y + left_dy;
6473 int right_x = x + right_dx, right_y = y + right_dy;
6474 int move_x = x + move_dx, move_y = y + move_dy;
6478 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6480 TestIfBadThingTouchesOtherBadThing(x, y);
6482 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6483 MovDir[x][y] = right_dir;
6484 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6485 MovDir[x][y] = left_dir;
6487 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6489 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6492 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6494 TestIfBadThingTouchesOtherBadThing(x, y);
6496 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6497 MovDir[x][y] = left_dir;
6498 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6499 MovDir[x][y] = right_dir;
6501 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6503 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6506 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6508 TestIfBadThingTouchesOtherBadThing(x, y);
6510 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6511 MovDir[x][y] = left_dir;
6512 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6513 MovDir[x][y] = right_dir;
6515 if (MovDir[x][y] != old_move_dir)
6518 else if (element == EL_YAMYAM)
6520 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6521 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6523 if (can_turn_left && can_turn_right)
6524 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6525 else if (can_turn_left)
6526 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6527 else if (can_turn_right)
6528 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6530 MovDir[x][y] = back_dir;
6532 MovDelay[x][y] = 16 + 16 * RND(3);
6534 else if (element == EL_DARK_YAMYAM)
6536 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6538 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6541 if (can_turn_left && can_turn_right)
6542 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6543 else if (can_turn_left)
6544 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6545 else if (can_turn_right)
6546 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6548 MovDir[x][y] = back_dir;
6550 MovDelay[x][y] = 16 + 16 * RND(3);
6552 else if (element == EL_PACMAN)
6554 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6555 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6557 if (can_turn_left && can_turn_right)
6558 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6559 else if (can_turn_left)
6560 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6561 else if (can_turn_right)
6562 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6564 MovDir[x][y] = back_dir;
6566 MovDelay[x][y] = 6 + RND(40);
6568 else if (element == EL_PIG)
6570 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6571 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6572 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6573 boolean should_turn_left, should_turn_right, should_move_on;
6575 int rnd = RND(rnd_value);
6577 should_turn_left = (can_turn_left &&
6579 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6580 y + back_dy + left_dy)));
6581 should_turn_right = (can_turn_right &&
6583 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6584 y + back_dy + right_dy)));
6585 should_move_on = (can_move_on &&
6588 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6589 y + move_dy + left_dy) ||
6590 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6591 y + move_dy + right_dy)));
6593 if (should_turn_left || should_turn_right || should_move_on)
6595 if (should_turn_left && should_turn_right && should_move_on)
6596 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6597 rnd < 2 * rnd_value / 3 ? right_dir :
6599 else if (should_turn_left && should_turn_right)
6600 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6601 else if (should_turn_left && should_move_on)
6602 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6603 else if (should_turn_right && should_move_on)
6604 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6605 else if (should_turn_left)
6606 MovDir[x][y] = left_dir;
6607 else if (should_turn_right)
6608 MovDir[x][y] = right_dir;
6609 else if (should_move_on)
6610 MovDir[x][y] = old_move_dir;
6612 else if (can_move_on && rnd > rnd_value / 8)
6613 MovDir[x][y] = old_move_dir;
6614 else if (can_turn_left && can_turn_right)
6615 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6616 else if (can_turn_left && rnd > rnd_value / 8)
6617 MovDir[x][y] = left_dir;
6618 else if (can_turn_right && rnd > rnd_value/8)
6619 MovDir[x][y] = right_dir;
6621 MovDir[x][y] = back_dir;
6623 xx = x + move_xy[MovDir[x][y]].dx;
6624 yy = y + move_xy[MovDir[x][y]].dy;
6626 if (!IN_LEV_FIELD(xx, yy) ||
6627 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6628 MovDir[x][y] = old_move_dir;
6632 else if (element == EL_DRAGON)
6634 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6635 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6636 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6638 int rnd = RND(rnd_value);
6640 if (can_move_on && rnd > rnd_value / 8)
6641 MovDir[x][y] = old_move_dir;
6642 else if (can_turn_left && can_turn_right)
6643 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6644 else if (can_turn_left && rnd > rnd_value / 8)
6645 MovDir[x][y] = left_dir;
6646 else if (can_turn_right && rnd > rnd_value / 8)
6647 MovDir[x][y] = right_dir;
6649 MovDir[x][y] = back_dir;
6651 xx = x + move_xy[MovDir[x][y]].dx;
6652 yy = y + move_xy[MovDir[x][y]].dy;
6654 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6655 MovDir[x][y] = old_move_dir;
6659 else if (element == EL_MOLE)
6661 boolean can_move_on =
6662 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6663 IS_AMOEBOID(Feld[move_x][move_y]) ||
6664 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6667 boolean can_turn_left =
6668 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6669 IS_AMOEBOID(Feld[left_x][left_y])));
6671 boolean can_turn_right =
6672 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6673 IS_AMOEBOID(Feld[right_x][right_y])));
6675 if (can_turn_left && can_turn_right)
6676 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6677 else if (can_turn_left)
6678 MovDir[x][y] = left_dir;
6680 MovDir[x][y] = right_dir;
6683 if (MovDir[x][y] != old_move_dir)
6686 else if (element == EL_BALLOON)
6688 MovDir[x][y] = game.wind_direction;
6691 else if (element == EL_SPRING)
6693 if (MovDir[x][y] & MV_HORIZONTAL)
6695 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6696 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6698 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6699 ResetGfxAnimation(move_x, move_y);
6700 TEST_DrawLevelField(move_x, move_y);
6702 MovDir[x][y] = back_dir;
6704 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6705 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6706 MovDir[x][y] = MV_NONE;
6711 else if (element == EL_ROBOT ||
6712 element == EL_SATELLITE ||
6713 element == EL_PENGUIN ||
6714 element == EL_EMC_ANDROID)
6716 int attr_x = -1, attr_y = -1;
6727 for (i = 0; i < MAX_PLAYERS; i++)
6729 struct PlayerInfo *player = &stored_player[i];
6730 int jx = player->jx, jy = player->jy;
6732 if (!player->active)
6736 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6744 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6745 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6746 game.engine_version < VERSION_IDENT(3,1,0,0)))
6752 if (element == EL_PENGUIN)
6755 static int xy[4][2] =
6763 for (i = 0; i < NUM_DIRECTIONS; i++)
6765 int ex = x + xy[i][0];
6766 int ey = y + xy[i][1];
6768 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6769 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6770 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6771 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6780 MovDir[x][y] = MV_NONE;
6782 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6783 else if (attr_x > x)
6784 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6786 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6787 else if (attr_y > y)
6788 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6790 if (element == EL_ROBOT)
6794 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6795 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6796 Moving2Blocked(x, y, &newx, &newy);
6798 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6799 MovDelay[x][y] = 8 + 8 * !RND(3);
6801 MovDelay[x][y] = 16;
6803 else if (element == EL_PENGUIN)
6809 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6811 boolean first_horiz = RND(2);
6812 int new_move_dir = MovDir[x][y];
6815 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6816 Moving2Blocked(x, y, &newx, &newy);
6818 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6822 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6823 Moving2Blocked(x, y, &newx, &newy);
6825 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6828 MovDir[x][y] = old_move_dir;
6832 else if (element == EL_SATELLITE)
6838 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6840 boolean first_horiz = RND(2);
6841 int new_move_dir = MovDir[x][y];
6844 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6845 Moving2Blocked(x, y, &newx, &newy);
6847 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6851 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6852 Moving2Blocked(x, y, &newx, &newy);
6854 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6857 MovDir[x][y] = old_move_dir;
6861 else if (element == EL_EMC_ANDROID)
6863 static int check_pos[16] =
6865 -1, /* 0 => (invalid) */
6866 7, /* 1 => MV_LEFT */
6867 3, /* 2 => MV_RIGHT */
6868 -1, /* 3 => (invalid) */
6870 0, /* 5 => MV_LEFT | MV_UP */
6871 2, /* 6 => MV_RIGHT | MV_UP */
6872 -1, /* 7 => (invalid) */
6873 5, /* 8 => MV_DOWN */
6874 6, /* 9 => MV_LEFT | MV_DOWN */
6875 4, /* 10 => MV_RIGHT | MV_DOWN */
6876 -1, /* 11 => (invalid) */
6877 -1, /* 12 => (invalid) */
6878 -1, /* 13 => (invalid) */
6879 -1, /* 14 => (invalid) */
6880 -1, /* 15 => (invalid) */
6888 { -1, -1, MV_LEFT | MV_UP },
6890 { +1, -1, MV_RIGHT | MV_UP },
6891 { +1, 0, MV_RIGHT },
6892 { +1, +1, MV_RIGHT | MV_DOWN },
6894 { -1, +1, MV_LEFT | MV_DOWN },
6897 int start_pos, check_order;
6898 boolean can_clone = FALSE;
6901 /* check if there is any free field around current position */
6902 for (i = 0; i < 8; i++)
6904 int newx = x + check_xy[i].dx;
6905 int newy = y + check_xy[i].dy;
6907 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6915 if (can_clone) /* randomly find an element to clone */
6919 start_pos = check_pos[RND(8)];
6920 check_order = (RND(2) ? -1 : +1);
6922 for (i = 0; i < 8; i++)
6924 int pos_raw = start_pos + i * check_order;
6925 int pos = (pos_raw + 8) % 8;
6926 int newx = x + check_xy[pos].dx;
6927 int newy = y + check_xy[pos].dy;
6929 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6931 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6932 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6934 Store[x][y] = Feld[newx][newy];
6943 if (can_clone) /* randomly find a direction to move */
6947 start_pos = check_pos[RND(8)];
6948 check_order = (RND(2) ? -1 : +1);
6950 for (i = 0; i < 8; i++)
6952 int pos_raw = start_pos + i * check_order;
6953 int pos = (pos_raw + 8) % 8;
6954 int newx = x + check_xy[pos].dx;
6955 int newy = y + check_xy[pos].dy;
6956 int new_move_dir = check_xy[pos].dir;
6958 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6960 MovDir[x][y] = new_move_dir;
6961 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6970 if (can_clone) /* cloning and moving successful */
6973 /* cannot clone -- try to move towards player */
6975 start_pos = check_pos[MovDir[x][y] & 0x0f];
6976 check_order = (RND(2) ? -1 : +1);
6978 for (i = 0; i < 3; i++)
6980 /* first check start_pos, then previous/next or (next/previous) pos */
6981 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6982 int pos = (pos_raw + 8) % 8;
6983 int newx = x + check_xy[pos].dx;
6984 int newy = y + check_xy[pos].dy;
6985 int new_move_dir = check_xy[pos].dir;
6987 if (IS_PLAYER(newx, newy))
6990 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6992 MovDir[x][y] = new_move_dir;
6993 MovDelay[x][y] = level.android_move_time * 8 + 1;
7000 else if (move_pattern == MV_TURNING_LEFT ||
7001 move_pattern == MV_TURNING_RIGHT ||
7002 move_pattern == MV_TURNING_LEFT_RIGHT ||
7003 move_pattern == MV_TURNING_RIGHT_LEFT ||
7004 move_pattern == MV_TURNING_RANDOM ||
7005 move_pattern == MV_ALL_DIRECTIONS)
7007 boolean can_turn_left =
7008 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7009 boolean can_turn_right =
7010 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7012 if (element_info[element].move_stepsize == 0) /* "not moving" */
7015 if (move_pattern == MV_TURNING_LEFT)
7016 MovDir[x][y] = left_dir;
7017 else if (move_pattern == MV_TURNING_RIGHT)
7018 MovDir[x][y] = right_dir;
7019 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7020 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7021 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7022 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7023 else if (move_pattern == MV_TURNING_RANDOM)
7024 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7025 can_turn_right && !can_turn_left ? right_dir :
7026 RND(2) ? left_dir : right_dir);
7027 else if (can_turn_left && can_turn_right)
7028 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7029 else if (can_turn_left)
7030 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7031 else if (can_turn_right)
7032 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7034 MovDir[x][y] = back_dir;
7036 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7038 else if (move_pattern == MV_HORIZONTAL ||
7039 move_pattern == MV_VERTICAL)
7041 if (move_pattern & old_move_dir)
7042 MovDir[x][y] = back_dir;
7043 else if (move_pattern == MV_HORIZONTAL)
7044 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7045 else if (move_pattern == MV_VERTICAL)
7046 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7048 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7050 else if (move_pattern & MV_ANY_DIRECTION)
7052 MovDir[x][y] = move_pattern;
7053 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7055 else if (move_pattern & MV_WIND_DIRECTION)
7057 MovDir[x][y] = game.wind_direction;
7058 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7060 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7062 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7063 MovDir[x][y] = left_dir;
7064 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7065 MovDir[x][y] = right_dir;
7067 if (MovDir[x][y] != old_move_dir)
7068 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7070 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7072 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7073 MovDir[x][y] = right_dir;
7074 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7075 MovDir[x][y] = left_dir;
7077 if (MovDir[x][y] != old_move_dir)
7078 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7080 else if (move_pattern == MV_TOWARDS_PLAYER ||
7081 move_pattern == MV_AWAY_FROM_PLAYER)
7083 int attr_x = -1, attr_y = -1;
7085 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7096 for (i = 0; i < MAX_PLAYERS; i++)
7098 struct PlayerInfo *player = &stored_player[i];
7099 int jx = player->jx, jy = player->jy;
7101 if (!player->active)
7105 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7113 MovDir[x][y] = MV_NONE;
7115 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7116 else if (attr_x > x)
7117 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7119 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7120 else if (attr_y > y)
7121 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7123 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7125 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7127 boolean first_horiz = RND(2);
7128 int new_move_dir = MovDir[x][y];
7130 if (element_info[element].move_stepsize == 0) /* "not moving" */
7132 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7133 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7139 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7140 Moving2Blocked(x, y, &newx, &newy);
7142 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7146 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7147 Moving2Blocked(x, y, &newx, &newy);
7149 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7152 MovDir[x][y] = old_move_dir;
7155 else if (move_pattern == MV_WHEN_PUSHED ||
7156 move_pattern == MV_WHEN_DROPPED)
7158 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7159 MovDir[x][y] = MV_NONE;
7163 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7165 static int test_xy[7][2] =
7175 static int test_dir[7] =
7185 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7186 int move_preference = -1000000; /* start with very low preference */
7187 int new_move_dir = MV_NONE;
7188 int start_test = RND(4);
7191 for (i = 0; i < NUM_DIRECTIONS; i++)
7193 int move_dir = test_dir[start_test + i];
7194 int move_dir_preference;
7196 xx = x + test_xy[start_test + i][0];
7197 yy = y + test_xy[start_test + i][1];
7199 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7200 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7202 new_move_dir = move_dir;
7207 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7210 move_dir_preference = -1 * RunnerVisit[xx][yy];
7211 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7212 move_dir_preference = PlayerVisit[xx][yy];
7214 if (move_dir_preference > move_preference)
7216 /* prefer field that has not been visited for the longest time */
7217 move_preference = move_dir_preference;
7218 new_move_dir = move_dir;
7220 else if (move_dir_preference == move_preference &&
7221 move_dir == old_move_dir)
7223 /* prefer last direction when all directions are preferred equally */
7224 move_preference = move_dir_preference;
7225 new_move_dir = move_dir;
7229 MovDir[x][y] = new_move_dir;
7230 if (old_move_dir != new_move_dir)
7231 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7235 static void TurnRound(int x, int y)
7237 int direction = MovDir[x][y];
7241 GfxDir[x][y] = MovDir[x][y];
7243 if (direction != MovDir[x][y])
7247 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7249 ResetGfxFrame(x, y);
7252 static boolean JustBeingPushed(int x, int y)
7256 for (i = 0; i < MAX_PLAYERS; i++)
7258 struct PlayerInfo *player = &stored_player[i];
7260 if (player->active && player->is_pushing && player->MovPos)
7262 int next_jx = player->jx + (player->jx - player->last_jx);
7263 int next_jy = player->jy + (player->jy - player->last_jy);
7265 if (x == next_jx && y == next_jy)
7273 void StartMoving(int x, int y)
7275 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7276 int element = Feld[x][y];
7281 if (MovDelay[x][y] == 0)
7282 GfxAction[x][y] = ACTION_DEFAULT;
7284 if (CAN_FALL(element) && y < lev_fieldy - 1)
7286 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7287 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7288 if (JustBeingPushed(x, y))
7291 if (element == EL_QUICKSAND_FULL)
7293 if (IS_FREE(x, y + 1))
7295 InitMovingField(x, y, MV_DOWN);
7296 started_moving = TRUE;
7298 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7299 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7300 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7301 Store[x][y] = EL_ROCK;
7303 Store[x][y] = EL_ROCK;
7306 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7308 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7310 if (!MovDelay[x][y])
7312 MovDelay[x][y] = TILEY + 1;
7314 ResetGfxAnimation(x, y);
7315 ResetGfxAnimation(x, y + 1);
7320 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7321 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7328 Feld[x][y] = EL_QUICKSAND_EMPTY;
7329 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7330 Store[x][y + 1] = Store[x][y];
7333 PlayLevelSoundAction(x, y, ACTION_FILLING);
7335 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7337 if (!MovDelay[x][y])
7339 MovDelay[x][y] = TILEY + 1;
7341 ResetGfxAnimation(x, y);
7342 ResetGfxAnimation(x, y + 1);
7347 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7348 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7355 Feld[x][y] = EL_QUICKSAND_EMPTY;
7356 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7357 Store[x][y + 1] = Store[x][y];
7360 PlayLevelSoundAction(x, y, ACTION_FILLING);
7363 else if (element == EL_QUICKSAND_FAST_FULL)
7365 if (IS_FREE(x, y + 1))
7367 InitMovingField(x, y, MV_DOWN);
7368 started_moving = TRUE;
7370 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7371 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7372 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7373 Store[x][y] = EL_ROCK;
7375 Store[x][y] = EL_ROCK;
7378 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7380 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7382 if (!MovDelay[x][y])
7384 MovDelay[x][y] = TILEY + 1;
7386 ResetGfxAnimation(x, y);
7387 ResetGfxAnimation(x, y + 1);
7392 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7393 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7400 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7401 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7402 Store[x][y + 1] = Store[x][y];
7405 PlayLevelSoundAction(x, y, ACTION_FILLING);
7407 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7409 if (!MovDelay[x][y])
7411 MovDelay[x][y] = TILEY + 1;
7413 ResetGfxAnimation(x, y);
7414 ResetGfxAnimation(x, y + 1);
7419 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7420 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7427 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7428 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7429 Store[x][y + 1] = Store[x][y];
7432 PlayLevelSoundAction(x, y, ACTION_FILLING);
7435 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7436 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7438 InitMovingField(x, y, MV_DOWN);
7439 started_moving = TRUE;
7441 Feld[x][y] = EL_QUICKSAND_FILLING;
7442 Store[x][y] = element;
7444 PlayLevelSoundAction(x, y, ACTION_FILLING);
7446 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7447 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7449 InitMovingField(x, y, MV_DOWN);
7450 started_moving = TRUE;
7452 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7453 Store[x][y] = element;
7455 PlayLevelSoundAction(x, y, ACTION_FILLING);
7457 else if (element == EL_MAGIC_WALL_FULL)
7459 if (IS_FREE(x, y + 1))
7461 InitMovingField(x, y, MV_DOWN);
7462 started_moving = TRUE;
7464 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7465 Store[x][y] = EL_CHANGED(Store[x][y]);
7467 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7469 if (!MovDelay[x][y])
7470 MovDelay[x][y] = TILEY / 4 + 1;
7479 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7480 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7481 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7485 else if (element == EL_BD_MAGIC_WALL_FULL)
7487 if (IS_FREE(x, y + 1))
7489 InitMovingField(x, y, MV_DOWN);
7490 started_moving = TRUE;
7492 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7493 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7495 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7497 if (!MovDelay[x][y])
7498 MovDelay[x][y] = TILEY / 4 + 1;
7507 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7508 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7509 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7513 else if (element == EL_DC_MAGIC_WALL_FULL)
7515 if (IS_FREE(x, y + 1))
7517 InitMovingField(x, y, MV_DOWN);
7518 started_moving = TRUE;
7520 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7521 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7523 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7525 if (!MovDelay[x][y])
7526 MovDelay[x][y] = TILEY / 4 + 1;
7535 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7536 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7537 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7541 else if ((CAN_PASS_MAGIC_WALL(element) &&
7542 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7543 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7544 (CAN_PASS_DC_MAGIC_WALL(element) &&
7545 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7548 InitMovingField(x, y, MV_DOWN);
7549 started_moving = TRUE;
7552 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7553 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7554 EL_DC_MAGIC_WALL_FILLING);
7555 Store[x][y] = element;
7557 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7559 SplashAcid(x, y + 1);
7561 InitMovingField(x, y, MV_DOWN);
7562 started_moving = TRUE;
7564 Store[x][y] = EL_ACID;
7567 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7568 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7569 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7570 CAN_FALL(element) && WasJustFalling[x][y] &&
7571 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7573 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7574 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7575 (Feld[x][y + 1] == EL_BLOCKED)))
7577 /* this is needed for a special case not covered by calling "Impact()"
7578 from "ContinueMoving()": if an element moves to a tile directly below
7579 another element which was just falling on that tile (which was empty
7580 in the previous frame), the falling element above would just stop
7581 instead of smashing the element below (in previous version, the above
7582 element was just checked for "moving" instead of "falling", resulting
7583 in incorrect smashes caused by horizontal movement of the above
7584 element; also, the case of the player being the element to smash was
7585 simply not covered here... :-/ ) */
7587 CheckCollision[x][y] = 0;
7588 CheckImpact[x][y] = 0;
7592 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7594 if (MovDir[x][y] == MV_NONE)
7596 InitMovingField(x, y, MV_DOWN);
7597 started_moving = TRUE;
7600 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7602 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7603 MovDir[x][y] = MV_DOWN;
7605 InitMovingField(x, y, MV_DOWN);
7606 started_moving = TRUE;
7608 else if (element == EL_AMOEBA_DROP)
7610 Feld[x][y] = EL_AMOEBA_GROWING;
7611 Store[x][y] = EL_AMOEBA_WET;
7613 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7614 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7615 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7616 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7618 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7619 (IS_FREE(x - 1, y + 1) ||
7620 Feld[x - 1][y + 1] == EL_ACID));
7621 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7622 (IS_FREE(x + 1, y + 1) ||
7623 Feld[x + 1][y + 1] == EL_ACID));
7624 boolean can_fall_any = (can_fall_left || can_fall_right);
7625 boolean can_fall_both = (can_fall_left && can_fall_right);
7626 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7628 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7630 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7631 can_fall_right = FALSE;
7632 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7633 can_fall_left = FALSE;
7634 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7635 can_fall_right = FALSE;
7636 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7637 can_fall_left = FALSE;
7639 can_fall_any = (can_fall_left || can_fall_right);
7640 can_fall_both = FALSE;
7645 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7646 can_fall_right = FALSE; /* slip down on left side */
7648 can_fall_left = !(can_fall_right = RND(2));
7650 can_fall_both = FALSE;
7655 /* if not determined otherwise, prefer left side for slipping down */
7656 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7657 started_moving = TRUE;
7660 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7662 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7663 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7664 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7665 int belt_dir = game.belt_dir[belt_nr];
7667 if ((belt_dir == MV_LEFT && left_is_free) ||
7668 (belt_dir == MV_RIGHT && right_is_free))
7670 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7672 InitMovingField(x, y, belt_dir);
7673 started_moving = TRUE;
7675 Pushed[x][y] = TRUE;
7676 Pushed[nextx][y] = TRUE;
7678 GfxAction[x][y] = ACTION_DEFAULT;
7682 MovDir[x][y] = 0; /* if element was moving, stop it */
7687 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7688 if (CAN_MOVE(element) && !started_moving)
7690 int move_pattern = element_info[element].move_pattern;
7693 Moving2Blocked(x, y, &newx, &newy);
7695 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7698 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7699 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7701 WasJustMoving[x][y] = 0;
7702 CheckCollision[x][y] = 0;
7704 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7706 if (Feld[x][y] != element) /* element has changed */
7710 if (!MovDelay[x][y]) /* start new movement phase */
7712 /* all objects that can change their move direction after each step
7713 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7715 if (element != EL_YAMYAM &&
7716 element != EL_DARK_YAMYAM &&
7717 element != EL_PACMAN &&
7718 !(move_pattern & MV_ANY_DIRECTION) &&
7719 move_pattern != MV_TURNING_LEFT &&
7720 move_pattern != MV_TURNING_RIGHT &&
7721 move_pattern != MV_TURNING_LEFT_RIGHT &&
7722 move_pattern != MV_TURNING_RIGHT_LEFT &&
7723 move_pattern != MV_TURNING_RANDOM)
7727 if (MovDelay[x][y] && (element == EL_BUG ||
7728 element == EL_SPACESHIP ||
7729 element == EL_SP_SNIKSNAK ||
7730 element == EL_SP_ELECTRON ||
7731 element == EL_MOLE))
7732 TEST_DrawLevelField(x, y);
7736 if (MovDelay[x][y]) /* wait some time before next movement */
7740 if (element == EL_ROBOT ||
7741 element == EL_YAMYAM ||
7742 element == EL_DARK_YAMYAM)
7744 DrawLevelElementAnimationIfNeeded(x, y, element);
7745 PlayLevelSoundAction(x, y, ACTION_WAITING);
7747 else if (element == EL_SP_ELECTRON)
7748 DrawLevelElementAnimationIfNeeded(x, y, element);
7749 else if (element == EL_DRAGON)
7752 int dir = MovDir[x][y];
7753 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7754 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7755 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7756 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7757 dir == MV_UP ? IMG_FLAMES_1_UP :
7758 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7759 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7761 GfxAction[x][y] = ACTION_ATTACKING;
7763 if (IS_PLAYER(x, y))
7764 DrawPlayerField(x, y);
7766 TEST_DrawLevelField(x, y);
7768 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7770 for (i = 1; i <= 3; i++)
7772 int xx = x + i * dx;
7773 int yy = y + i * dy;
7774 int sx = SCREENX(xx);
7775 int sy = SCREENY(yy);
7776 int flame_graphic = graphic + (i - 1);
7778 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7783 int flamed = MovingOrBlocked2Element(xx, yy);
7785 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7788 RemoveMovingField(xx, yy);
7790 ChangeDelay[xx][yy] = 0;
7792 Feld[xx][yy] = EL_FLAMES;
7794 if (IN_SCR_FIELD(sx, sy))
7796 TEST_DrawLevelFieldCrumbled(xx, yy);
7797 DrawGraphic(sx, sy, flame_graphic, frame);
7802 if (Feld[xx][yy] == EL_FLAMES)
7803 Feld[xx][yy] = EL_EMPTY;
7804 TEST_DrawLevelField(xx, yy);
7809 if (MovDelay[x][y]) /* element still has to wait some time */
7811 PlayLevelSoundAction(x, y, ACTION_WAITING);
7817 /* now make next step */
7819 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7821 if (DONT_COLLIDE_WITH(element) &&
7822 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7823 !PLAYER_ENEMY_PROTECTED(newx, newy))
7825 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7830 else if (CAN_MOVE_INTO_ACID(element) &&
7831 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7832 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7833 (MovDir[x][y] == MV_DOWN ||
7834 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7836 SplashAcid(newx, newy);
7837 Store[x][y] = EL_ACID;
7839 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7841 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7842 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7843 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7844 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7847 TEST_DrawLevelField(x, y);
7849 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7850 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7851 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7853 local_player->friends_still_needed--;
7854 if (!local_player->friends_still_needed &&
7855 !local_player->GameOver && AllPlayersGone)
7856 PlayerWins(local_player);
7860 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7862 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7863 TEST_DrawLevelField(newx, newy);
7865 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7867 else if (!IS_FREE(newx, newy))
7869 GfxAction[x][y] = ACTION_WAITING;
7871 if (IS_PLAYER(x, y))
7872 DrawPlayerField(x, y);
7874 TEST_DrawLevelField(x, y);
7879 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7881 if (IS_FOOD_PIG(Feld[newx][newy]))
7883 if (IS_MOVING(newx, newy))
7884 RemoveMovingField(newx, newy);
7887 Feld[newx][newy] = EL_EMPTY;
7888 TEST_DrawLevelField(newx, newy);
7891 PlayLevelSound(x, y, SND_PIG_DIGGING);
7893 else if (!IS_FREE(newx, newy))
7895 if (IS_PLAYER(x, y))
7896 DrawPlayerField(x, y);
7898 TEST_DrawLevelField(x, y);
7903 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7905 if (Store[x][y] != EL_EMPTY)
7907 boolean can_clone = FALSE;
7910 /* check if element to clone is still there */
7911 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7913 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7921 /* cannot clone or target field not free anymore -- do not clone */
7922 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7923 Store[x][y] = EL_EMPTY;
7926 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7928 if (IS_MV_DIAGONAL(MovDir[x][y]))
7930 int diagonal_move_dir = MovDir[x][y];
7931 int stored = Store[x][y];
7932 int change_delay = 8;
7935 /* android is moving diagonally */
7937 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7939 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7940 GfxElement[x][y] = EL_EMC_ANDROID;
7941 GfxAction[x][y] = ACTION_SHRINKING;
7942 GfxDir[x][y] = diagonal_move_dir;
7943 ChangeDelay[x][y] = change_delay;
7945 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7948 DrawLevelGraphicAnimation(x, y, graphic);
7949 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7951 if (Feld[newx][newy] == EL_ACID)
7953 SplashAcid(newx, newy);
7958 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7960 Store[newx][newy] = EL_EMC_ANDROID;
7961 GfxElement[newx][newy] = EL_EMC_ANDROID;
7962 GfxAction[newx][newy] = ACTION_GROWING;
7963 GfxDir[newx][newy] = diagonal_move_dir;
7964 ChangeDelay[newx][newy] = change_delay;
7966 graphic = el_act_dir2img(GfxElement[newx][newy],
7967 GfxAction[newx][newy], GfxDir[newx][newy]);
7969 DrawLevelGraphicAnimation(newx, newy, graphic);
7970 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7976 Feld[newx][newy] = EL_EMPTY;
7977 TEST_DrawLevelField(newx, newy);
7979 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7982 else if (!IS_FREE(newx, newy))
7987 else if (IS_CUSTOM_ELEMENT(element) &&
7988 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7990 if (!DigFieldByCE(newx, newy, element))
7993 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7995 RunnerVisit[x][y] = FrameCounter;
7996 PlayerVisit[x][y] /= 8; /* expire player visit path */
7999 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8001 if (!IS_FREE(newx, newy))
8003 if (IS_PLAYER(x, y))
8004 DrawPlayerField(x, y);
8006 TEST_DrawLevelField(x, y);
8012 boolean wanna_flame = !RND(10);
8013 int dx = newx - x, dy = newy - y;
8014 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8015 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8016 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8017 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8018 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8019 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8022 IS_CLASSIC_ENEMY(element1) ||
8023 IS_CLASSIC_ENEMY(element2)) &&
8024 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8025 element1 != EL_FLAMES && element2 != EL_FLAMES)
8027 ResetGfxAnimation(x, y);
8028 GfxAction[x][y] = ACTION_ATTACKING;
8030 if (IS_PLAYER(x, y))
8031 DrawPlayerField(x, y);
8033 TEST_DrawLevelField(x, y);
8035 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8037 MovDelay[x][y] = 50;
8039 Feld[newx][newy] = EL_FLAMES;
8040 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8041 Feld[newx1][newy1] = EL_FLAMES;
8042 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8043 Feld[newx2][newy2] = EL_FLAMES;
8049 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8050 Feld[newx][newy] == EL_DIAMOND)
8052 if (IS_MOVING(newx, newy))
8053 RemoveMovingField(newx, newy);
8056 Feld[newx][newy] = EL_EMPTY;
8057 TEST_DrawLevelField(newx, newy);
8060 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8062 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8063 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8065 if (AmoebaNr[newx][newy])
8067 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8068 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8069 Feld[newx][newy] == EL_BD_AMOEBA)
8070 AmoebaCnt[AmoebaNr[newx][newy]]--;
8073 if (IS_MOVING(newx, newy))
8075 RemoveMovingField(newx, newy);
8079 Feld[newx][newy] = EL_EMPTY;
8080 TEST_DrawLevelField(newx, newy);
8083 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8085 else if ((element == EL_PACMAN || element == EL_MOLE)
8086 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8088 if (AmoebaNr[newx][newy])
8090 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8091 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8092 Feld[newx][newy] == EL_BD_AMOEBA)
8093 AmoebaCnt[AmoebaNr[newx][newy]]--;
8096 if (element == EL_MOLE)
8098 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8099 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8101 ResetGfxAnimation(x, y);
8102 GfxAction[x][y] = ACTION_DIGGING;
8103 TEST_DrawLevelField(x, y);
8105 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8107 return; /* wait for shrinking amoeba */
8109 else /* element == EL_PACMAN */
8111 Feld[newx][newy] = EL_EMPTY;
8112 TEST_DrawLevelField(newx, newy);
8113 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8116 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8117 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8118 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8120 /* wait for shrinking amoeba to completely disappear */
8123 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8125 /* object was running against a wall */
8129 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8130 DrawLevelElementAnimation(x, y, element);
8132 if (DONT_TOUCH(element))
8133 TestIfBadThingTouchesPlayer(x, y);
8138 InitMovingField(x, y, MovDir[x][y]);
8140 PlayLevelSoundAction(x, y, ACTION_MOVING);
8144 ContinueMoving(x, y);
8147 void ContinueMoving(int x, int y)
8149 int element = Feld[x][y];
8150 struct ElementInfo *ei = &element_info[element];
8151 int direction = MovDir[x][y];
8152 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8153 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8154 int newx = x + dx, newy = y + dy;
8155 int stored = Store[x][y];
8156 int stored_new = Store[newx][newy];
8157 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8158 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8159 boolean last_line = (newy == lev_fieldy - 1);
8161 MovPos[x][y] += getElementMoveStepsize(x, y);
8163 if (pushed_by_player) /* special case: moving object pushed by player */
8164 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8166 if (ABS(MovPos[x][y]) < TILEX)
8168 TEST_DrawLevelField(x, y);
8170 return; /* element is still moving */
8173 /* element reached destination field */
8175 Feld[x][y] = EL_EMPTY;
8176 Feld[newx][newy] = element;
8177 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8179 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8181 element = Feld[newx][newy] = EL_ACID;
8183 else if (element == EL_MOLE)
8185 Feld[x][y] = EL_SAND;
8187 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8189 else if (element == EL_QUICKSAND_FILLING)
8191 element = Feld[newx][newy] = get_next_element(element);
8192 Store[newx][newy] = Store[x][y];
8194 else if (element == EL_QUICKSAND_EMPTYING)
8196 Feld[x][y] = get_next_element(element);
8197 element = Feld[newx][newy] = Store[x][y];
8199 else if (element == EL_QUICKSAND_FAST_FILLING)
8201 element = Feld[newx][newy] = get_next_element(element);
8202 Store[newx][newy] = Store[x][y];
8204 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8206 Feld[x][y] = get_next_element(element);
8207 element = Feld[newx][newy] = Store[x][y];
8209 else if (element == EL_MAGIC_WALL_FILLING)
8211 element = Feld[newx][newy] = get_next_element(element);
8212 if (!game.magic_wall_active)
8213 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8214 Store[newx][newy] = Store[x][y];
8216 else if (element == EL_MAGIC_WALL_EMPTYING)
8218 Feld[x][y] = get_next_element(element);
8219 if (!game.magic_wall_active)
8220 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8221 element = Feld[newx][newy] = Store[x][y];
8223 InitField(newx, newy, FALSE);
8225 else if (element == EL_BD_MAGIC_WALL_FILLING)
8227 element = Feld[newx][newy] = get_next_element(element);
8228 if (!game.magic_wall_active)
8229 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8230 Store[newx][newy] = Store[x][y];
8232 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8234 Feld[x][y] = get_next_element(element);
8235 if (!game.magic_wall_active)
8236 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8237 element = Feld[newx][newy] = Store[x][y];
8239 InitField(newx, newy, FALSE);
8241 else if (element == EL_DC_MAGIC_WALL_FILLING)
8243 element = Feld[newx][newy] = get_next_element(element);
8244 if (!game.magic_wall_active)
8245 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8246 Store[newx][newy] = Store[x][y];
8248 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8250 Feld[x][y] = get_next_element(element);
8251 if (!game.magic_wall_active)
8252 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8253 element = Feld[newx][newy] = Store[x][y];
8255 InitField(newx, newy, FALSE);
8257 else if (element == EL_AMOEBA_DROPPING)
8259 Feld[x][y] = get_next_element(element);
8260 element = Feld[newx][newy] = Store[x][y];
8262 else if (element == EL_SOKOBAN_OBJECT)
8265 Feld[x][y] = Back[x][y];
8267 if (Back[newx][newy])
8268 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8270 Back[x][y] = Back[newx][newy] = 0;
8273 Store[x][y] = EL_EMPTY;
8278 MovDelay[newx][newy] = 0;
8280 if (CAN_CHANGE_OR_HAS_ACTION(element))
8282 /* copy element change control values to new field */
8283 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8284 ChangePage[newx][newy] = ChangePage[x][y];
8285 ChangeCount[newx][newy] = ChangeCount[x][y];
8286 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8289 CustomValue[newx][newy] = CustomValue[x][y];
8291 ChangeDelay[x][y] = 0;
8292 ChangePage[x][y] = -1;
8293 ChangeCount[x][y] = 0;
8294 ChangeEvent[x][y] = -1;
8296 CustomValue[x][y] = 0;
8298 /* copy animation control values to new field */
8299 GfxFrame[newx][newy] = GfxFrame[x][y];
8300 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8301 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8302 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8304 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8306 /* some elements can leave other elements behind after moving */
8307 if (ei->move_leave_element != EL_EMPTY &&
8308 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8309 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8311 int move_leave_element = ei->move_leave_element;
8313 /* this makes it possible to leave the removed element again */
8314 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8315 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8317 Feld[x][y] = move_leave_element;
8319 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8320 MovDir[x][y] = direction;
8322 InitField(x, y, FALSE);
8324 if (GFX_CRUMBLED(Feld[x][y]))
8325 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8327 if (ELEM_IS_PLAYER(move_leave_element))
8328 RelocatePlayer(x, y, move_leave_element);
8331 /* do this after checking for left-behind element */
8332 ResetGfxAnimation(x, y); /* reset animation values for old field */
8334 if (!CAN_MOVE(element) ||
8335 (CAN_FALL(element) && direction == MV_DOWN &&
8336 (element == EL_SPRING ||
8337 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8338 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8339 GfxDir[x][y] = MovDir[newx][newy] = 0;
8341 TEST_DrawLevelField(x, y);
8342 TEST_DrawLevelField(newx, newy);
8344 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8346 /* prevent pushed element from moving on in pushed direction */
8347 if (pushed_by_player && CAN_MOVE(element) &&
8348 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8349 !(element_info[element].move_pattern & direction))
8350 TurnRound(newx, newy);
8352 /* prevent elements on conveyor belt from moving on in last direction */
8353 if (pushed_by_conveyor && CAN_FALL(element) &&
8354 direction & MV_HORIZONTAL)
8355 MovDir[newx][newy] = 0;
8357 if (!pushed_by_player)
8359 int nextx = newx + dx, nexty = newy + dy;
8360 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8362 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8364 if (CAN_FALL(element) && direction == MV_DOWN)
8365 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8367 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8368 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8370 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8371 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8374 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8376 TestIfBadThingTouchesPlayer(newx, newy);
8377 TestIfBadThingTouchesFriend(newx, newy);
8379 if (!IS_CUSTOM_ELEMENT(element))
8380 TestIfBadThingTouchesOtherBadThing(newx, newy);
8382 else if (element == EL_PENGUIN)
8383 TestIfFriendTouchesBadThing(newx, newy);
8385 if (DONT_GET_HIT_BY(element))
8387 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8390 /* give the player one last chance (one more frame) to move away */
8391 if (CAN_FALL(element) && direction == MV_DOWN &&
8392 (last_line || (!IS_FREE(x, newy + 1) &&
8393 (!IS_PLAYER(x, newy + 1) ||
8394 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8397 if (pushed_by_player && !game.use_change_when_pushing_bug)
8399 int push_side = MV_DIR_OPPOSITE(direction);
8400 struct PlayerInfo *player = PLAYERINFO(x, y);
8402 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8403 player->index_bit, push_side);
8404 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8405 player->index_bit, push_side);
8408 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8409 MovDelay[newx][newy] = 1;
8411 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8413 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8414 TestIfElementHitsCustomElement(newx, newy, direction);
8415 TestIfPlayerTouchesCustomElement(newx, newy);
8416 TestIfElementTouchesCustomElement(newx, newy);
8418 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8419 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8420 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8421 MV_DIR_OPPOSITE(direction));
8424 int AmoebeNachbarNr(int ax, int ay)
8427 int element = Feld[ax][ay];
8429 static int xy[4][2] =
8437 for (i = 0; i < NUM_DIRECTIONS; i++)
8439 int x = ax + xy[i][0];
8440 int y = ay + xy[i][1];
8442 if (!IN_LEV_FIELD(x, y))
8445 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8446 group_nr = AmoebaNr[x][y];
8452 void AmoebenVereinigen(int ax, int ay)
8454 int i, x, y, xx, yy;
8455 int new_group_nr = AmoebaNr[ax][ay];
8456 static int xy[4][2] =
8464 if (new_group_nr == 0)
8467 for (i = 0; i < NUM_DIRECTIONS; i++)
8472 if (!IN_LEV_FIELD(x, y))
8475 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8476 Feld[x][y] == EL_BD_AMOEBA ||
8477 Feld[x][y] == EL_AMOEBA_DEAD) &&
8478 AmoebaNr[x][y] != new_group_nr)
8480 int old_group_nr = AmoebaNr[x][y];
8482 if (old_group_nr == 0)
8485 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8486 AmoebaCnt[old_group_nr] = 0;
8487 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8488 AmoebaCnt2[old_group_nr] = 0;
8490 SCAN_PLAYFIELD(xx, yy)
8492 if (AmoebaNr[xx][yy] == old_group_nr)
8493 AmoebaNr[xx][yy] = new_group_nr;
8499 void AmoebeUmwandeln(int ax, int ay)
8503 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8505 int group_nr = AmoebaNr[ax][ay];
8510 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8511 printf("AmoebeUmwandeln(): This should never happen!\n");
8516 SCAN_PLAYFIELD(x, y)
8518 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8521 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8525 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8526 SND_AMOEBA_TURNING_TO_GEM :
8527 SND_AMOEBA_TURNING_TO_ROCK));
8532 static int xy[4][2] =
8540 for (i = 0; i < NUM_DIRECTIONS; i++)
8545 if (!IN_LEV_FIELD(x, y))
8548 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8550 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8551 SND_AMOEBA_TURNING_TO_GEM :
8552 SND_AMOEBA_TURNING_TO_ROCK));
8559 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8562 int group_nr = AmoebaNr[ax][ay];
8563 boolean done = FALSE;
8568 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8569 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8574 SCAN_PLAYFIELD(x, y)
8576 if (AmoebaNr[x][y] == group_nr &&
8577 (Feld[x][y] == EL_AMOEBA_DEAD ||
8578 Feld[x][y] == EL_BD_AMOEBA ||
8579 Feld[x][y] == EL_AMOEBA_GROWING))
8582 Feld[x][y] = new_element;
8583 InitField(x, y, FALSE);
8584 TEST_DrawLevelField(x, y);
8590 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8591 SND_BD_AMOEBA_TURNING_TO_ROCK :
8592 SND_BD_AMOEBA_TURNING_TO_GEM));
8595 void AmoebeWaechst(int x, int y)
8597 static unsigned int sound_delay = 0;
8598 static unsigned int sound_delay_value = 0;
8600 if (!MovDelay[x][y]) /* start new growing cycle */
8604 if (DelayReached(&sound_delay, sound_delay_value))
8606 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8607 sound_delay_value = 30;
8611 if (MovDelay[x][y]) /* wait some time before growing bigger */
8614 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8616 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8617 6 - MovDelay[x][y]);
8619 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8622 if (!MovDelay[x][y])
8624 Feld[x][y] = Store[x][y];
8626 TEST_DrawLevelField(x, y);
8631 void AmoebaDisappearing(int x, int y)
8633 static unsigned int sound_delay = 0;
8634 static unsigned int sound_delay_value = 0;
8636 if (!MovDelay[x][y]) /* start new shrinking cycle */
8640 if (DelayReached(&sound_delay, sound_delay_value))
8641 sound_delay_value = 30;
8644 if (MovDelay[x][y]) /* wait some time before shrinking */
8647 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8649 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8650 6 - MovDelay[x][y]);
8652 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8655 if (!MovDelay[x][y])
8657 Feld[x][y] = EL_EMPTY;
8658 TEST_DrawLevelField(x, y);
8660 /* don't let mole enter this field in this cycle;
8661 (give priority to objects falling to this field from above) */
8667 void AmoebeAbleger(int ax, int ay)
8670 int element = Feld[ax][ay];
8671 int graphic = el2img(element);
8672 int newax = ax, neway = ay;
8673 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8674 static int xy[4][2] =
8682 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8684 Feld[ax][ay] = EL_AMOEBA_DEAD;
8685 TEST_DrawLevelField(ax, ay);
8689 if (IS_ANIMATED(graphic))
8690 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8692 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8693 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8695 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8698 if (MovDelay[ax][ay])
8702 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8705 int x = ax + xy[start][0];
8706 int y = ay + xy[start][1];
8708 if (!IN_LEV_FIELD(x, y))
8711 if (IS_FREE(x, y) ||
8712 CAN_GROW_INTO(Feld[x][y]) ||
8713 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8714 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8720 if (newax == ax && neway == ay)
8723 else /* normal or "filled" (BD style) amoeba */
8726 boolean waiting_for_player = FALSE;
8728 for (i = 0; i < NUM_DIRECTIONS; i++)
8730 int j = (start + i) % 4;
8731 int x = ax + xy[j][0];
8732 int y = ay + xy[j][1];
8734 if (!IN_LEV_FIELD(x, y))
8737 if (IS_FREE(x, y) ||
8738 CAN_GROW_INTO(Feld[x][y]) ||
8739 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8740 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8746 else if (IS_PLAYER(x, y))
8747 waiting_for_player = TRUE;
8750 if (newax == ax && neway == ay) /* amoeba cannot grow */
8752 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8754 Feld[ax][ay] = EL_AMOEBA_DEAD;
8755 TEST_DrawLevelField(ax, ay);
8756 AmoebaCnt[AmoebaNr[ax][ay]]--;
8758 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8760 if (element == EL_AMOEBA_FULL)
8761 AmoebeUmwandeln(ax, ay);
8762 else if (element == EL_BD_AMOEBA)
8763 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8768 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8770 /* amoeba gets larger by growing in some direction */
8772 int new_group_nr = AmoebaNr[ax][ay];
8775 if (new_group_nr == 0)
8777 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8778 printf("AmoebeAbleger(): This should never happen!\n");
8783 AmoebaNr[newax][neway] = new_group_nr;
8784 AmoebaCnt[new_group_nr]++;
8785 AmoebaCnt2[new_group_nr]++;
8787 /* if amoeba touches other amoeba(s) after growing, unify them */
8788 AmoebenVereinigen(newax, neway);
8790 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8792 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8798 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8799 (neway == lev_fieldy - 1 && newax != ax))
8801 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8802 Store[newax][neway] = element;
8804 else if (neway == ay || element == EL_EMC_DRIPPER)
8806 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8808 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8812 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8813 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8814 Store[ax][ay] = EL_AMOEBA_DROP;
8815 ContinueMoving(ax, ay);
8819 TEST_DrawLevelField(newax, neway);
8822 void Life(int ax, int ay)
8826 int element = Feld[ax][ay];
8827 int graphic = el2img(element);
8828 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8830 boolean changed = FALSE;
8832 if (IS_ANIMATED(graphic))
8833 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8838 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8839 MovDelay[ax][ay] = life_time;
8841 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8844 if (MovDelay[ax][ay])
8848 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8850 int xx = ax+x1, yy = ay+y1;
8853 if (!IN_LEV_FIELD(xx, yy))
8856 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8858 int x = xx+x2, y = yy+y2;
8860 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8863 if (((Feld[x][y] == element ||
8864 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8866 (IS_FREE(x, y) && Stop[x][y]))
8870 if (xx == ax && yy == ay) /* field in the middle */
8872 if (nachbarn < life_parameter[0] ||
8873 nachbarn > life_parameter[1])
8875 Feld[xx][yy] = EL_EMPTY;
8877 TEST_DrawLevelField(xx, yy);
8878 Stop[xx][yy] = TRUE;
8882 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8883 { /* free border field */
8884 if (nachbarn >= life_parameter[2] &&
8885 nachbarn <= life_parameter[3])
8887 Feld[xx][yy] = element;
8888 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8890 TEST_DrawLevelField(xx, yy);
8891 Stop[xx][yy] = TRUE;
8898 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8899 SND_GAME_OF_LIFE_GROWING);
8902 static void InitRobotWheel(int x, int y)
8904 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8907 static void RunRobotWheel(int x, int y)
8909 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8912 static void StopRobotWheel(int x, int y)
8914 if (ZX == x && ZY == y)
8918 game.robot_wheel_active = FALSE;
8922 static void InitTimegateWheel(int x, int y)
8924 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8927 static void RunTimegateWheel(int x, int y)
8929 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8932 static void InitMagicBallDelay(int x, int y)
8934 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8937 static void ActivateMagicBall(int bx, int by)
8941 if (level.ball_random)
8943 int pos_border = RND(8); /* select one of the eight border elements */
8944 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8945 int xx = pos_content % 3;
8946 int yy = pos_content / 3;
8951 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8952 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8956 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8958 int xx = x - bx + 1;
8959 int yy = y - by + 1;
8961 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8962 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8966 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8969 void CheckExit(int x, int y)
8971 if (local_player->gems_still_needed > 0 ||
8972 local_player->sokobanfields_still_needed > 0 ||
8973 local_player->lights_still_needed > 0)
8975 int element = Feld[x][y];
8976 int graphic = el2img(element);
8978 if (IS_ANIMATED(graphic))
8979 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8984 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8987 Feld[x][y] = EL_EXIT_OPENING;
8989 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8992 void CheckExitEM(int x, int y)
8994 if (local_player->gems_still_needed > 0 ||
8995 local_player->sokobanfields_still_needed > 0 ||
8996 local_player->lights_still_needed > 0)
8998 int element = Feld[x][y];
8999 int graphic = el2img(element);
9001 if (IS_ANIMATED(graphic))
9002 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9007 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9010 Feld[x][y] = EL_EM_EXIT_OPENING;
9012 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9015 void CheckExitSteel(int x, int y)
9017 if (local_player->gems_still_needed > 0 ||
9018 local_player->sokobanfields_still_needed > 0 ||
9019 local_player->lights_still_needed > 0)
9021 int element = Feld[x][y];
9022 int graphic = el2img(element);
9024 if (IS_ANIMATED(graphic))
9025 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9030 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9033 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9035 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9038 void CheckExitSteelEM(int x, int y)
9040 if (local_player->gems_still_needed > 0 ||
9041 local_player->sokobanfields_still_needed > 0 ||
9042 local_player->lights_still_needed > 0)
9044 int element = Feld[x][y];
9045 int graphic = el2img(element);
9047 if (IS_ANIMATED(graphic))
9048 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9053 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9056 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9058 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9061 void CheckExitSP(int x, int y)
9063 if (local_player->gems_still_needed > 0)
9065 int element = Feld[x][y];
9066 int graphic = el2img(element);
9068 if (IS_ANIMATED(graphic))
9069 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9074 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9077 Feld[x][y] = EL_SP_EXIT_OPENING;
9079 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9082 static void CloseAllOpenTimegates()
9086 SCAN_PLAYFIELD(x, y)
9088 int element = Feld[x][y];
9090 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9092 Feld[x][y] = EL_TIMEGATE_CLOSING;
9094 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9099 void DrawTwinkleOnField(int x, int y)
9101 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9104 if (Feld[x][y] == EL_BD_DIAMOND)
9107 if (MovDelay[x][y] == 0) /* next animation frame */
9108 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9110 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9114 DrawLevelElementAnimation(x, y, Feld[x][y]);
9116 if (MovDelay[x][y] != 0)
9118 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9119 10 - MovDelay[x][y]);
9121 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9126 void MauerWaechst(int x, int y)
9130 if (!MovDelay[x][y]) /* next animation frame */
9131 MovDelay[x][y] = 3 * delay;
9133 if (MovDelay[x][y]) /* wait some time before next frame */
9137 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9139 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9140 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9142 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9145 if (!MovDelay[x][y])
9147 if (MovDir[x][y] == MV_LEFT)
9149 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9150 TEST_DrawLevelField(x - 1, y);
9152 else if (MovDir[x][y] == MV_RIGHT)
9154 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9155 TEST_DrawLevelField(x + 1, y);
9157 else if (MovDir[x][y] == MV_UP)
9159 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9160 TEST_DrawLevelField(x, y - 1);
9164 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9165 TEST_DrawLevelField(x, y + 1);
9168 Feld[x][y] = Store[x][y];
9170 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9171 TEST_DrawLevelField(x, y);
9176 void MauerAbleger(int ax, int ay)
9178 int element = Feld[ax][ay];
9179 int graphic = el2img(element);
9180 boolean oben_frei = FALSE, unten_frei = FALSE;
9181 boolean links_frei = FALSE, rechts_frei = FALSE;
9182 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9183 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9184 boolean new_wall = FALSE;
9186 if (IS_ANIMATED(graphic))
9187 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9189 if (!MovDelay[ax][ay]) /* start building new wall */
9190 MovDelay[ax][ay] = 6;
9192 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9195 if (MovDelay[ax][ay])
9199 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9201 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9203 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9205 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9208 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9209 element == EL_EXPANDABLE_WALL_ANY)
9213 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9214 Store[ax][ay-1] = element;
9215 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9216 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9217 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9218 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9223 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9224 Store[ax][ay+1] = element;
9225 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9226 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9227 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9228 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9233 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9234 element == EL_EXPANDABLE_WALL_ANY ||
9235 element == EL_EXPANDABLE_WALL ||
9236 element == EL_BD_EXPANDABLE_WALL)
9240 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9241 Store[ax-1][ay] = element;
9242 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9243 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9244 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9245 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9251 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9252 Store[ax+1][ay] = element;
9253 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9254 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9255 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9256 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9261 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9262 TEST_DrawLevelField(ax, ay);
9264 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9266 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9267 unten_massiv = TRUE;
9268 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9269 links_massiv = TRUE;
9270 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9271 rechts_massiv = TRUE;
9273 if (((oben_massiv && unten_massiv) ||
9274 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9275 element == EL_EXPANDABLE_WALL) &&
9276 ((links_massiv && rechts_massiv) ||
9277 element == EL_EXPANDABLE_WALL_VERTICAL))
9278 Feld[ax][ay] = EL_WALL;
9281 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9284 void MauerAblegerStahl(int ax, int ay)
9286 int element = Feld[ax][ay];
9287 int graphic = el2img(element);
9288 boolean oben_frei = FALSE, unten_frei = FALSE;
9289 boolean links_frei = FALSE, rechts_frei = FALSE;
9290 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9291 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9292 boolean new_wall = FALSE;
9294 if (IS_ANIMATED(graphic))
9295 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9297 if (!MovDelay[ax][ay]) /* start building new wall */
9298 MovDelay[ax][ay] = 6;
9300 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9303 if (MovDelay[ax][ay])
9307 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9309 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9311 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9313 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9316 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9317 element == EL_EXPANDABLE_STEELWALL_ANY)
9321 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9322 Store[ax][ay-1] = element;
9323 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9324 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9325 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9326 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9331 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9332 Store[ax][ay+1] = element;
9333 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9334 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9335 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9336 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9341 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9342 element == EL_EXPANDABLE_STEELWALL_ANY)
9346 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9347 Store[ax-1][ay] = element;
9348 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9349 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9350 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9351 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9357 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9358 Store[ax+1][ay] = element;
9359 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9360 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9361 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9362 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9367 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9369 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9370 unten_massiv = TRUE;
9371 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9372 links_massiv = TRUE;
9373 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9374 rechts_massiv = TRUE;
9376 if (((oben_massiv && unten_massiv) ||
9377 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9378 ((links_massiv && rechts_massiv) ||
9379 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9380 Feld[ax][ay] = EL_STEELWALL;
9383 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9386 void CheckForDragon(int x, int y)
9389 boolean dragon_found = FALSE;
9390 static int xy[4][2] =
9398 for (i = 0; i < NUM_DIRECTIONS; i++)
9400 for (j = 0; j < 4; j++)
9402 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9404 if (IN_LEV_FIELD(xx, yy) &&
9405 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9407 if (Feld[xx][yy] == EL_DRAGON)
9408 dragon_found = TRUE;
9417 for (i = 0; i < NUM_DIRECTIONS; i++)
9419 for (j = 0; j < 3; j++)
9421 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9423 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9425 Feld[xx][yy] = EL_EMPTY;
9426 TEST_DrawLevelField(xx, yy);
9435 static void InitBuggyBase(int x, int y)
9437 int element = Feld[x][y];
9438 int activating_delay = FRAMES_PER_SECOND / 4;
9441 (element == EL_SP_BUGGY_BASE ?
9442 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9443 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9445 element == EL_SP_BUGGY_BASE_ACTIVE ?
9446 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9449 static void WarnBuggyBase(int x, int y)
9452 static int xy[4][2] =
9460 for (i = 0; i < NUM_DIRECTIONS; i++)
9462 int xx = x + xy[i][0];
9463 int yy = y + xy[i][1];
9465 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9467 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9474 static void InitTrap(int x, int y)
9476 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9479 static void ActivateTrap(int x, int y)
9481 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9484 static void ChangeActiveTrap(int x, int y)
9486 int graphic = IMG_TRAP_ACTIVE;
9488 /* if new animation frame was drawn, correct crumbled sand border */
9489 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9490 TEST_DrawLevelFieldCrumbled(x, y);
9493 static int getSpecialActionElement(int element, int number, int base_element)
9495 return (element != EL_EMPTY ? element :
9496 number != -1 ? base_element + number - 1 :
9500 static int getModifiedActionNumber(int value_old, int operator, int operand,
9501 int value_min, int value_max)
9503 int value_new = (operator == CA_MODE_SET ? operand :
9504 operator == CA_MODE_ADD ? value_old + operand :
9505 operator == CA_MODE_SUBTRACT ? value_old - operand :
9506 operator == CA_MODE_MULTIPLY ? value_old * operand :
9507 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9508 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9511 return (value_new < value_min ? value_min :
9512 value_new > value_max ? value_max :
9516 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9518 struct ElementInfo *ei = &element_info[element];
9519 struct ElementChangeInfo *change = &ei->change_page[page];
9520 int target_element = change->target_element;
9521 int action_type = change->action_type;
9522 int action_mode = change->action_mode;
9523 int action_arg = change->action_arg;
9524 int action_element = change->action_element;
9527 if (!change->has_action)
9530 /* ---------- determine action paramater values -------------------------- */
9532 int level_time_value =
9533 (level.time > 0 ? TimeLeft :
9536 int action_arg_element_raw =
9537 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9538 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9539 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9540 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9541 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9542 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9543 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9545 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9547 int action_arg_direction =
9548 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9549 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9550 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9551 change->actual_trigger_side :
9552 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9553 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9556 int action_arg_number_min =
9557 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9560 int action_arg_number_max =
9561 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9562 action_type == CA_SET_LEVEL_GEMS ? 999 :
9563 action_type == CA_SET_LEVEL_TIME ? 9999 :
9564 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9565 action_type == CA_SET_CE_VALUE ? 9999 :
9566 action_type == CA_SET_CE_SCORE ? 9999 :
9569 int action_arg_number_reset =
9570 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9571 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9572 action_type == CA_SET_LEVEL_TIME ? level.time :
9573 action_type == CA_SET_LEVEL_SCORE ? 0 :
9574 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9575 action_type == CA_SET_CE_SCORE ? 0 :
9578 int action_arg_number =
9579 (action_arg <= CA_ARG_MAX ? action_arg :
9580 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9581 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9582 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9583 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9584 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9585 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9586 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9587 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9588 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9589 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9590 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9591 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9592 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9593 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9594 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9595 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9596 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9597 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9598 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9599 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9600 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9603 int action_arg_number_old =
9604 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9605 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9606 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9607 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9608 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9611 int action_arg_number_new =
9612 getModifiedActionNumber(action_arg_number_old,
9613 action_mode, action_arg_number,
9614 action_arg_number_min, action_arg_number_max);
9616 int trigger_player_bits =
9617 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9618 change->actual_trigger_player_bits : change->trigger_player);
9620 int action_arg_player_bits =
9621 (action_arg >= CA_ARG_PLAYER_1 &&
9622 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9623 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9624 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9627 /* ---------- execute action -------------------------------------------- */
9629 switch (action_type)
9636 /* ---------- level actions ------------------------------------------- */
9638 case CA_RESTART_LEVEL:
9640 game.restart_level = TRUE;
9645 case CA_SHOW_ENVELOPE:
9647 int element = getSpecialActionElement(action_arg_element,
9648 action_arg_number, EL_ENVELOPE_1);
9650 if (IS_ENVELOPE(element))
9651 local_player->show_envelope = element;
9656 case CA_SET_LEVEL_TIME:
9658 if (level.time > 0) /* only modify limited time value */
9660 TimeLeft = action_arg_number_new;
9662 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9664 DisplayGameControlValues();
9666 if (!TimeLeft && setup.time_limit)
9667 for (i = 0; i < MAX_PLAYERS; i++)
9668 KillPlayer(&stored_player[i]);
9674 case CA_SET_LEVEL_SCORE:
9676 local_player->score = action_arg_number_new;
9678 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9680 DisplayGameControlValues();
9685 case CA_SET_LEVEL_GEMS:
9687 local_player->gems_still_needed = action_arg_number_new;
9689 game.snapshot.collected_item = TRUE;
9691 game_panel_controls[GAME_PANEL_GEMS].value =
9692 local_player->gems_still_needed;
9694 DisplayGameControlValues();
9699 case CA_SET_LEVEL_WIND:
9701 game.wind_direction = action_arg_direction;
9706 case CA_SET_LEVEL_RANDOM_SEED:
9708 /* ensure that setting a new random seed while playing is predictable */
9709 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9714 /* ---------- player actions ------------------------------------------ */
9716 case CA_MOVE_PLAYER:
9718 /* automatically move to the next field in specified direction */
9719 for (i = 0; i < MAX_PLAYERS; i++)
9720 if (trigger_player_bits & (1 << i))
9721 stored_player[i].programmed_action = action_arg_direction;
9726 case CA_EXIT_PLAYER:
9728 for (i = 0; i < MAX_PLAYERS; i++)
9729 if (action_arg_player_bits & (1 << i))
9730 PlayerWins(&stored_player[i]);
9735 case CA_KILL_PLAYER:
9737 for (i = 0; i < MAX_PLAYERS; i++)
9738 if (action_arg_player_bits & (1 << i))
9739 KillPlayer(&stored_player[i]);
9744 case CA_SET_PLAYER_KEYS:
9746 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9747 int element = getSpecialActionElement(action_arg_element,
9748 action_arg_number, EL_KEY_1);
9750 if (IS_KEY(element))
9752 for (i = 0; i < MAX_PLAYERS; i++)
9754 if (trigger_player_bits & (1 << i))
9756 stored_player[i].key[KEY_NR(element)] = key_state;
9758 DrawGameDoorValues();
9766 case CA_SET_PLAYER_SPEED:
9768 for (i = 0; i < MAX_PLAYERS; i++)
9770 if (trigger_player_bits & (1 << i))
9772 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9774 if (action_arg == CA_ARG_SPEED_FASTER &&
9775 stored_player[i].cannot_move)
9777 action_arg_number = STEPSIZE_VERY_SLOW;
9779 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9780 action_arg == CA_ARG_SPEED_FASTER)
9782 action_arg_number = 2;
9783 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9786 else if (action_arg == CA_ARG_NUMBER_RESET)
9788 action_arg_number = level.initial_player_stepsize[i];
9792 getModifiedActionNumber(move_stepsize,
9795 action_arg_number_min,
9796 action_arg_number_max);
9798 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9805 case CA_SET_PLAYER_SHIELD:
9807 for (i = 0; i < MAX_PLAYERS; i++)
9809 if (trigger_player_bits & (1 << i))
9811 if (action_arg == CA_ARG_SHIELD_OFF)
9813 stored_player[i].shield_normal_time_left = 0;
9814 stored_player[i].shield_deadly_time_left = 0;
9816 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9818 stored_player[i].shield_normal_time_left = 999999;
9820 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9822 stored_player[i].shield_normal_time_left = 999999;
9823 stored_player[i].shield_deadly_time_left = 999999;
9831 case CA_SET_PLAYER_GRAVITY:
9833 for (i = 0; i < MAX_PLAYERS; i++)
9835 if (trigger_player_bits & (1 << i))
9837 stored_player[i].gravity =
9838 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9839 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9840 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9841 stored_player[i].gravity);
9848 case CA_SET_PLAYER_ARTWORK:
9850 for (i = 0; i < MAX_PLAYERS; i++)
9852 if (trigger_player_bits & (1 << i))
9854 int artwork_element = action_arg_element;
9856 if (action_arg == CA_ARG_ELEMENT_RESET)
9858 (level.use_artwork_element[i] ? level.artwork_element[i] :
9859 stored_player[i].element_nr);
9861 if (stored_player[i].artwork_element != artwork_element)
9862 stored_player[i].Frame = 0;
9864 stored_player[i].artwork_element = artwork_element;
9866 SetPlayerWaiting(&stored_player[i], FALSE);
9868 /* set number of special actions for bored and sleeping animation */
9869 stored_player[i].num_special_action_bored =
9870 get_num_special_action(artwork_element,
9871 ACTION_BORING_1, ACTION_BORING_LAST);
9872 stored_player[i].num_special_action_sleeping =
9873 get_num_special_action(artwork_element,
9874 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9881 case CA_SET_PLAYER_INVENTORY:
9883 for (i = 0; i < MAX_PLAYERS; i++)
9885 struct PlayerInfo *player = &stored_player[i];
9888 if (trigger_player_bits & (1 << i))
9890 int inventory_element = action_arg_element;
9892 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9893 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9894 action_arg == CA_ARG_ELEMENT_ACTION)
9896 int element = inventory_element;
9897 int collect_count = element_info[element].collect_count_initial;
9899 if (!IS_CUSTOM_ELEMENT(element))
9902 if (collect_count == 0)
9903 player->inventory_infinite_element = element;
9905 for (k = 0; k < collect_count; k++)
9906 if (player->inventory_size < MAX_INVENTORY_SIZE)
9907 player->inventory_element[player->inventory_size++] =
9910 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9911 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9912 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9914 if (player->inventory_infinite_element != EL_UNDEFINED &&
9915 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9916 action_arg_element_raw))
9917 player->inventory_infinite_element = EL_UNDEFINED;
9919 for (k = 0, j = 0; j < player->inventory_size; j++)
9921 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9922 action_arg_element_raw))
9923 player->inventory_element[k++] = player->inventory_element[j];
9926 player->inventory_size = k;
9928 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9930 if (player->inventory_size > 0)
9932 for (j = 0; j < player->inventory_size - 1; j++)
9933 player->inventory_element[j] = player->inventory_element[j + 1];
9935 player->inventory_size--;
9938 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9940 if (player->inventory_size > 0)
9941 player->inventory_size--;
9943 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9945 player->inventory_infinite_element = EL_UNDEFINED;
9946 player->inventory_size = 0;
9948 else if (action_arg == CA_ARG_INVENTORY_RESET)
9950 player->inventory_infinite_element = EL_UNDEFINED;
9951 player->inventory_size = 0;
9953 if (level.use_initial_inventory[i])
9955 for (j = 0; j < level.initial_inventory_size[i]; j++)
9957 int element = level.initial_inventory_content[i][j];
9958 int collect_count = element_info[element].collect_count_initial;
9960 if (!IS_CUSTOM_ELEMENT(element))
9963 if (collect_count == 0)
9964 player->inventory_infinite_element = element;
9966 for (k = 0; k < collect_count; k++)
9967 if (player->inventory_size < MAX_INVENTORY_SIZE)
9968 player->inventory_element[player->inventory_size++] =
9979 /* ---------- CE actions ---------------------------------------------- */
9981 case CA_SET_CE_VALUE:
9983 int last_ce_value = CustomValue[x][y];
9985 CustomValue[x][y] = action_arg_number_new;
9987 if (CustomValue[x][y] != last_ce_value)
9989 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9990 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9992 if (CustomValue[x][y] == 0)
9994 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9995 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10002 case CA_SET_CE_SCORE:
10004 int last_ce_score = ei->collect_score;
10006 ei->collect_score = action_arg_number_new;
10008 if (ei->collect_score != last_ce_score)
10010 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10011 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10013 if (ei->collect_score == 0)
10017 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10018 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10021 This is a very special case that seems to be a mixture between
10022 CheckElementChange() and CheckTriggeredElementChange(): while
10023 the first one only affects single elements that are triggered
10024 directly, the second one affects multiple elements in the playfield
10025 that are triggered indirectly by another element. This is a third
10026 case: Changing the CE score always affects multiple identical CEs,
10027 so every affected CE must be checked, not only the single CE for
10028 which the CE score was changed in the first place (as every instance
10029 of that CE shares the same CE score, and therefore also can change)!
10031 SCAN_PLAYFIELD(xx, yy)
10033 if (Feld[xx][yy] == element)
10034 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10035 CE_SCORE_GETS_ZERO);
10043 case CA_SET_CE_ARTWORK:
10045 int artwork_element = action_arg_element;
10046 boolean reset_frame = FALSE;
10049 if (action_arg == CA_ARG_ELEMENT_RESET)
10050 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10053 if (ei->gfx_element != artwork_element)
10054 reset_frame = TRUE;
10056 ei->gfx_element = artwork_element;
10058 SCAN_PLAYFIELD(xx, yy)
10060 if (Feld[xx][yy] == element)
10064 ResetGfxAnimation(xx, yy);
10065 ResetRandomAnimationValue(xx, yy);
10068 TEST_DrawLevelField(xx, yy);
10075 /* ---------- engine actions ------------------------------------------ */
10077 case CA_SET_ENGINE_SCAN_MODE:
10079 InitPlayfieldScanMode(action_arg);
10089 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10091 int old_element = Feld[x][y];
10092 int new_element = GetElementFromGroupElement(element);
10093 int previous_move_direction = MovDir[x][y];
10094 int last_ce_value = CustomValue[x][y];
10095 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10096 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10097 boolean add_player_onto_element = (new_element_is_player &&
10098 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10099 IS_WALKABLE(old_element));
10101 if (!add_player_onto_element)
10103 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10104 RemoveMovingField(x, y);
10108 Feld[x][y] = new_element;
10110 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10111 MovDir[x][y] = previous_move_direction;
10113 if (element_info[new_element].use_last_ce_value)
10114 CustomValue[x][y] = last_ce_value;
10116 InitField_WithBug1(x, y, FALSE);
10118 new_element = Feld[x][y]; /* element may have changed */
10120 ResetGfxAnimation(x, y);
10121 ResetRandomAnimationValue(x, y);
10123 TEST_DrawLevelField(x, y);
10125 if (GFX_CRUMBLED(new_element))
10126 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10129 /* check if element under the player changes from accessible to unaccessible
10130 (needed for special case of dropping element which then changes) */
10131 /* (must be checked after creating new element for walkable group elements) */
10132 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10133 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10140 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10141 if (new_element_is_player)
10142 RelocatePlayer(x, y, new_element);
10145 ChangeCount[x][y]++; /* count number of changes in the same frame */
10147 TestIfBadThingTouchesPlayer(x, y);
10148 TestIfPlayerTouchesCustomElement(x, y);
10149 TestIfElementTouchesCustomElement(x, y);
10152 static void CreateField(int x, int y, int element)
10154 CreateFieldExt(x, y, element, FALSE);
10157 static void CreateElementFromChange(int x, int y, int element)
10159 element = GET_VALID_RUNTIME_ELEMENT(element);
10161 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10163 int old_element = Feld[x][y];
10165 /* prevent changed element from moving in same engine frame
10166 unless both old and new element can either fall or move */
10167 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10168 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10172 CreateFieldExt(x, y, element, TRUE);
10175 static boolean ChangeElement(int x, int y, int element, int page)
10177 struct ElementInfo *ei = &element_info[element];
10178 struct ElementChangeInfo *change = &ei->change_page[page];
10179 int ce_value = CustomValue[x][y];
10180 int ce_score = ei->collect_score;
10181 int target_element;
10182 int old_element = Feld[x][y];
10184 /* always use default change event to prevent running into a loop */
10185 if (ChangeEvent[x][y] == -1)
10186 ChangeEvent[x][y] = CE_DELAY;
10188 if (ChangeEvent[x][y] == CE_DELAY)
10190 /* reset actual trigger element, trigger player and action element */
10191 change->actual_trigger_element = EL_EMPTY;
10192 change->actual_trigger_player = EL_EMPTY;
10193 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10194 change->actual_trigger_side = CH_SIDE_NONE;
10195 change->actual_trigger_ce_value = 0;
10196 change->actual_trigger_ce_score = 0;
10199 /* do not change elements more than a specified maximum number of changes */
10200 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10203 ChangeCount[x][y]++; /* count number of changes in the same frame */
10205 if (change->explode)
10212 if (change->use_target_content)
10214 boolean complete_replace = TRUE;
10215 boolean can_replace[3][3];
10218 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10221 boolean is_walkable;
10222 boolean is_diggable;
10223 boolean is_collectible;
10224 boolean is_removable;
10225 boolean is_destructible;
10226 int ex = x + xx - 1;
10227 int ey = y + yy - 1;
10228 int content_element = change->target_content.e[xx][yy];
10231 can_replace[xx][yy] = TRUE;
10233 if (ex == x && ey == y) /* do not check changing element itself */
10236 if (content_element == EL_EMPTY_SPACE)
10238 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10243 if (!IN_LEV_FIELD(ex, ey))
10245 can_replace[xx][yy] = FALSE;
10246 complete_replace = FALSE;
10253 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10254 e = MovingOrBlocked2Element(ex, ey);
10256 is_empty = (IS_FREE(ex, ey) ||
10257 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10259 is_walkable = (is_empty || IS_WALKABLE(e));
10260 is_diggable = (is_empty || IS_DIGGABLE(e));
10261 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10262 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10263 is_removable = (is_diggable || is_collectible);
10265 can_replace[xx][yy] =
10266 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10267 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10268 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10269 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10270 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10271 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10272 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10274 if (!can_replace[xx][yy])
10275 complete_replace = FALSE;
10278 if (!change->only_if_complete || complete_replace)
10280 boolean something_has_changed = FALSE;
10282 if (change->only_if_complete && change->use_random_replace &&
10283 RND(100) < change->random_percentage)
10286 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10288 int ex = x + xx - 1;
10289 int ey = y + yy - 1;
10290 int content_element;
10292 if (can_replace[xx][yy] && (!change->use_random_replace ||
10293 RND(100) < change->random_percentage))
10295 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10296 RemoveMovingField(ex, ey);
10298 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10300 content_element = change->target_content.e[xx][yy];
10301 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10302 ce_value, ce_score);
10304 CreateElementFromChange(ex, ey, target_element);
10306 something_has_changed = TRUE;
10308 /* for symmetry reasons, freeze newly created border elements */
10309 if (ex != x || ey != y)
10310 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10314 if (something_has_changed)
10316 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10317 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10323 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10324 ce_value, ce_score);
10326 if (element == EL_DIAGONAL_GROWING ||
10327 element == EL_DIAGONAL_SHRINKING)
10329 target_element = Store[x][y];
10331 Store[x][y] = EL_EMPTY;
10334 CreateElementFromChange(x, y, target_element);
10336 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10337 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10340 /* this uses direct change before indirect change */
10341 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10346 static void HandleElementChange(int x, int y, int page)
10348 int element = MovingOrBlocked2Element(x, y);
10349 struct ElementInfo *ei = &element_info[element];
10350 struct ElementChangeInfo *change = &ei->change_page[page];
10351 boolean handle_action_before_change = FALSE;
10354 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10355 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10358 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10359 x, y, element, element_info[element].token_name);
10360 printf("HandleElementChange(): This should never happen!\n");
10365 /* this can happen with classic bombs on walkable, changing elements */
10366 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10371 if (ChangeDelay[x][y] == 0) /* initialize element change */
10373 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10375 if (change->can_change)
10377 /* !!! not clear why graphic animation should be reset at all here !!! */
10378 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10379 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10382 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10384 When using an animation frame delay of 1 (this only happens with
10385 "sp_zonk.moving.left/right" in the classic graphics), the default
10386 (non-moving) animation shows wrong animation frames (while the
10387 moving animation, like "sp_zonk.moving.left/right", is correct,
10388 so this graphical bug never shows up with the classic graphics).
10389 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10390 be drawn instead of the correct frames 0,1,2,3. This is caused by
10391 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10392 an element change: First when the change delay ("ChangeDelay[][]")
10393 counter has reached zero after decrementing, then a second time in
10394 the next frame (after "GfxFrame[][]" was already incremented) when
10395 "ChangeDelay[][]" is reset to the initial delay value again.
10397 This causes frame 0 to be drawn twice, while the last frame won't
10398 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10400 As some animations may already be cleverly designed around this bug
10401 (at least the "Snake Bite" snake tail animation does this), it cannot
10402 simply be fixed here without breaking such existing animations.
10403 Unfortunately, it cannot easily be detected if a graphics set was
10404 designed "before" or "after" the bug was fixed. As a workaround,
10405 a new graphics set option "game.graphics_engine_version" was added
10406 to be able to specify the game's major release version for which the
10407 graphics set was designed, which can then be used to decide if the
10408 bugfix should be used (version 4 and above) or not (version 3 or
10409 below, or if no version was specified at all, as with old sets).
10411 (The wrong/fixed animation frames can be tested with the test level set
10412 "test_gfxframe" and level "000", which contains a specially prepared
10413 custom element at level position (x/y) == (11/9) which uses the zonk
10414 animation mentioned above. Using "game.graphics_engine_version: 4"
10415 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10416 This can also be seen from the debug output for this test element.)
10419 /* when a custom element is about to change (for example by change delay),
10420 do not reset graphic animation when the custom element is moving */
10421 if (game.graphics_engine_version < 4 &&
10424 ResetGfxAnimation(x, y);
10425 ResetRandomAnimationValue(x, y);
10428 if (change->pre_change_function)
10429 change->pre_change_function(x, y);
10433 ChangeDelay[x][y]--;
10435 if (ChangeDelay[x][y] != 0) /* continue element change */
10437 if (change->can_change)
10439 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10441 if (IS_ANIMATED(graphic))
10442 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10444 if (change->change_function)
10445 change->change_function(x, y);
10448 else /* finish element change */
10450 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10452 page = ChangePage[x][y];
10453 ChangePage[x][y] = -1;
10455 change = &ei->change_page[page];
10458 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10460 ChangeDelay[x][y] = 1; /* try change after next move step */
10461 ChangePage[x][y] = page; /* remember page to use for change */
10466 /* special case: set new level random seed before changing element */
10467 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10468 handle_action_before_change = TRUE;
10470 if (change->has_action && handle_action_before_change)
10471 ExecuteCustomElementAction(x, y, element, page);
10473 if (change->can_change)
10475 if (ChangeElement(x, y, element, page))
10477 if (change->post_change_function)
10478 change->post_change_function(x, y);
10482 if (change->has_action && !handle_action_before_change)
10483 ExecuteCustomElementAction(x, y, element, page);
10487 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10488 int trigger_element,
10490 int trigger_player,
10494 boolean change_done_any = FALSE;
10495 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10498 if (!(trigger_events[trigger_element][trigger_event]))
10501 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10503 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10505 int element = EL_CUSTOM_START + i;
10506 boolean change_done = FALSE;
10509 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10510 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10513 for (p = 0; p < element_info[element].num_change_pages; p++)
10515 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10517 if (change->can_change_or_has_action &&
10518 change->has_event[trigger_event] &&
10519 change->trigger_side & trigger_side &&
10520 change->trigger_player & trigger_player &&
10521 change->trigger_page & trigger_page_bits &&
10522 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10524 change->actual_trigger_element = trigger_element;
10525 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10526 change->actual_trigger_player_bits = trigger_player;
10527 change->actual_trigger_side = trigger_side;
10528 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10529 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10531 if ((change->can_change && !change_done) || change->has_action)
10535 SCAN_PLAYFIELD(x, y)
10537 if (Feld[x][y] == element)
10539 if (change->can_change && !change_done)
10541 /* if element already changed in this frame, not only prevent
10542 another element change (checked in ChangeElement()), but
10543 also prevent additional element actions for this element */
10545 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10546 !level.use_action_after_change_bug)
10549 ChangeDelay[x][y] = 1;
10550 ChangeEvent[x][y] = trigger_event;
10552 HandleElementChange(x, y, p);
10554 else if (change->has_action)
10556 /* if element already changed in this frame, not only prevent
10557 another element change (checked in ChangeElement()), but
10558 also prevent additional element actions for this element */
10560 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10561 !level.use_action_after_change_bug)
10564 ExecuteCustomElementAction(x, y, element, p);
10565 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10570 if (change->can_change)
10572 change_done = TRUE;
10573 change_done_any = TRUE;
10580 RECURSION_LOOP_DETECTION_END();
10582 return change_done_any;
10585 static boolean CheckElementChangeExt(int x, int y,
10587 int trigger_element,
10589 int trigger_player,
10592 boolean change_done = FALSE;
10595 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10596 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10599 if (Feld[x][y] == EL_BLOCKED)
10601 Blocked2Moving(x, y, &x, &y);
10602 element = Feld[x][y];
10605 /* check if element has already changed or is about to change after moving */
10606 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10607 Feld[x][y] != element) ||
10609 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10610 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10611 ChangePage[x][y] != -1)))
10614 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10616 for (p = 0; p < element_info[element].num_change_pages; p++)
10618 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10620 /* check trigger element for all events where the element that is checked
10621 for changing interacts with a directly adjacent element -- this is
10622 different to element changes that affect other elements to change on the
10623 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10624 boolean check_trigger_element =
10625 (trigger_event == CE_TOUCHING_X ||
10626 trigger_event == CE_HITTING_X ||
10627 trigger_event == CE_HIT_BY_X ||
10628 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10630 if (change->can_change_or_has_action &&
10631 change->has_event[trigger_event] &&
10632 change->trigger_side & trigger_side &&
10633 change->trigger_player & trigger_player &&
10634 (!check_trigger_element ||
10635 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10637 change->actual_trigger_element = trigger_element;
10638 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10639 change->actual_trigger_player_bits = trigger_player;
10640 change->actual_trigger_side = trigger_side;
10641 change->actual_trigger_ce_value = CustomValue[x][y];
10642 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10644 /* special case: trigger element not at (x,y) position for some events */
10645 if (check_trigger_element)
10657 { 0, 0 }, { 0, 0 }, { 0, 0 },
10661 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10662 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10664 change->actual_trigger_ce_value = CustomValue[xx][yy];
10665 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10668 if (change->can_change && !change_done)
10670 ChangeDelay[x][y] = 1;
10671 ChangeEvent[x][y] = trigger_event;
10673 HandleElementChange(x, y, p);
10675 change_done = TRUE;
10677 else if (change->has_action)
10679 ExecuteCustomElementAction(x, y, element, p);
10680 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10685 RECURSION_LOOP_DETECTION_END();
10687 return change_done;
10690 static void PlayPlayerSound(struct PlayerInfo *player)
10692 int jx = player->jx, jy = player->jy;
10693 int sound_element = player->artwork_element;
10694 int last_action = player->last_action_waiting;
10695 int action = player->action_waiting;
10697 if (player->is_waiting)
10699 if (action != last_action)
10700 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10702 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10706 if (action != last_action)
10707 StopSound(element_info[sound_element].sound[last_action]);
10709 if (last_action == ACTION_SLEEPING)
10710 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10714 static void PlayAllPlayersSound()
10718 for (i = 0; i < MAX_PLAYERS; i++)
10719 if (stored_player[i].active)
10720 PlayPlayerSound(&stored_player[i]);
10723 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10725 boolean last_waiting = player->is_waiting;
10726 int move_dir = player->MovDir;
10728 player->dir_waiting = move_dir;
10729 player->last_action_waiting = player->action_waiting;
10733 if (!last_waiting) /* not waiting -> waiting */
10735 player->is_waiting = TRUE;
10737 player->frame_counter_bored =
10739 game.player_boring_delay_fixed +
10740 GetSimpleRandom(game.player_boring_delay_random);
10741 player->frame_counter_sleeping =
10743 game.player_sleeping_delay_fixed +
10744 GetSimpleRandom(game.player_sleeping_delay_random);
10746 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10749 if (game.player_sleeping_delay_fixed +
10750 game.player_sleeping_delay_random > 0 &&
10751 player->anim_delay_counter == 0 &&
10752 player->post_delay_counter == 0 &&
10753 FrameCounter >= player->frame_counter_sleeping)
10754 player->is_sleeping = TRUE;
10755 else if (game.player_boring_delay_fixed +
10756 game.player_boring_delay_random > 0 &&
10757 FrameCounter >= player->frame_counter_bored)
10758 player->is_bored = TRUE;
10760 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10761 player->is_bored ? ACTION_BORING :
10764 if (player->is_sleeping && player->use_murphy)
10766 /* special case for sleeping Murphy when leaning against non-free tile */
10768 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10769 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10770 !IS_MOVING(player->jx - 1, player->jy)))
10771 move_dir = MV_LEFT;
10772 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10773 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10774 !IS_MOVING(player->jx + 1, player->jy)))
10775 move_dir = MV_RIGHT;
10777 player->is_sleeping = FALSE;
10779 player->dir_waiting = move_dir;
10782 if (player->is_sleeping)
10784 if (player->num_special_action_sleeping > 0)
10786 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10788 int last_special_action = player->special_action_sleeping;
10789 int num_special_action = player->num_special_action_sleeping;
10790 int special_action =
10791 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10792 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10793 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10794 last_special_action + 1 : ACTION_SLEEPING);
10795 int special_graphic =
10796 el_act_dir2img(player->artwork_element, special_action, move_dir);
10798 player->anim_delay_counter =
10799 graphic_info[special_graphic].anim_delay_fixed +
10800 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10801 player->post_delay_counter =
10802 graphic_info[special_graphic].post_delay_fixed +
10803 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10805 player->special_action_sleeping = special_action;
10808 if (player->anim_delay_counter > 0)
10810 player->action_waiting = player->special_action_sleeping;
10811 player->anim_delay_counter--;
10813 else if (player->post_delay_counter > 0)
10815 player->post_delay_counter--;
10819 else if (player->is_bored)
10821 if (player->num_special_action_bored > 0)
10823 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10825 int special_action =
10826 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10827 int special_graphic =
10828 el_act_dir2img(player->artwork_element, special_action, move_dir);
10830 player->anim_delay_counter =
10831 graphic_info[special_graphic].anim_delay_fixed +
10832 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10833 player->post_delay_counter =
10834 graphic_info[special_graphic].post_delay_fixed +
10835 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10837 player->special_action_bored = special_action;
10840 if (player->anim_delay_counter > 0)
10842 player->action_waiting = player->special_action_bored;
10843 player->anim_delay_counter--;
10845 else if (player->post_delay_counter > 0)
10847 player->post_delay_counter--;
10852 else if (last_waiting) /* waiting -> not waiting */
10854 player->is_waiting = FALSE;
10855 player->is_bored = FALSE;
10856 player->is_sleeping = FALSE;
10858 player->frame_counter_bored = -1;
10859 player->frame_counter_sleeping = -1;
10861 player->anim_delay_counter = 0;
10862 player->post_delay_counter = 0;
10864 player->dir_waiting = player->MovDir;
10865 player->action_waiting = ACTION_DEFAULT;
10867 player->special_action_bored = ACTION_DEFAULT;
10868 player->special_action_sleeping = ACTION_DEFAULT;
10872 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10874 if ((!player->is_moving && player->was_moving) ||
10875 (player->MovPos == 0 && player->was_moving) ||
10876 (player->is_snapping && !player->was_snapping) ||
10877 (player->is_dropping && !player->was_dropping))
10879 if (!CheckSaveEngineSnapshotToList())
10882 player->was_moving = FALSE;
10883 player->was_snapping = TRUE;
10884 player->was_dropping = TRUE;
10888 if (player->is_moving)
10889 player->was_moving = TRUE;
10891 if (!player->is_snapping)
10892 player->was_snapping = FALSE;
10894 if (!player->is_dropping)
10895 player->was_dropping = FALSE;
10899 static void CheckSingleStepMode(struct PlayerInfo *player)
10901 if (tape.single_step && tape.recording && !tape.pausing)
10903 /* as it is called "single step mode", just return to pause mode when the
10904 player stopped moving after one tile (or never starts moving at all) */
10905 if (!player->is_moving &&
10906 !player->is_pushing &&
10907 !player->is_dropping_pressed)
10909 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10910 SnapField(player, 0, 0); /* stop snapping */
10914 CheckSaveEngineSnapshot(player);
10917 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10919 int left = player_action & JOY_LEFT;
10920 int right = player_action & JOY_RIGHT;
10921 int up = player_action & JOY_UP;
10922 int down = player_action & JOY_DOWN;
10923 int button1 = player_action & JOY_BUTTON_1;
10924 int button2 = player_action & JOY_BUTTON_2;
10925 int dx = (left ? -1 : right ? 1 : 0);
10926 int dy = (up ? -1 : down ? 1 : 0);
10928 if (!player->active || tape.pausing)
10934 SnapField(player, dx, dy);
10938 DropElement(player);
10940 MovePlayer(player, dx, dy);
10943 CheckSingleStepMode(player);
10945 SetPlayerWaiting(player, FALSE);
10947 return player_action;
10951 /* no actions for this player (no input at player's configured device) */
10953 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10954 SnapField(player, 0, 0);
10955 CheckGravityMovementWhenNotMoving(player);
10957 if (player->MovPos == 0)
10958 SetPlayerWaiting(player, TRUE);
10960 if (player->MovPos == 0) /* needed for tape.playing */
10961 player->is_moving = FALSE;
10963 player->is_dropping = FALSE;
10964 player->is_dropping_pressed = FALSE;
10965 player->drop_pressed_delay = 0;
10967 CheckSingleStepMode(player);
10973 static void CheckLevelTime()
10977 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10978 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10980 if (level.native_em_level->lev->home == 0) /* all players at home */
10982 PlayerWins(local_player);
10984 AllPlayersGone = TRUE;
10986 level.native_em_level->lev->home = -1;
10989 if (level.native_em_level->ply[0]->alive == 0 &&
10990 level.native_em_level->ply[1]->alive == 0 &&
10991 level.native_em_level->ply[2]->alive == 0 &&
10992 level.native_em_level->ply[3]->alive == 0) /* all dead */
10993 AllPlayersGone = TRUE;
10995 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10997 if (game_sp.LevelSolved &&
10998 !game_sp.GameOver) /* game won */
11000 PlayerWins(local_player);
11002 game_sp.GameOver = TRUE;
11004 AllPlayersGone = TRUE;
11007 if (game_sp.GameOver) /* game lost */
11008 AllPlayersGone = TRUE;
11010 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11012 if (game_mm.level_solved &&
11013 !game_mm.game_over) /* game won */
11015 PlayerWins(local_player);
11017 game_mm.game_over = TRUE;
11019 AllPlayersGone = TRUE;
11022 if (game_mm.game_over) /* game lost */
11023 AllPlayersGone = TRUE;
11026 if (TimeFrames >= FRAMES_PER_SECOND)
11031 for (i = 0; i < MAX_PLAYERS; i++)
11033 struct PlayerInfo *player = &stored_player[i];
11035 if (SHIELD_ON(player))
11037 player->shield_normal_time_left--;
11039 if (player->shield_deadly_time_left > 0)
11040 player->shield_deadly_time_left--;
11044 if (!local_player->LevelSolved && !level.use_step_counter)
11052 if (TimeLeft <= 10 && setup.time_limit)
11053 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11055 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11056 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11058 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11060 if (!TimeLeft && setup.time_limit)
11062 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11063 level.native_em_level->lev->killed_out_of_time = TRUE;
11065 for (i = 0; i < MAX_PLAYERS; i++)
11066 KillPlayer(&stored_player[i]);
11069 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11071 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11074 level.native_em_level->lev->time =
11075 (game.no_time_limit ? TimePlayed : TimeLeft);
11078 if (tape.recording || tape.playing)
11079 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11082 if (tape.recording || tape.playing)
11083 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11085 UpdateAndDisplayGameControlValues();
11088 void AdvanceFrameAndPlayerCounters(int player_nr)
11092 /* advance frame counters (global frame counter and time frame counter) */
11096 /* advance player counters (counters for move delay, move animation etc.) */
11097 for (i = 0; i < MAX_PLAYERS; i++)
11099 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11100 int move_delay_value = stored_player[i].move_delay_value;
11101 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11103 if (!advance_player_counters) /* not all players may be affected */
11106 if (move_frames == 0) /* less than one move per game frame */
11108 int stepsize = TILEX / move_delay_value;
11109 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11110 int count = (stored_player[i].is_moving ?
11111 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11113 if (count % delay == 0)
11117 stored_player[i].Frame += move_frames;
11119 if (stored_player[i].MovPos != 0)
11120 stored_player[i].StepFrame += move_frames;
11122 if (stored_player[i].move_delay > 0)
11123 stored_player[i].move_delay--;
11125 /* due to bugs in previous versions, counter must count up, not down */
11126 if (stored_player[i].push_delay != -1)
11127 stored_player[i].push_delay++;
11129 if (stored_player[i].drop_delay > 0)
11130 stored_player[i].drop_delay--;
11132 if (stored_player[i].is_dropping_pressed)
11133 stored_player[i].drop_pressed_delay++;
11137 void StartGameActions(boolean init_network_game, boolean record_tape,
11140 unsigned int new_random_seed = InitRND(random_seed);
11143 TapeStartRecording(new_random_seed);
11145 #if defined(NETWORK_AVALIABLE)
11146 if (init_network_game)
11148 SendToServer_StartPlaying();
11157 void GameActionsExt()
11160 static unsigned int game_frame_delay = 0;
11162 unsigned int game_frame_delay_value;
11163 byte *recorded_player_action;
11164 byte summarized_player_action = 0;
11165 byte tape_action[MAX_PLAYERS];
11168 /* detect endless loops, caused by custom element programming */
11169 if (recursion_loop_detected && recursion_loop_depth == 0)
11171 char *message = getStringCat3("Internal Error! Element ",
11172 EL_NAME(recursion_loop_element),
11173 " caused endless loop! Quit the game?");
11175 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11176 EL_NAME(recursion_loop_element));
11178 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11180 recursion_loop_detected = FALSE; /* if game should be continued */
11187 if (game.restart_level)
11188 StartGameActions(options.network, setup.autorecord, level.random_seed);
11190 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11191 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11193 if (level.native_em_level->lev->home == 0) /* all players at home */
11195 PlayerWins(local_player);
11197 AllPlayersGone = TRUE;
11199 level.native_em_level->lev->home = -1;
11202 if (level.native_em_level->ply[0]->alive == 0 &&
11203 level.native_em_level->ply[1]->alive == 0 &&
11204 level.native_em_level->ply[2]->alive == 0 &&
11205 level.native_em_level->ply[3]->alive == 0) /* all dead */
11206 AllPlayersGone = TRUE;
11208 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11210 if (game_sp.LevelSolved &&
11211 !game_sp.GameOver) /* game won */
11213 PlayerWins(local_player);
11215 game_sp.GameOver = TRUE;
11217 AllPlayersGone = TRUE;
11220 if (game_sp.GameOver) /* game lost */
11221 AllPlayersGone = TRUE;
11223 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11225 if (game_mm.level_solved &&
11226 !game_mm.game_over) /* game won */
11228 PlayerWins(local_player);
11230 game_mm.game_over = TRUE;
11232 AllPlayersGone = TRUE;
11235 if (game_mm.game_over) /* game lost */
11236 AllPlayersGone = TRUE;
11239 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11242 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11245 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11248 game_frame_delay_value =
11249 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11251 if (tape.playing && tape.warp_forward && !tape.pausing)
11252 game_frame_delay_value = 0;
11254 SetVideoFrameDelay(game_frame_delay_value);
11258 /* ---------- main game synchronization point ---------- */
11260 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11262 printf("::: skip == %d\n", skip);
11265 /* ---------- main game synchronization point ---------- */
11267 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11271 if (network_playing && !network_player_action_received)
11273 /* try to get network player actions in time */
11275 #if defined(NETWORK_AVALIABLE)
11276 /* last chance to get network player actions without main loop delay */
11277 HandleNetworking();
11280 /* game was quit by network peer */
11281 if (game_status != GAME_MODE_PLAYING)
11284 if (!network_player_action_received)
11285 return; /* failed to get network player actions in time */
11287 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11293 /* at this point we know that we really continue executing the game */
11295 network_player_action_received = FALSE;
11297 /* when playing tape, read previously recorded player input from tape data */
11298 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11300 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11304 if (tape.set_centered_player)
11306 game.centered_player_nr_next = tape.centered_player_nr_next;
11307 game.set_centered_player = TRUE;
11310 for (i = 0; i < MAX_PLAYERS; i++)
11312 summarized_player_action |= stored_player[i].action;
11314 if (!network_playing && (game.team_mode || tape.playing))
11315 stored_player[i].effective_action = stored_player[i].action;
11318 #if defined(NETWORK_AVALIABLE)
11319 if (network_playing)
11320 SendToServer_MovePlayer(summarized_player_action);
11323 // summarize all actions at local players mapped input device position
11324 // (this allows using different input devices in single player mode)
11325 if (!options.network && !game.team_mode)
11326 stored_player[map_player_action[local_player->index_nr]].effective_action =
11327 summarized_player_action;
11329 if (tape.recording &&
11331 setup.input_on_focus &&
11332 game.centered_player_nr != -1)
11334 for (i = 0; i < MAX_PLAYERS; i++)
11335 stored_player[i].effective_action =
11336 (i == game.centered_player_nr ? summarized_player_action : 0);
11339 if (recorded_player_action != NULL)
11340 for (i = 0; i < MAX_PLAYERS; i++)
11341 stored_player[i].effective_action = recorded_player_action[i];
11343 for (i = 0; i < MAX_PLAYERS; i++)
11345 tape_action[i] = stored_player[i].effective_action;
11347 /* (this may happen in the RND game engine if a player was not present on
11348 the playfield on level start, but appeared later from a custom element */
11349 if (setup.team_mode &&
11352 !tape.player_participates[i])
11353 tape.player_participates[i] = TRUE;
11356 /* only record actions from input devices, but not programmed actions */
11357 if (tape.recording)
11358 TapeRecordAction(tape_action);
11360 #if USE_NEW_PLAYER_ASSIGNMENTS
11361 // !!! also map player actions in single player mode !!!
11362 // if (game.team_mode)
11365 byte mapped_action[MAX_PLAYERS];
11367 #if DEBUG_PLAYER_ACTIONS
11369 for (i = 0; i < MAX_PLAYERS; i++)
11370 printf(" %d, ", stored_player[i].effective_action);
11373 for (i = 0; i < MAX_PLAYERS; i++)
11374 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11376 for (i = 0; i < MAX_PLAYERS; i++)
11377 stored_player[i].effective_action = mapped_action[i];
11379 #if DEBUG_PLAYER_ACTIONS
11381 for (i = 0; i < MAX_PLAYERS; i++)
11382 printf(" %d, ", stored_player[i].effective_action);
11386 #if DEBUG_PLAYER_ACTIONS
11390 for (i = 0; i < MAX_PLAYERS; i++)
11391 printf(" %d, ", stored_player[i].effective_action);
11397 for (i = 0; i < MAX_PLAYERS; i++)
11399 // allow engine snapshot in case of changed movement attempt
11400 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11401 (stored_player[i].effective_action & KEY_MOTION))
11402 game.snapshot.changed_action = TRUE;
11404 // allow engine snapshot in case of snapping/dropping attempt
11405 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11406 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11407 game.snapshot.changed_action = TRUE;
11409 game.snapshot.last_action[i] = stored_player[i].effective_action;
11412 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11414 GameActions_EM_Main();
11416 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11418 GameActions_SP_Main();
11420 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11422 GameActions_MM_Main();
11426 GameActions_RND_Main();
11429 BlitScreenToBitmap(backbuffer);
11433 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11435 if (global.show_frames_per_second)
11437 static unsigned int fps_counter = 0;
11438 static int fps_frames = 0;
11439 unsigned int fps_delay_ms = Counter() - fps_counter;
11443 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11445 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11448 fps_counter = Counter();
11450 /* always draw FPS to screen after FPS value was updated */
11451 redraw_mask |= REDRAW_FPS;
11454 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11455 if (GetDrawDeactivationMask() == REDRAW_NONE)
11456 redraw_mask |= REDRAW_FPS;
11460 static void GameActions_CheckSaveEngineSnapshot()
11462 if (!game.snapshot.save_snapshot)
11465 // clear flag for saving snapshot _before_ saving snapshot
11466 game.snapshot.save_snapshot = FALSE;
11468 SaveEngineSnapshotToList();
11475 GameActions_CheckSaveEngineSnapshot();
11478 void GameActions_EM_Main()
11480 byte effective_action[MAX_PLAYERS];
11481 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11484 for (i = 0; i < MAX_PLAYERS; i++)
11485 effective_action[i] = stored_player[i].effective_action;
11487 GameActions_EM(effective_action, warp_mode);
11490 void GameActions_SP_Main()
11492 byte effective_action[MAX_PLAYERS];
11493 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11496 for (i = 0; i < MAX_PLAYERS; i++)
11497 effective_action[i] = stored_player[i].effective_action;
11499 GameActions_SP(effective_action, warp_mode);
11501 for (i = 0; i < MAX_PLAYERS; i++)
11503 if (stored_player[i].force_dropping)
11504 stored_player[i].action |= KEY_BUTTON_DROP;
11506 stored_player[i].force_dropping = FALSE;
11510 void GameActions_MM_Main()
11512 byte effective_action[MAX_PLAYERS];
11513 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11516 for (i = 0; i < MAX_PLAYERS; i++)
11517 effective_action[i] = stored_player[i].effective_action;
11519 GameActions_MM(effective_action, warp_mode);
11522 void GameActions_RND_Main()
11527 void GameActions_RND()
11529 int magic_wall_x = 0, magic_wall_y = 0;
11530 int i, x, y, element, graphic, last_gfx_frame;
11532 InitPlayfieldScanModeVars();
11534 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11536 SCAN_PLAYFIELD(x, y)
11538 ChangeCount[x][y] = 0;
11539 ChangeEvent[x][y] = -1;
11543 if (game.set_centered_player)
11545 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11547 /* switching to "all players" only possible if all players fit to screen */
11548 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11550 game.centered_player_nr_next = game.centered_player_nr;
11551 game.set_centered_player = FALSE;
11554 /* do not switch focus to non-existing (or non-active) player */
11555 if (game.centered_player_nr_next >= 0 &&
11556 !stored_player[game.centered_player_nr_next].active)
11558 game.centered_player_nr_next = game.centered_player_nr;
11559 game.set_centered_player = FALSE;
11563 if (game.set_centered_player &&
11564 ScreenMovPos == 0) /* screen currently aligned at tile position */
11568 if (game.centered_player_nr_next == -1)
11570 setScreenCenteredToAllPlayers(&sx, &sy);
11574 sx = stored_player[game.centered_player_nr_next].jx;
11575 sy = stored_player[game.centered_player_nr_next].jy;
11578 game.centered_player_nr = game.centered_player_nr_next;
11579 game.set_centered_player = FALSE;
11581 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11582 DrawGameDoorValues();
11585 for (i = 0; i < MAX_PLAYERS; i++)
11587 int actual_player_action = stored_player[i].effective_action;
11590 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11591 - rnd_equinox_tetrachloride 048
11592 - rnd_equinox_tetrachloride_ii 096
11593 - rnd_emanuel_schmieg 002
11594 - doctor_sloan_ww 001, 020
11596 if (stored_player[i].MovPos == 0)
11597 CheckGravityMovement(&stored_player[i]);
11600 /* overwrite programmed action with tape action */
11601 if (stored_player[i].programmed_action)
11602 actual_player_action = stored_player[i].programmed_action;
11604 PlayerActions(&stored_player[i], actual_player_action);
11606 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11609 ScrollScreen(NULL, SCROLL_GO_ON);
11611 /* for backwards compatibility, the following code emulates a fixed bug that
11612 occured when pushing elements (causing elements that just made their last
11613 pushing step to already (if possible) make their first falling step in the
11614 same game frame, which is bad); this code is also needed to use the famous
11615 "spring push bug" which is used in older levels and might be wanted to be
11616 used also in newer levels, but in this case the buggy pushing code is only
11617 affecting the "spring" element and no other elements */
11619 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11621 for (i = 0; i < MAX_PLAYERS; i++)
11623 struct PlayerInfo *player = &stored_player[i];
11624 int x = player->jx;
11625 int y = player->jy;
11627 if (player->active && player->is_pushing && player->is_moving &&
11629 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11630 Feld[x][y] == EL_SPRING))
11632 ContinueMoving(x, y);
11634 /* continue moving after pushing (this is actually a bug) */
11635 if (!IS_MOVING(x, y))
11636 Stop[x][y] = FALSE;
11641 SCAN_PLAYFIELD(x, y)
11643 ChangeCount[x][y] = 0;
11644 ChangeEvent[x][y] = -1;
11646 /* this must be handled before main playfield loop */
11647 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11650 if (MovDelay[x][y] <= 0)
11654 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11657 if (MovDelay[x][y] <= 0)
11660 TEST_DrawLevelField(x, y);
11662 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11667 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11669 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11670 printf("GameActions(): This should never happen!\n");
11672 ChangePage[x][y] = -1;
11676 Stop[x][y] = FALSE;
11677 if (WasJustMoving[x][y] > 0)
11678 WasJustMoving[x][y]--;
11679 if (WasJustFalling[x][y] > 0)
11680 WasJustFalling[x][y]--;
11681 if (CheckCollision[x][y] > 0)
11682 CheckCollision[x][y]--;
11683 if (CheckImpact[x][y] > 0)
11684 CheckImpact[x][y]--;
11688 /* reset finished pushing action (not done in ContinueMoving() to allow
11689 continuous pushing animation for elements with zero push delay) */
11690 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11692 ResetGfxAnimation(x, y);
11693 TEST_DrawLevelField(x, y);
11697 if (IS_BLOCKED(x, y))
11701 Blocked2Moving(x, y, &oldx, &oldy);
11702 if (!IS_MOVING(oldx, oldy))
11704 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11705 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11706 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11707 printf("GameActions(): This should never happen!\n");
11713 SCAN_PLAYFIELD(x, y)
11715 element = Feld[x][y];
11716 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11717 last_gfx_frame = GfxFrame[x][y];
11719 ResetGfxFrame(x, y);
11721 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11722 DrawLevelGraphicAnimation(x, y, graphic);
11724 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11725 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11726 ResetRandomAnimationValue(x, y);
11728 SetRandomAnimationValue(x, y);
11730 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11732 if (IS_INACTIVE(element))
11734 if (IS_ANIMATED(graphic))
11735 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11740 /* this may take place after moving, so 'element' may have changed */
11741 if (IS_CHANGING(x, y) &&
11742 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11744 int page = element_info[element].event_page_nr[CE_DELAY];
11746 HandleElementChange(x, y, page);
11748 element = Feld[x][y];
11749 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11752 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11756 element = Feld[x][y];
11757 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11759 if (IS_ANIMATED(graphic) &&
11760 !IS_MOVING(x, y) &&
11762 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11764 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11765 TEST_DrawTwinkleOnField(x, y);
11767 else if (element == EL_ACID)
11770 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11772 else if ((element == EL_EXIT_OPEN ||
11773 element == EL_EM_EXIT_OPEN ||
11774 element == EL_SP_EXIT_OPEN ||
11775 element == EL_STEEL_EXIT_OPEN ||
11776 element == EL_EM_STEEL_EXIT_OPEN ||
11777 element == EL_SP_TERMINAL ||
11778 element == EL_SP_TERMINAL_ACTIVE ||
11779 element == EL_EXTRA_TIME ||
11780 element == EL_SHIELD_NORMAL ||
11781 element == EL_SHIELD_DEADLY) &&
11782 IS_ANIMATED(graphic))
11783 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11784 else if (IS_MOVING(x, y))
11785 ContinueMoving(x, y);
11786 else if (IS_ACTIVE_BOMB(element))
11787 CheckDynamite(x, y);
11788 else if (element == EL_AMOEBA_GROWING)
11789 AmoebeWaechst(x, y);
11790 else if (element == EL_AMOEBA_SHRINKING)
11791 AmoebaDisappearing(x, y);
11793 #if !USE_NEW_AMOEBA_CODE
11794 else if (IS_AMOEBALIVE(element))
11795 AmoebeAbleger(x, y);
11798 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11800 else if (element == EL_EXIT_CLOSED)
11802 else if (element == EL_EM_EXIT_CLOSED)
11804 else if (element == EL_STEEL_EXIT_CLOSED)
11805 CheckExitSteel(x, y);
11806 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11807 CheckExitSteelEM(x, y);
11808 else if (element == EL_SP_EXIT_CLOSED)
11810 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11811 element == EL_EXPANDABLE_STEELWALL_GROWING)
11812 MauerWaechst(x, y);
11813 else if (element == EL_EXPANDABLE_WALL ||
11814 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11815 element == EL_EXPANDABLE_WALL_VERTICAL ||
11816 element == EL_EXPANDABLE_WALL_ANY ||
11817 element == EL_BD_EXPANDABLE_WALL)
11818 MauerAbleger(x, y);
11819 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11820 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11821 element == EL_EXPANDABLE_STEELWALL_ANY)
11822 MauerAblegerStahl(x, y);
11823 else if (element == EL_FLAMES)
11824 CheckForDragon(x, y);
11825 else if (element == EL_EXPLOSION)
11826 ; /* drawing of correct explosion animation is handled separately */
11827 else if (element == EL_ELEMENT_SNAPPING ||
11828 element == EL_DIAGONAL_SHRINKING ||
11829 element == EL_DIAGONAL_GROWING)
11831 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11833 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11835 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11836 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11838 if (IS_BELT_ACTIVE(element))
11839 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11841 if (game.magic_wall_active)
11843 int jx = local_player->jx, jy = local_player->jy;
11845 /* play the element sound at the position nearest to the player */
11846 if ((element == EL_MAGIC_WALL_FULL ||
11847 element == EL_MAGIC_WALL_ACTIVE ||
11848 element == EL_MAGIC_WALL_EMPTYING ||
11849 element == EL_BD_MAGIC_WALL_FULL ||
11850 element == EL_BD_MAGIC_WALL_ACTIVE ||
11851 element == EL_BD_MAGIC_WALL_EMPTYING ||
11852 element == EL_DC_MAGIC_WALL_FULL ||
11853 element == EL_DC_MAGIC_WALL_ACTIVE ||
11854 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11855 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11863 #if USE_NEW_AMOEBA_CODE
11864 /* new experimental amoeba growth stuff */
11865 if (!(FrameCounter % 8))
11867 static unsigned int random = 1684108901;
11869 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11871 x = RND(lev_fieldx);
11872 y = RND(lev_fieldy);
11873 element = Feld[x][y];
11875 if (!IS_PLAYER(x,y) &&
11876 (element == EL_EMPTY ||
11877 CAN_GROW_INTO(element) ||
11878 element == EL_QUICKSAND_EMPTY ||
11879 element == EL_QUICKSAND_FAST_EMPTY ||
11880 element == EL_ACID_SPLASH_LEFT ||
11881 element == EL_ACID_SPLASH_RIGHT))
11883 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11884 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11885 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11886 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11887 Feld[x][y] = EL_AMOEBA_DROP;
11890 random = random * 129 + 1;
11895 game.explosions_delayed = FALSE;
11897 SCAN_PLAYFIELD(x, y)
11899 element = Feld[x][y];
11901 if (ExplodeField[x][y])
11902 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11903 else if (element == EL_EXPLOSION)
11904 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11906 ExplodeField[x][y] = EX_TYPE_NONE;
11909 game.explosions_delayed = TRUE;
11911 if (game.magic_wall_active)
11913 if (!(game.magic_wall_time_left % 4))
11915 int element = Feld[magic_wall_x][magic_wall_y];
11917 if (element == EL_BD_MAGIC_WALL_FULL ||
11918 element == EL_BD_MAGIC_WALL_ACTIVE ||
11919 element == EL_BD_MAGIC_WALL_EMPTYING)
11920 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11921 else if (element == EL_DC_MAGIC_WALL_FULL ||
11922 element == EL_DC_MAGIC_WALL_ACTIVE ||
11923 element == EL_DC_MAGIC_WALL_EMPTYING)
11924 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11926 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11929 if (game.magic_wall_time_left > 0)
11931 game.magic_wall_time_left--;
11933 if (!game.magic_wall_time_left)
11935 SCAN_PLAYFIELD(x, y)
11937 element = Feld[x][y];
11939 if (element == EL_MAGIC_WALL_ACTIVE ||
11940 element == EL_MAGIC_WALL_FULL)
11942 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11943 TEST_DrawLevelField(x, y);
11945 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11946 element == EL_BD_MAGIC_WALL_FULL)
11948 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11949 TEST_DrawLevelField(x, y);
11951 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11952 element == EL_DC_MAGIC_WALL_FULL)
11954 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11955 TEST_DrawLevelField(x, y);
11959 game.magic_wall_active = FALSE;
11964 if (game.light_time_left > 0)
11966 game.light_time_left--;
11968 if (game.light_time_left == 0)
11969 RedrawAllLightSwitchesAndInvisibleElements();
11972 if (game.timegate_time_left > 0)
11974 game.timegate_time_left--;
11976 if (game.timegate_time_left == 0)
11977 CloseAllOpenTimegates();
11980 if (game.lenses_time_left > 0)
11982 game.lenses_time_left--;
11984 if (game.lenses_time_left == 0)
11985 RedrawAllInvisibleElementsForLenses();
11988 if (game.magnify_time_left > 0)
11990 game.magnify_time_left--;
11992 if (game.magnify_time_left == 0)
11993 RedrawAllInvisibleElementsForMagnifier();
11996 for (i = 0; i < MAX_PLAYERS; i++)
11998 struct PlayerInfo *player = &stored_player[i];
12000 if (SHIELD_ON(player))
12002 if (player->shield_deadly_time_left)
12003 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12004 else if (player->shield_normal_time_left)
12005 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12009 #if USE_DELAYED_GFX_REDRAW
12010 SCAN_PLAYFIELD(x, y)
12012 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12014 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12015 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12017 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12018 DrawLevelField(x, y);
12020 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12021 DrawLevelFieldCrumbled(x, y);
12023 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12024 DrawLevelFieldCrumbledNeighbours(x, y);
12026 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12027 DrawTwinkleOnField(x, y);
12030 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12035 PlayAllPlayersSound();
12037 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12039 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12041 local_player->show_envelope = 0;
12044 /* use random number generator in every frame to make it less predictable */
12045 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12049 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12051 int min_x = x, min_y = y, max_x = x, max_y = y;
12054 for (i = 0; i < MAX_PLAYERS; i++)
12056 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12058 if (!stored_player[i].active || &stored_player[i] == player)
12061 min_x = MIN(min_x, jx);
12062 min_y = MIN(min_y, jy);
12063 max_x = MAX(max_x, jx);
12064 max_y = MAX(max_y, jy);
12067 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12070 static boolean AllPlayersInVisibleScreen()
12074 for (i = 0; i < MAX_PLAYERS; i++)
12076 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12078 if (!stored_player[i].active)
12081 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12088 void ScrollLevel(int dx, int dy)
12090 int scroll_offset = 2 * TILEX_VAR;
12093 BlitBitmap(drawto_field, drawto_field,
12094 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12095 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12096 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12097 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12098 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12099 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12103 x = (dx == 1 ? BX1 : BX2);
12104 for (y = BY1; y <= BY2; y++)
12105 DrawScreenField(x, y);
12110 y = (dy == 1 ? BY1 : BY2);
12111 for (x = BX1; x <= BX2; x++)
12112 DrawScreenField(x, y);
12115 redraw_mask |= REDRAW_FIELD;
12118 static boolean canFallDown(struct PlayerInfo *player)
12120 int jx = player->jx, jy = player->jy;
12122 return (IN_LEV_FIELD(jx, jy + 1) &&
12123 (IS_FREE(jx, jy + 1) ||
12124 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12125 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12126 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12129 static boolean canPassField(int x, int y, int move_dir)
12131 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12132 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12133 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12134 int nextx = x + dx;
12135 int nexty = y + dy;
12136 int element = Feld[x][y];
12138 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12139 !CAN_MOVE(element) &&
12140 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12141 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12142 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12145 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12147 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12148 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12149 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12153 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12154 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12155 (IS_DIGGABLE(Feld[newx][newy]) ||
12156 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12157 canPassField(newx, newy, move_dir)));
12160 static void CheckGravityMovement(struct PlayerInfo *player)
12162 if (player->gravity && !player->programmed_action)
12164 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12165 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12166 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12167 int jx = player->jx, jy = player->jy;
12168 boolean player_is_moving_to_valid_field =
12169 (!player_is_snapping &&
12170 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12171 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12172 boolean player_can_fall_down = canFallDown(player);
12174 if (player_can_fall_down &&
12175 !player_is_moving_to_valid_field)
12176 player->programmed_action = MV_DOWN;
12180 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12182 return CheckGravityMovement(player);
12184 if (player->gravity && !player->programmed_action)
12186 int jx = player->jx, jy = player->jy;
12187 boolean field_under_player_is_free =
12188 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12189 boolean player_is_standing_on_valid_field =
12190 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12191 (IS_WALKABLE(Feld[jx][jy]) &&
12192 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12194 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12195 player->programmed_action = MV_DOWN;
12200 MovePlayerOneStep()
12201 -----------------------------------------------------------------------------
12202 dx, dy: direction (non-diagonal) to try to move the player to
12203 real_dx, real_dy: direction as read from input device (can be diagonal)
12206 boolean MovePlayerOneStep(struct PlayerInfo *player,
12207 int dx, int dy, int real_dx, int real_dy)
12209 int jx = player->jx, jy = player->jy;
12210 int new_jx = jx + dx, new_jy = jy + dy;
12212 boolean player_can_move = !player->cannot_move;
12214 if (!player->active || (!dx && !dy))
12215 return MP_NO_ACTION;
12217 player->MovDir = (dx < 0 ? MV_LEFT :
12218 dx > 0 ? MV_RIGHT :
12220 dy > 0 ? MV_DOWN : MV_NONE);
12222 if (!IN_LEV_FIELD(new_jx, new_jy))
12223 return MP_NO_ACTION;
12225 if (!player_can_move)
12227 if (player->MovPos == 0)
12229 player->is_moving = FALSE;
12230 player->is_digging = FALSE;
12231 player->is_collecting = FALSE;
12232 player->is_snapping = FALSE;
12233 player->is_pushing = FALSE;
12237 if (!options.network && game.centered_player_nr == -1 &&
12238 !AllPlayersInSight(player, new_jx, new_jy))
12239 return MP_NO_ACTION;
12241 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12242 if (can_move != MP_MOVING)
12245 /* check if DigField() has caused relocation of the player */
12246 if (player->jx != jx || player->jy != jy)
12247 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12249 StorePlayer[jx][jy] = 0;
12250 player->last_jx = jx;
12251 player->last_jy = jy;
12252 player->jx = new_jx;
12253 player->jy = new_jy;
12254 StorePlayer[new_jx][new_jy] = player->element_nr;
12256 if (player->move_delay_value_next != -1)
12258 player->move_delay_value = player->move_delay_value_next;
12259 player->move_delay_value_next = -1;
12263 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12265 player->step_counter++;
12267 PlayerVisit[jx][jy] = FrameCounter;
12269 player->is_moving = TRUE;
12272 /* should better be called in MovePlayer(), but this breaks some tapes */
12273 ScrollPlayer(player, SCROLL_INIT);
12279 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12281 int jx = player->jx, jy = player->jy;
12282 int old_jx = jx, old_jy = jy;
12283 int moved = MP_NO_ACTION;
12285 if (!player->active)
12290 if (player->MovPos == 0)
12292 player->is_moving = FALSE;
12293 player->is_digging = FALSE;
12294 player->is_collecting = FALSE;
12295 player->is_snapping = FALSE;
12296 player->is_pushing = FALSE;
12302 if (player->move_delay > 0)
12305 player->move_delay = -1; /* set to "uninitialized" value */
12307 /* store if player is automatically moved to next field */
12308 player->is_auto_moving = (player->programmed_action != MV_NONE);
12310 /* remove the last programmed player action */
12311 player->programmed_action = 0;
12313 if (player->MovPos)
12315 /* should only happen if pre-1.2 tape recordings are played */
12316 /* this is only for backward compatibility */
12318 int original_move_delay_value = player->move_delay_value;
12321 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12325 /* scroll remaining steps with finest movement resolution */
12326 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12328 while (player->MovPos)
12330 ScrollPlayer(player, SCROLL_GO_ON);
12331 ScrollScreen(NULL, SCROLL_GO_ON);
12333 AdvanceFrameAndPlayerCounters(player->index_nr);
12336 BackToFront_WithFrameDelay(0);
12339 player->move_delay_value = original_move_delay_value;
12342 player->is_active = FALSE;
12344 if (player->last_move_dir & MV_HORIZONTAL)
12346 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12347 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12351 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12352 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12355 if (!moved && !player->is_active)
12357 player->is_moving = FALSE;
12358 player->is_digging = FALSE;
12359 player->is_collecting = FALSE;
12360 player->is_snapping = FALSE;
12361 player->is_pushing = FALSE;
12367 if (moved & MP_MOVING && !ScreenMovPos &&
12368 (player->index_nr == game.centered_player_nr ||
12369 game.centered_player_nr == -1))
12371 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12372 int offset = game.scroll_delay_value;
12374 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12376 /* actual player has left the screen -- scroll in that direction */
12377 if (jx != old_jx) /* player has moved horizontally */
12378 scroll_x += (jx - old_jx);
12379 else /* player has moved vertically */
12380 scroll_y += (jy - old_jy);
12384 if (jx != old_jx) /* player has moved horizontally */
12386 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12387 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12388 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12390 /* don't scroll over playfield boundaries */
12391 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12392 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12394 /* don't scroll more than one field at a time */
12395 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12397 /* don't scroll against the player's moving direction */
12398 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12399 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12400 scroll_x = old_scroll_x;
12402 else /* player has moved vertically */
12404 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12405 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12406 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12408 /* don't scroll over playfield boundaries */
12409 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12410 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12412 /* don't scroll more than one field at a time */
12413 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12415 /* don't scroll against the player's moving direction */
12416 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12417 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12418 scroll_y = old_scroll_y;
12422 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12424 if (!options.network && game.centered_player_nr == -1 &&
12425 !AllPlayersInVisibleScreen())
12427 scroll_x = old_scroll_x;
12428 scroll_y = old_scroll_y;
12432 ScrollScreen(player, SCROLL_INIT);
12433 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12438 player->StepFrame = 0;
12440 if (moved & MP_MOVING)
12442 if (old_jx != jx && old_jy == jy)
12443 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12444 else if (old_jx == jx && old_jy != jy)
12445 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12447 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12449 player->last_move_dir = player->MovDir;
12450 player->is_moving = TRUE;
12451 player->is_snapping = FALSE;
12452 player->is_switching = FALSE;
12453 player->is_dropping = FALSE;
12454 player->is_dropping_pressed = FALSE;
12455 player->drop_pressed_delay = 0;
12458 /* should better be called here than above, but this breaks some tapes */
12459 ScrollPlayer(player, SCROLL_INIT);
12464 CheckGravityMovementWhenNotMoving(player);
12466 player->is_moving = FALSE;
12468 /* at this point, the player is allowed to move, but cannot move right now
12469 (e.g. because of something blocking the way) -- ensure that the player
12470 is also allowed to move in the next frame (in old versions before 3.1.1,
12471 the player was forced to wait again for eight frames before next try) */
12473 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12474 player->move_delay = 0; /* allow direct movement in the next frame */
12477 if (player->move_delay == -1) /* not yet initialized by DigField() */
12478 player->move_delay = player->move_delay_value;
12480 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12482 TestIfPlayerTouchesBadThing(jx, jy);
12483 TestIfPlayerTouchesCustomElement(jx, jy);
12486 if (!player->active)
12487 RemovePlayer(player);
12492 void ScrollPlayer(struct PlayerInfo *player, int mode)
12494 int jx = player->jx, jy = player->jy;
12495 int last_jx = player->last_jx, last_jy = player->last_jy;
12496 int move_stepsize = TILEX / player->move_delay_value;
12498 if (!player->active)
12501 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12504 if (mode == SCROLL_INIT)
12506 player->actual_frame_counter = FrameCounter;
12507 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12509 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12510 Feld[last_jx][last_jy] == EL_EMPTY)
12512 int last_field_block_delay = 0; /* start with no blocking at all */
12513 int block_delay_adjustment = player->block_delay_adjustment;
12515 /* if player blocks last field, add delay for exactly one move */
12516 if (player->block_last_field)
12518 last_field_block_delay += player->move_delay_value;
12520 /* when blocking enabled, prevent moving up despite gravity */
12521 if (player->gravity && player->MovDir == MV_UP)
12522 block_delay_adjustment = -1;
12525 /* add block delay adjustment (also possible when not blocking) */
12526 last_field_block_delay += block_delay_adjustment;
12528 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12529 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12532 if (player->MovPos != 0) /* player has not yet reached destination */
12535 else if (!FrameReached(&player->actual_frame_counter, 1))
12538 if (player->MovPos != 0)
12540 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12541 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12543 /* before DrawPlayer() to draw correct player graphic for this case */
12544 if (player->MovPos == 0)
12545 CheckGravityMovement(player);
12548 if (player->MovPos == 0) /* player reached destination field */
12550 if (player->move_delay_reset_counter > 0)
12552 player->move_delay_reset_counter--;
12554 if (player->move_delay_reset_counter == 0)
12556 /* continue with normal speed after quickly moving through gate */
12557 HALVE_PLAYER_SPEED(player);
12559 /* be able to make the next move without delay */
12560 player->move_delay = 0;
12564 player->last_jx = jx;
12565 player->last_jy = jy;
12567 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12568 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12569 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12570 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12571 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12572 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12573 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12574 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12576 DrawPlayer(player); /* needed here only to cleanup last field */
12577 RemovePlayer(player);
12579 if (local_player->friends_still_needed == 0 ||
12580 IS_SP_ELEMENT(Feld[jx][jy]))
12581 PlayerWins(player);
12584 /* this breaks one level: "machine", level 000 */
12586 int move_direction = player->MovDir;
12587 int enter_side = MV_DIR_OPPOSITE(move_direction);
12588 int leave_side = move_direction;
12589 int old_jx = last_jx;
12590 int old_jy = last_jy;
12591 int old_element = Feld[old_jx][old_jy];
12592 int new_element = Feld[jx][jy];
12594 if (IS_CUSTOM_ELEMENT(old_element))
12595 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12597 player->index_bit, leave_side);
12599 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12600 CE_PLAYER_LEAVES_X,
12601 player->index_bit, leave_side);
12603 if (IS_CUSTOM_ELEMENT(new_element))
12604 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12605 player->index_bit, enter_side);
12607 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12608 CE_PLAYER_ENTERS_X,
12609 player->index_bit, enter_side);
12611 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12612 CE_MOVE_OF_X, move_direction);
12615 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12617 TestIfPlayerTouchesBadThing(jx, jy);
12618 TestIfPlayerTouchesCustomElement(jx, jy);
12620 /* needed because pushed element has not yet reached its destination,
12621 so it would trigger a change event at its previous field location */
12622 if (!player->is_pushing)
12623 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12625 if (!player->active)
12626 RemovePlayer(player);
12629 if (!local_player->LevelSolved && level.use_step_counter)
12639 if (TimeLeft <= 10 && setup.time_limit)
12640 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12642 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12644 DisplayGameControlValues();
12646 if (!TimeLeft && setup.time_limit)
12647 for (i = 0; i < MAX_PLAYERS; i++)
12648 KillPlayer(&stored_player[i]);
12650 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12652 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12654 DisplayGameControlValues();
12658 if (tape.single_step && tape.recording && !tape.pausing &&
12659 !player->programmed_action)
12660 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12662 if (!player->programmed_action)
12663 CheckSaveEngineSnapshot(player);
12667 void ScrollScreen(struct PlayerInfo *player, int mode)
12669 static unsigned int screen_frame_counter = 0;
12671 if (mode == SCROLL_INIT)
12673 /* set scrolling step size according to actual player's moving speed */
12674 ScrollStepSize = TILEX / player->move_delay_value;
12676 screen_frame_counter = FrameCounter;
12677 ScreenMovDir = player->MovDir;
12678 ScreenMovPos = player->MovPos;
12679 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12682 else if (!FrameReached(&screen_frame_counter, 1))
12687 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12688 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12689 redraw_mask |= REDRAW_FIELD;
12692 ScreenMovDir = MV_NONE;
12695 void TestIfPlayerTouchesCustomElement(int x, int y)
12697 static int xy[4][2] =
12704 static int trigger_sides[4][2] =
12706 /* center side border side */
12707 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12708 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12709 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12710 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12712 static int touch_dir[4] =
12714 MV_LEFT | MV_RIGHT,
12719 int center_element = Feld[x][y]; /* should always be non-moving! */
12722 for (i = 0; i < NUM_DIRECTIONS; i++)
12724 int xx = x + xy[i][0];
12725 int yy = y + xy[i][1];
12726 int center_side = trigger_sides[i][0];
12727 int border_side = trigger_sides[i][1];
12728 int border_element;
12730 if (!IN_LEV_FIELD(xx, yy))
12733 if (IS_PLAYER(x, y)) /* player found at center element */
12735 struct PlayerInfo *player = PLAYERINFO(x, y);
12737 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12738 border_element = Feld[xx][yy]; /* may be moving! */
12739 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12740 border_element = Feld[xx][yy];
12741 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12742 border_element = MovingOrBlocked2Element(xx, yy);
12744 continue; /* center and border element do not touch */
12746 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12747 player->index_bit, border_side);
12748 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12749 CE_PLAYER_TOUCHES_X,
12750 player->index_bit, border_side);
12753 /* use player element that is initially defined in the level playfield,
12754 not the player element that corresponds to the runtime player number
12755 (example: a level that contains EL_PLAYER_3 as the only player would
12756 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12757 int player_element = PLAYERINFO(x, y)->initial_element;
12759 CheckElementChangeBySide(xx, yy, border_element, player_element,
12760 CE_TOUCHING_X, border_side);
12763 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12765 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12767 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12769 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12770 continue; /* center and border element do not touch */
12773 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12774 player->index_bit, center_side);
12775 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12776 CE_PLAYER_TOUCHES_X,
12777 player->index_bit, center_side);
12780 /* use player element that is initially defined in the level playfield,
12781 not the player element that corresponds to the runtime player number
12782 (example: a level that contains EL_PLAYER_3 as the only player would
12783 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12784 int player_element = PLAYERINFO(xx, yy)->initial_element;
12786 CheckElementChangeBySide(x, y, center_element, player_element,
12787 CE_TOUCHING_X, center_side);
12795 void TestIfElementTouchesCustomElement(int x, int y)
12797 static int xy[4][2] =
12804 static int trigger_sides[4][2] =
12806 /* center side border side */
12807 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12808 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12809 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12810 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12812 static int touch_dir[4] =
12814 MV_LEFT | MV_RIGHT,
12819 boolean change_center_element = FALSE;
12820 int center_element = Feld[x][y]; /* should always be non-moving! */
12821 int border_element_old[NUM_DIRECTIONS];
12824 for (i = 0; i < NUM_DIRECTIONS; i++)
12826 int xx = x + xy[i][0];
12827 int yy = y + xy[i][1];
12828 int border_element;
12830 border_element_old[i] = -1;
12832 if (!IN_LEV_FIELD(xx, yy))
12835 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12836 border_element = Feld[xx][yy]; /* may be moving! */
12837 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12838 border_element = Feld[xx][yy];
12839 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12840 border_element = MovingOrBlocked2Element(xx, yy);
12842 continue; /* center and border element do not touch */
12844 border_element_old[i] = border_element;
12847 for (i = 0; i < NUM_DIRECTIONS; i++)
12849 int xx = x + xy[i][0];
12850 int yy = y + xy[i][1];
12851 int center_side = trigger_sides[i][0];
12852 int border_element = border_element_old[i];
12854 if (border_element == -1)
12857 /* check for change of border element */
12858 CheckElementChangeBySide(xx, yy, border_element, center_element,
12859 CE_TOUCHING_X, center_side);
12861 /* (center element cannot be player, so we dont have to check this here) */
12864 for (i = 0; i < NUM_DIRECTIONS; i++)
12866 int xx = x + xy[i][0];
12867 int yy = y + xy[i][1];
12868 int border_side = trigger_sides[i][1];
12869 int border_element = border_element_old[i];
12871 if (border_element == -1)
12874 /* check for change of center element (but change it only once) */
12875 if (!change_center_element)
12876 change_center_element =
12877 CheckElementChangeBySide(x, y, center_element, border_element,
12878 CE_TOUCHING_X, border_side);
12880 if (IS_PLAYER(xx, yy))
12882 /* use player element that is initially defined in the level playfield,
12883 not the player element that corresponds to the runtime player number
12884 (example: a level that contains EL_PLAYER_3 as the only player would
12885 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12886 int player_element = PLAYERINFO(xx, yy)->initial_element;
12888 CheckElementChangeBySide(x, y, center_element, player_element,
12889 CE_TOUCHING_X, border_side);
12894 void TestIfElementHitsCustomElement(int x, int y, int direction)
12896 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12897 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12898 int hitx = x + dx, hity = y + dy;
12899 int hitting_element = Feld[x][y];
12900 int touched_element;
12902 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12905 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12906 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12908 if (IN_LEV_FIELD(hitx, hity))
12910 int opposite_direction = MV_DIR_OPPOSITE(direction);
12911 int hitting_side = direction;
12912 int touched_side = opposite_direction;
12913 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12914 MovDir[hitx][hity] != direction ||
12915 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12921 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12922 CE_HITTING_X, touched_side);
12924 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12925 CE_HIT_BY_X, hitting_side);
12927 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12928 CE_HIT_BY_SOMETHING, opposite_direction);
12930 if (IS_PLAYER(hitx, hity))
12932 /* use player element that is initially defined in the level playfield,
12933 not the player element that corresponds to the runtime player number
12934 (example: a level that contains EL_PLAYER_3 as the only player would
12935 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12936 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12938 CheckElementChangeBySide(x, y, hitting_element, player_element,
12939 CE_HITTING_X, touched_side);
12944 /* "hitting something" is also true when hitting the playfield border */
12945 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12946 CE_HITTING_SOMETHING, direction);
12949 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12951 int i, kill_x = -1, kill_y = -1;
12953 int bad_element = -1;
12954 static int test_xy[4][2] =
12961 static int test_dir[4] =
12969 for (i = 0; i < NUM_DIRECTIONS; i++)
12971 int test_x, test_y, test_move_dir, test_element;
12973 test_x = good_x + test_xy[i][0];
12974 test_y = good_y + test_xy[i][1];
12976 if (!IN_LEV_FIELD(test_x, test_y))
12980 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12982 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12984 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12985 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12987 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12988 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12992 bad_element = test_element;
12998 if (kill_x != -1 || kill_y != -1)
13000 if (IS_PLAYER(good_x, good_y))
13002 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13004 if (player->shield_deadly_time_left > 0 &&
13005 !IS_INDESTRUCTIBLE(bad_element))
13006 Bang(kill_x, kill_y);
13007 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13008 KillPlayer(player);
13011 Bang(good_x, good_y);
13015 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13017 int i, kill_x = -1, kill_y = -1;
13018 int bad_element = Feld[bad_x][bad_y];
13019 static int test_xy[4][2] =
13026 static int touch_dir[4] =
13028 MV_LEFT | MV_RIGHT,
13033 static int test_dir[4] =
13041 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13044 for (i = 0; i < NUM_DIRECTIONS; i++)
13046 int test_x, test_y, test_move_dir, test_element;
13048 test_x = bad_x + test_xy[i][0];
13049 test_y = bad_y + test_xy[i][1];
13051 if (!IN_LEV_FIELD(test_x, test_y))
13055 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13057 test_element = Feld[test_x][test_y];
13059 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13060 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13062 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13063 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13065 /* good thing is player or penguin that does not move away */
13066 if (IS_PLAYER(test_x, test_y))
13068 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13070 if (bad_element == EL_ROBOT && player->is_moving)
13071 continue; /* robot does not kill player if he is moving */
13073 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13075 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13076 continue; /* center and border element do not touch */
13084 else if (test_element == EL_PENGUIN)
13094 if (kill_x != -1 || kill_y != -1)
13096 if (IS_PLAYER(kill_x, kill_y))
13098 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13100 if (player->shield_deadly_time_left > 0 &&
13101 !IS_INDESTRUCTIBLE(bad_element))
13102 Bang(bad_x, bad_y);
13103 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13104 KillPlayer(player);
13107 Bang(kill_x, kill_y);
13111 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13113 int bad_element = Feld[bad_x][bad_y];
13114 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13115 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13116 int test_x = bad_x + dx, test_y = bad_y + dy;
13117 int test_move_dir, test_element;
13118 int kill_x = -1, kill_y = -1;
13120 if (!IN_LEV_FIELD(test_x, test_y))
13124 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13126 test_element = Feld[test_x][test_y];
13128 if (test_move_dir != bad_move_dir)
13130 /* good thing can be player or penguin that does not move away */
13131 if (IS_PLAYER(test_x, test_y))
13133 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13135 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13136 player as being hit when he is moving towards the bad thing, because
13137 the "get hit by" condition would be lost after the player stops) */
13138 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13139 return; /* player moves away from bad thing */
13144 else if (test_element == EL_PENGUIN)
13151 if (kill_x != -1 || kill_y != -1)
13153 if (IS_PLAYER(kill_x, kill_y))
13155 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13157 if (player->shield_deadly_time_left > 0 &&
13158 !IS_INDESTRUCTIBLE(bad_element))
13159 Bang(bad_x, bad_y);
13160 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13161 KillPlayer(player);
13164 Bang(kill_x, kill_y);
13168 void TestIfPlayerTouchesBadThing(int x, int y)
13170 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13173 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13175 TestIfGoodThingHitsBadThing(x, y, move_dir);
13178 void TestIfBadThingTouchesPlayer(int x, int y)
13180 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13183 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13185 TestIfBadThingHitsGoodThing(x, y, move_dir);
13188 void TestIfFriendTouchesBadThing(int x, int y)
13190 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13193 void TestIfBadThingTouchesFriend(int x, int y)
13195 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13198 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13200 int i, kill_x = bad_x, kill_y = bad_y;
13201 static int xy[4][2] =
13209 for (i = 0; i < NUM_DIRECTIONS; i++)
13213 x = bad_x + xy[i][0];
13214 y = bad_y + xy[i][1];
13215 if (!IN_LEV_FIELD(x, y))
13218 element = Feld[x][y];
13219 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13220 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13228 if (kill_x != bad_x || kill_y != bad_y)
13229 Bang(bad_x, bad_y);
13232 void KillPlayer(struct PlayerInfo *player)
13234 int jx = player->jx, jy = player->jy;
13236 if (!player->active)
13240 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13241 player->killed, player->active, player->reanimated);
13244 /* the following code was introduced to prevent an infinite loop when calling
13246 -> CheckTriggeredElementChangeExt()
13247 -> ExecuteCustomElementAction()
13249 -> (infinitely repeating the above sequence of function calls)
13250 which occurs when killing the player while having a CE with the setting
13251 "kill player X when explosion of <player X>"; the solution using a new
13252 field "player->killed" was chosen for backwards compatibility, although
13253 clever use of the fields "player->active" etc. would probably also work */
13255 if (player->killed)
13259 player->killed = TRUE;
13261 /* remove accessible field at the player's position */
13262 Feld[jx][jy] = EL_EMPTY;
13264 /* deactivate shield (else Bang()/Explode() would not work right) */
13265 player->shield_normal_time_left = 0;
13266 player->shield_deadly_time_left = 0;
13269 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13270 player->killed, player->active, player->reanimated);
13276 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13277 player->killed, player->active, player->reanimated);
13280 if (player->reanimated) /* killed player may have been reanimated */
13281 player->killed = player->reanimated = FALSE;
13283 BuryPlayer(player);
13286 static void KillPlayerUnlessEnemyProtected(int x, int y)
13288 if (!PLAYER_ENEMY_PROTECTED(x, y))
13289 KillPlayer(PLAYERINFO(x, y));
13292 static void KillPlayerUnlessExplosionProtected(int x, int y)
13294 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13295 KillPlayer(PLAYERINFO(x, y));
13298 void BuryPlayer(struct PlayerInfo *player)
13300 int jx = player->jx, jy = player->jy;
13302 if (!player->active)
13305 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13306 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13308 player->GameOver = TRUE;
13309 RemovePlayer(player);
13312 void RemovePlayer(struct PlayerInfo *player)
13314 int jx = player->jx, jy = player->jy;
13315 int i, found = FALSE;
13317 player->present = FALSE;
13318 player->active = FALSE;
13320 if (!ExplodeField[jx][jy])
13321 StorePlayer[jx][jy] = 0;
13323 if (player->is_moving)
13324 TEST_DrawLevelField(player->last_jx, player->last_jy);
13326 for (i = 0; i < MAX_PLAYERS; i++)
13327 if (stored_player[i].active)
13331 AllPlayersGone = TRUE;
13337 static void setFieldForSnapping(int x, int y, int element, int direction)
13339 struct ElementInfo *ei = &element_info[element];
13340 int direction_bit = MV_DIR_TO_BIT(direction);
13341 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13342 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13343 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13345 Feld[x][y] = EL_ELEMENT_SNAPPING;
13346 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13348 ResetGfxAnimation(x, y);
13350 GfxElement[x][y] = element;
13351 GfxAction[x][y] = action;
13352 GfxDir[x][y] = direction;
13353 GfxFrame[x][y] = -1;
13357 =============================================================================
13358 checkDiagonalPushing()
13359 -----------------------------------------------------------------------------
13360 check if diagonal input device direction results in pushing of object
13361 (by checking if the alternative direction is walkable, diggable, ...)
13362 =============================================================================
13365 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13366 int x, int y, int real_dx, int real_dy)
13368 int jx, jy, dx, dy, xx, yy;
13370 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13373 /* diagonal direction: check alternative direction */
13378 xx = jx + (dx == 0 ? real_dx : 0);
13379 yy = jy + (dy == 0 ? real_dy : 0);
13381 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13385 =============================================================================
13387 -----------------------------------------------------------------------------
13388 x, y: field next to player (non-diagonal) to try to dig to
13389 real_dx, real_dy: direction as read from input device (can be diagonal)
13390 =============================================================================
13393 static int DigField(struct PlayerInfo *player,
13394 int oldx, int oldy, int x, int y,
13395 int real_dx, int real_dy, int mode)
13397 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13398 boolean player_was_pushing = player->is_pushing;
13399 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13400 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13401 int jx = oldx, jy = oldy;
13402 int dx = x - jx, dy = y - jy;
13403 int nextx = x + dx, nexty = y + dy;
13404 int move_direction = (dx == -1 ? MV_LEFT :
13405 dx == +1 ? MV_RIGHT :
13407 dy == +1 ? MV_DOWN : MV_NONE);
13408 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13409 int dig_side = MV_DIR_OPPOSITE(move_direction);
13410 int old_element = Feld[jx][jy];
13411 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13414 if (is_player) /* function can also be called by EL_PENGUIN */
13416 if (player->MovPos == 0)
13418 player->is_digging = FALSE;
13419 player->is_collecting = FALSE;
13422 if (player->MovPos == 0) /* last pushing move finished */
13423 player->is_pushing = FALSE;
13425 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13427 player->is_switching = FALSE;
13428 player->push_delay = -1;
13430 return MP_NO_ACTION;
13434 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13435 old_element = Back[jx][jy];
13437 /* in case of element dropped at player position, check background */
13438 else if (Back[jx][jy] != EL_EMPTY &&
13439 game.engine_version >= VERSION_IDENT(2,2,0,0))
13440 old_element = Back[jx][jy];
13442 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13443 return MP_NO_ACTION; /* field has no opening in this direction */
13445 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13446 return MP_NO_ACTION; /* field has no opening in this direction */
13448 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13452 Feld[jx][jy] = player->artwork_element;
13453 InitMovingField(jx, jy, MV_DOWN);
13454 Store[jx][jy] = EL_ACID;
13455 ContinueMoving(jx, jy);
13456 BuryPlayer(player);
13458 return MP_DONT_RUN_INTO;
13461 if (player_can_move && DONT_RUN_INTO(element))
13463 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13465 return MP_DONT_RUN_INTO;
13468 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13469 return MP_NO_ACTION;
13471 collect_count = element_info[element].collect_count_initial;
13473 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13474 return MP_NO_ACTION;
13476 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13477 player_can_move = player_can_move_or_snap;
13479 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13480 game.engine_version >= VERSION_IDENT(2,2,0,0))
13482 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13483 player->index_bit, dig_side);
13484 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13485 player->index_bit, dig_side);
13487 if (element == EL_DC_LANDMINE)
13490 if (Feld[x][y] != element) /* field changed by snapping */
13493 return MP_NO_ACTION;
13496 if (player->gravity && is_player && !player->is_auto_moving &&
13497 canFallDown(player) && move_direction != MV_DOWN &&
13498 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13499 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13501 if (player_can_move &&
13502 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13504 int sound_element = SND_ELEMENT(element);
13505 int sound_action = ACTION_WALKING;
13507 if (IS_RND_GATE(element))
13509 if (!player->key[RND_GATE_NR(element)])
13510 return MP_NO_ACTION;
13512 else if (IS_RND_GATE_GRAY(element))
13514 if (!player->key[RND_GATE_GRAY_NR(element)])
13515 return MP_NO_ACTION;
13517 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13519 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13520 return MP_NO_ACTION;
13522 else if (element == EL_EXIT_OPEN ||
13523 element == EL_EM_EXIT_OPEN ||
13524 element == EL_EM_EXIT_OPENING ||
13525 element == EL_STEEL_EXIT_OPEN ||
13526 element == EL_EM_STEEL_EXIT_OPEN ||
13527 element == EL_EM_STEEL_EXIT_OPENING ||
13528 element == EL_SP_EXIT_OPEN ||
13529 element == EL_SP_EXIT_OPENING)
13531 sound_action = ACTION_PASSING; /* player is passing exit */
13533 else if (element == EL_EMPTY)
13535 sound_action = ACTION_MOVING; /* nothing to walk on */
13538 /* play sound from background or player, whatever is available */
13539 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13540 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13542 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13544 else if (player_can_move &&
13545 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13547 if (!ACCESS_FROM(element, opposite_direction))
13548 return MP_NO_ACTION; /* field not accessible from this direction */
13550 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13551 return MP_NO_ACTION;
13553 if (IS_EM_GATE(element))
13555 if (!player->key[EM_GATE_NR(element)])
13556 return MP_NO_ACTION;
13558 else if (IS_EM_GATE_GRAY(element))
13560 if (!player->key[EM_GATE_GRAY_NR(element)])
13561 return MP_NO_ACTION;
13563 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13565 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13566 return MP_NO_ACTION;
13568 else if (IS_EMC_GATE(element))
13570 if (!player->key[EMC_GATE_NR(element)])
13571 return MP_NO_ACTION;
13573 else if (IS_EMC_GATE_GRAY(element))
13575 if (!player->key[EMC_GATE_GRAY_NR(element)])
13576 return MP_NO_ACTION;
13578 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13580 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13581 return MP_NO_ACTION;
13583 else if (element == EL_DC_GATE_WHITE ||
13584 element == EL_DC_GATE_WHITE_GRAY ||
13585 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13587 if (player->num_white_keys == 0)
13588 return MP_NO_ACTION;
13590 player->num_white_keys--;
13592 else if (IS_SP_PORT(element))
13594 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13595 element == EL_SP_GRAVITY_PORT_RIGHT ||
13596 element == EL_SP_GRAVITY_PORT_UP ||
13597 element == EL_SP_GRAVITY_PORT_DOWN)
13598 player->gravity = !player->gravity;
13599 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13600 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13601 element == EL_SP_GRAVITY_ON_PORT_UP ||
13602 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13603 player->gravity = TRUE;
13604 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13605 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13606 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13607 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13608 player->gravity = FALSE;
13611 /* automatically move to the next field with double speed */
13612 player->programmed_action = move_direction;
13614 if (player->move_delay_reset_counter == 0)
13616 player->move_delay_reset_counter = 2; /* two double speed steps */
13618 DOUBLE_PLAYER_SPEED(player);
13621 PlayLevelSoundAction(x, y, ACTION_PASSING);
13623 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13627 if (mode != DF_SNAP)
13629 GfxElement[x][y] = GFX_ELEMENT(element);
13630 player->is_digging = TRUE;
13633 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13635 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13636 player->index_bit, dig_side);
13638 if (mode == DF_SNAP)
13640 if (level.block_snap_field)
13641 setFieldForSnapping(x, y, element, move_direction);
13643 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13645 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13646 player->index_bit, dig_side);
13649 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13653 if (is_player && mode != DF_SNAP)
13655 GfxElement[x][y] = element;
13656 player->is_collecting = TRUE;
13659 if (element == EL_SPEED_PILL)
13661 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13663 else if (element == EL_EXTRA_TIME && level.time > 0)
13665 TimeLeft += level.extra_time;
13667 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13669 DisplayGameControlValues();
13671 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13673 player->shield_normal_time_left += level.shield_normal_time;
13674 if (element == EL_SHIELD_DEADLY)
13675 player->shield_deadly_time_left += level.shield_deadly_time;
13677 else if (element == EL_DYNAMITE ||
13678 element == EL_EM_DYNAMITE ||
13679 element == EL_SP_DISK_RED)
13681 if (player->inventory_size < MAX_INVENTORY_SIZE)
13682 player->inventory_element[player->inventory_size++] = element;
13684 DrawGameDoorValues();
13686 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13688 player->dynabomb_count++;
13689 player->dynabombs_left++;
13691 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13693 player->dynabomb_size++;
13695 else if (element == EL_DYNABOMB_INCREASE_POWER)
13697 player->dynabomb_xl = TRUE;
13699 else if (IS_KEY(element))
13701 player->key[KEY_NR(element)] = TRUE;
13703 DrawGameDoorValues();
13705 else if (element == EL_DC_KEY_WHITE)
13707 player->num_white_keys++;
13709 /* display white keys? */
13710 /* DrawGameDoorValues(); */
13712 else if (IS_ENVELOPE(element))
13714 player->show_envelope = element;
13716 else if (element == EL_EMC_LENSES)
13718 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13720 RedrawAllInvisibleElementsForLenses();
13722 else if (element == EL_EMC_MAGNIFIER)
13724 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13726 RedrawAllInvisibleElementsForMagnifier();
13728 else if (IS_DROPPABLE(element) ||
13729 IS_THROWABLE(element)) /* can be collected and dropped */
13733 if (collect_count == 0)
13734 player->inventory_infinite_element = element;
13736 for (i = 0; i < collect_count; i++)
13737 if (player->inventory_size < MAX_INVENTORY_SIZE)
13738 player->inventory_element[player->inventory_size++] = element;
13740 DrawGameDoorValues();
13742 else if (collect_count > 0)
13744 local_player->gems_still_needed -= collect_count;
13745 if (local_player->gems_still_needed < 0)
13746 local_player->gems_still_needed = 0;
13748 game.snapshot.collected_item = TRUE;
13750 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13752 DisplayGameControlValues();
13755 RaiseScoreElement(element);
13756 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13759 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13760 player->index_bit, dig_side);
13762 if (mode == DF_SNAP)
13764 if (level.block_snap_field)
13765 setFieldForSnapping(x, y, element, move_direction);
13767 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13769 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13770 player->index_bit, dig_side);
13773 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13775 if (mode == DF_SNAP && element != EL_BD_ROCK)
13776 return MP_NO_ACTION;
13778 if (CAN_FALL(element) && dy)
13779 return MP_NO_ACTION;
13781 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13782 !(element == EL_SPRING && level.use_spring_bug))
13783 return MP_NO_ACTION;
13785 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13786 ((move_direction & MV_VERTICAL &&
13787 ((element_info[element].move_pattern & MV_LEFT &&
13788 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13789 (element_info[element].move_pattern & MV_RIGHT &&
13790 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13791 (move_direction & MV_HORIZONTAL &&
13792 ((element_info[element].move_pattern & MV_UP &&
13793 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13794 (element_info[element].move_pattern & MV_DOWN &&
13795 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13796 return MP_NO_ACTION;
13798 /* do not push elements already moving away faster than player */
13799 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13800 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13801 return MP_NO_ACTION;
13803 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13805 if (player->push_delay_value == -1 || !player_was_pushing)
13806 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13808 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13810 if (player->push_delay_value == -1)
13811 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13813 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13815 if (!player->is_pushing)
13816 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13819 player->is_pushing = TRUE;
13820 player->is_active = TRUE;
13822 if (!(IN_LEV_FIELD(nextx, nexty) &&
13823 (IS_FREE(nextx, nexty) ||
13824 (IS_SB_ELEMENT(element) &&
13825 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13826 (IS_CUSTOM_ELEMENT(element) &&
13827 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13828 return MP_NO_ACTION;
13830 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13831 return MP_NO_ACTION;
13833 if (player->push_delay == -1) /* new pushing; restart delay */
13834 player->push_delay = 0;
13836 if (player->push_delay < player->push_delay_value &&
13837 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13838 element != EL_SPRING && element != EL_BALLOON)
13840 /* make sure that there is no move delay before next try to push */
13841 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13842 player->move_delay = 0;
13844 return MP_NO_ACTION;
13847 if (IS_CUSTOM_ELEMENT(element) &&
13848 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13850 if (!DigFieldByCE(nextx, nexty, element))
13851 return MP_NO_ACTION;
13854 if (IS_SB_ELEMENT(element))
13856 if (element == EL_SOKOBAN_FIELD_FULL)
13858 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13859 local_player->sokobanfields_still_needed++;
13862 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13864 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13865 local_player->sokobanfields_still_needed--;
13868 Feld[x][y] = EL_SOKOBAN_OBJECT;
13870 if (Back[x][y] == Back[nextx][nexty])
13871 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13872 else if (Back[x][y] != 0)
13873 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13876 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13879 if (local_player->sokobanfields_still_needed == 0 &&
13880 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13882 PlayerWins(player);
13884 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13888 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13890 InitMovingField(x, y, move_direction);
13891 GfxAction[x][y] = ACTION_PUSHING;
13893 if (mode == DF_SNAP)
13894 ContinueMoving(x, y);
13896 MovPos[x][y] = (dx != 0 ? dx : dy);
13898 Pushed[x][y] = TRUE;
13899 Pushed[nextx][nexty] = TRUE;
13901 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13902 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13904 player->push_delay_value = -1; /* get new value later */
13906 /* check for element change _after_ element has been pushed */
13907 if (game.use_change_when_pushing_bug)
13909 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13910 player->index_bit, dig_side);
13911 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13912 player->index_bit, dig_side);
13915 else if (IS_SWITCHABLE(element))
13917 if (PLAYER_SWITCHING(player, x, y))
13919 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13920 player->index_bit, dig_side);
13925 player->is_switching = TRUE;
13926 player->switch_x = x;
13927 player->switch_y = y;
13929 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13931 if (element == EL_ROBOT_WHEEL)
13933 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13937 game.robot_wheel_active = TRUE;
13939 TEST_DrawLevelField(x, y);
13941 else if (element == EL_SP_TERMINAL)
13945 SCAN_PLAYFIELD(xx, yy)
13947 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13951 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13953 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13955 ResetGfxAnimation(xx, yy);
13956 TEST_DrawLevelField(xx, yy);
13960 else if (IS_BELT_SWITCH(element))
13962 ToggleBeltSwitch(x, y);
13964 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13965 element == EL_SWITCHGATE_SWITCH_DOWN ||
13966 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13967 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13969 ToggleSwitchgateSwitch(x, y);
13971 else if (element == EL_LIGHT_SWITCH ||
13972 element == EL_LIGHT_SWITCH_ACTIVE)
13974 ToggleLightSwitch(x, y);
13976 else if (element == EL_TIMEGATE_SWITCH ||
13977 element == EL_DC_TIMEGATE_SWITCH)
13979 ActivateTimegateSwitch(x, y);
13981 else if (element == EL_BALLOON_SWITCH_LEFT ||
13982 element == EL_BALLOON_SWITCH_RIGHT ||
13983 element == EL_BALLOON_SWITCH_UP ||
13984 element == EL_BALLOON_SWITCH_DOWN ||
13985 element == EL_BALLOON_SWITCH_NONE ||
13986 element == EL_BALLOON_SWITCH_ANY)
13988 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13989 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13990 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13991 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13992 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13995 else if (element == EL_LAMP)
13997 Feld[x][y] = EL_LAMP_ACTIVE;
13998 local_player->lights_still_needed--;
14000 ResetGfxAnimation(x, y);
14001 TEST_DrawLevelField(x, y);
14003 else if (element == EL_TIME_ORB_FULL)
14005 Feld[x][y] = EL_TIME_ORB_EMPTY;
14007 if (level.time > 0 || level.use_time_orb_bug)
14009 TimeLeft += level.time_orb_time;
14010 game.no_time_limit = FALSE;
14012 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14014 DisplayGameControlValues();
14017 ResetGfxAnimation(x, y);
14018 TEST_DrawLevelField(x, y);
14020 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14021 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14025 game.ball_state = !game.ball_state;
14027 SCAN_PLAYFIELD(xx, yy)
14029 int e = Feld[xx][yy];
14031 if (game.ball_state)
14033 if (e == EL_EMC_MAGIC_BALL)
14034 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14035 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14036 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14040 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14041 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14042 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14043 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14048 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14049 player->index_bit, dig_side);
14051 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14052 player->index_bit, dig_side);
14054 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14055 player->index_bit, dig_side);
14061 if (!PLAYER_SWITCHING(player, x, y))
14063 player->is_switching = TRUE;
14064 player->switch_x = x;
14065 player->switch_y = y;
14067 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14068 player->index_bit, dig_side);
14069 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14070 player->index_bit, dig_side);
14072 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14073 player->index_bit, dig_side);
14074 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14075 player->index_bit, dig_side);
14078 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14079 player->index_bit, dig_side);
14080 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14081 player->index_bit, dig_side);
14083 return MP_NO_ACTION;
14086 player->push_delay = -1;
14088 if (is_player) /* function can also be called by EL_PENGUIN */
14090 if (Feld[x][y] != element) /* really digged/collected something */
14092 player->is_collecting = !player->is_digging;
14093 player->is_active = TRUE;
14100 static boolean DigFieldByCE(int x, int y, int digging_element)
14102 int element = Feld[x][y];
14104 if (!IS_FREE(x, y))
14106 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14107 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14110 /* no element can dig solid indestructible elements */
14111 if (IS_INDESTRUCTIBLE(element) &&
14112 !IS_DIGGABLE(element) &&
14113 !IS_COLLECTIBLE(element))
14116 if (AmoebaNr[x][y] &&
14117 (element == EL_AMOEBA_FULL ||
14118 element == EL_BD_AMOEBA ||
14119 element == EL_AMOEBA_GROWING))
14121 AmoebaCnt[AmoebaNr[x][y]]--;
14122 AmoebaCnt2[AmoebaNr[x][y]]--;
14125 if (IS_MOVING(x, y))
14126 RemoveMovingField(x, y);
14130 TEST_DrawLevelField(x, y);
14133 /* if digged element was about to explode, prevent the explosion */
14134 ExplodeField[x][y] = EX_TYPE_NONE;
14136 PlayLevelSoundAction(x, y, action);
14139 Store[x][y] = EL_EMPTY;
14141 /* this makes it possible to leave the removed element again */
14142 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14143 Store[x][y] = element;
14148 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14150 int jx = player->jx, jy = player->jy;
14151 int x = jx + dx, y = jy + dy;
14152 int snap_direction = (dx == -1 ? MV_LEFT :
14153 dx == +1 ? MV_RIGHT :
14155 dy == +1 ? MV_DOWN : MV_NONE);
14156 boolean can_continue_snapping = (level.continuous_snapping &&
14157 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14159 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14162 if (!player->active || !IN_LEV_FIELD(x, y))
14170 if (player->MovPos == 0)
14171 player->is_pushing = FALSE;
14173 player->is_snapping = FALSE;
14175 if (player->MovPos == 0)
14177 player->is_moving = FALSE;
14178 player->is_digging = FALSE;
14179 player->is_collecting = FALSE;
14185 /* prevent snapping with already pressed snap key when not allowed */
14186 if (player->is_snapping && !can_continue_snapping)
14189 player->MovDir = snap_direction;
14191 if (player->MovPos == 0)
14193 player->is_moving = FALSE;
14194 player->is_digging = FALSE;
14195 player->is_collecting = FALSE;
14198 player->is_dropping = FALSE;
14199 player->is_dropping_pressed = FALSE;
14200 player->drop_pressed_delay = 0;
14202 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14205 player->is_snapping = TRUE;
14206 player->is_active = TRUE;
14208 if (player->MovPos == 0)
14210 player->is_moving = FALSE;
14211 player->is_digging = FALSE;
14212 player->is_collecting = FALSE;
14215 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14216 TEST_DrawLevelField(player->last_jx, player->last_jy);
14218 TEST_DrawLevelField(x, y);
14223 static boolean DropElement(struct PlayerInfo *player)
14225 int old_element, new_element;
14226 int dropx = player->jx, dropy = player->jy;
14227 int drop_direction = player->MovDir;
14228 int drop_side = drop_direction;
14229 int drop_element = get_next_dropped_element(player);
14231 /* do not drop an element on top of another element; when holding drop key
14232 pressed without moving, dropped element must move away before the next
14233 element can be dropped (this is especially important if the next element
14234 is dynamite, which can be placed on background for historical reasons) */
14235 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14238 if (IS_THROWABLE(drop_element))
14240 dropx += GET_DX_FROM_DIR(drop_direction);
14241 dropy += GET_DY_FROM_DIR(drop_direction);
14243 if (!IN_LEV_FIELD(dropx, dropy))
14247 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14248 new_element = drop_element; /* default: no change when dropping */
14250 /* check if player is active, not moving and ready to drop */
14251 if (!player->active || player->MovPos || player->drop_delay > 0)
14254 /* check if player has anything that can be dropped */
14255 if (new_element == EL_UNDEFINED)
14258 /* only set if player has anything that can be dropped */
14259 player->is_dropping_pressed = TRUE;
14261 /* check if drop key was pressed long enough for EM style dynamite */
14262 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14265 /* check if anything can be dropped at the current position */
14266 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14269 /* collected custom elements can only be dropped on empty fields */
14270 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14273 if (old_element != EL_EMPTY)
14274 Back[dropx][dropy] = old_element; /* store old element on this field */
14276 ResetGfxAnimation(dropx, dropy);
14277 ResetRandomAnimationValue(dropx, dropy);
14279 if (player->inventory_size > 0 ||
14280 player->inventory_infinite_element != EL_UNDEFINED)
14282 if (player->inventory_size > 0)
14284 player->inventory_size--;
14286 DrawGameDoorValues();
14288 if (new_element == EL_DYNAMITE)
14289 new_element = EL_DYNAMITE_ACTIVE;
14290 else if (new_element == EL_EM_DYNAMITE)
14291 new_element = EL_EM_DYNAMITE_ACTIVE;
14292 else if (new_element == EL_SP_DISK_RED)
14293 new_element = EL_SP_DISK_RED_ACTIVE;
14296 Feld[dropx][dropy] = new_element;
14298 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14299 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14300 el2img(Feld[dropx][dropy]), 0);
14302 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14304 /* needed if previous element just changed to "empty" in the last frame */
14305 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14307 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14308 player->index_bit, drop_side);
14309 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14311 player->index_bit, drop_side);
14313 TestIfElementTouchesCustomElement(dropx, dropy);
14315 else /* player is dropping a dyna bomb */
14317 player->dynabombs_left--;
14319 Feld[dropx][dropy] = new_element;
14321 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14322 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14323 el2img(Feld[dropx][dropy]), 0);
14325 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14328 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14329 InitField_WithBug1(dropx, dropy, FALSE);
14331 new_element = Feld[dropx][dropy]; /* element might have changed */
14333 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14334 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14336 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14337 MovDir[dropx][dropy] = drop_direction;
14339 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14341 /* do not cause impact style collision by dropping elements that can fall */
14342 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14345 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14346 player->is_dropping = TRUE;
14348 player->drop_pressed_delay = 0;
14349 player->is_dropping_pressed = FALSE;
14351 player->drop_x = dropx;
14352 player->drop_y = dropy;
14357 /* ------------------------------------------------------------------------- */
14358 /* game sound playing functions */
14359 /* ------------------------------------------------------------------------- */
14361 static int *loop_sound_frame = NULL;
14362 static int *loop_sound_volume = NULL;
14364 void InitPlayLevelSound()
14366 int num_sounds = getSoundListSize();
14368 checked_free(loop_sound_frame);
14369 checked_free(loop_sound_volume);
14371 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14372 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14375 static void PlayLevelSound(int x, int y, int nr)
14377 int sx = SCREENX(x), sy = SCREENY(y);
14378 int volume, stereo_position;
14379 int max_distance = 8;
14380 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14382 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14383 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14386 if (!IN_LEV_FIELD(x, y) ||
14387 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14388 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14391 volume = SOUND_MAX_VOLUME;
14393 if (!IN_SCR_FIELD(sx, sy))
14395 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14396 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14398 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14401 stereo_position = (SOUND_MAX_LEFT +
14402 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14403 (SCR_FIELDX + 2 * max_distance));
14405 if (IS_LOOP_SOUND(nr))
14407 /* This assures that quieter loop sounds do not overwrite louder ones,
14408 while restarting sound volume comparison with each new game frame. */
14410 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14413 loop_sound_volume[nr] = volume;
14414 loop_sound_frame[nr] = FrameCounter;
14417 PlaySoundExt(nr, volume, stereo_position, type);
14420 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14422 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14423 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14424 y < LEVELY(BY1) ? LEVELY(BY1) :
14425 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14429 static void PlayLevelSoundAction(int x, int y, int action)
14431 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14434 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14436 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14438 if (sound_effect != SND_UNDEFINED)
14439 PlayLevelSound(x, y, sound_effect);
14442 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14445 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14447 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14448 PlayLevelSound(x, y, sound_effect);
14451 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14453 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14455 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14456 PlayLevelSound(x, y, sound_effect);
14459 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14461 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14463 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14464 StopSound(sound_effect);
14467 static int getLevelMusicNr()
14469 if (levelset.music[level_nr] != MUS_UNDEFINED)
14470 return levelset.music[level_nr]; /* from config file */
14472 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14475 static void FadeLevelSounds()
14480 static void FadeLevelMusic()
14482 int music_nr = getLevelMusicNr();
14483 char *curr_music = getCurrentlyPlayingMusicFilename();
14484 char *next_music = getMusicInfoEntryFilename(music_nr);
14486 if (!strEqual(curr_music, next_music))
14490 void FadeLevelSoundsAndMusic()
14496 static void PlayLevelMusic()
14498 int music_nr = getLevelMusicNr();
14499 char *curr_music = getCurrentlyPlayingMusicFilename();
14500 char *next_music = getMusicInfoEntryFilename(music_nr);
14502 if (!strEqual(curr_music, next_music))
14503 PlayMusic(music_nr);
14506 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14508 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14509 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14510 int x = xx - 1 - offset;
14511 int y = yy - 1 - offset;
14516 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14520 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14524 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14528 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14532 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14536 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14540 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14543 case SAMPLE_android_clone:
14544 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14547 case SAMPLE_android_move:
14548 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14551 case SAMPLE_spring:
14552 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14556 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14560 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14563 case SAMPLE_eater_eat:
14564 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14568 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14571 case SAMPLE_collect:
14572 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14575 case SAMPLE_diamond:
14576 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14579 case SAMPLE_squash:
14580 /* !!! CHECK THIS !!! */
14582 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14584 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14588 case SAMPLE_wonderfall:
14589 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14593 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14597 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14601 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14605 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14609 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14613 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14616 case SAMPLE_wonder:
14617 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14621 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14624 case SAMPLE_exit_open:
14625 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14628 case SAMPLE_exit_leave:
14629 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14632 case SAMPLE_dynamite:
14633 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14637 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14641 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14645 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14649 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14653 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14657 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14661 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14666 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14668 int element = map_element_SP_to_RND(element_sp);
14669 int action = map_action_SP_to_RND(action_sp);
14670 int offset = (setup.sp_show_border_elements ? 0 : 1);
14671 int x = xx - offset;
14672 int y = yy - offset;
14674 PlayLevelSoundElementAction(x, y, element, action);
14677 void RaiseScore(int value)
14679 local_player->score += value;
14681 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14683 DisplayGameControlValues();
14686 void RaiseScoreElement(int element)
14691 case EL_BD_DIAMOND:
14692 case EL_EMERALD_YELLOW:
14693 case EL_EMERALD_RED:
14694 case EL_EMERALD_PURPLE:
14695 case EL_SP_INFOTRON:
14696 RaiseScore(level.score[SC_EMERALD]);
14699 RaiseScore(level.score[SC_DIAMOND]);
14702 RaiseScore(level.score[SC_CRYSTAL]);
14705 RaiseScore(level.score[SC_PEARL]);
14708 case EL_BD_BUTTERFLY:
14709 case EL_SP_ELECTRON:
14710 RaiseScore(level.score[SC_BUG]);
14713 case EL_BD_FIREFLY:
14714 case EL_SP_SNIKSNAK:
14715 RaiseScore(level.score[SC_SPACESHIP]);
14718 case EL_DARK_YAMYAM:
14719 RaiseScore(level.score[SC_YAMYAM]);
14722 RaiseScore(level.score[SC_ROBOT]);
14725 RaiseScore(level.score[SC_PACMAN]);
14728 RaiseScore(level.score[SC_NUT]);
14731 case EL_EM_DYNAMITE:
14732 case EL_SP_DISK_RED:
14733 case EL_DYNABOMB_INCREASE_NUMBER:
14734 case EL_DYNABOMB_INCREASE_SIZE:
14735 case EL_DYNABOMB_INCREASE_POWER:
14736 RaiseScore(level.score[SC_DYNAMITE]);
14738 case EL_SHIELD_NORMAL:
14739 case EL_SHIELD_DEADLY:
14740 RaiseScore(level.score[SC_SHIELD]);
14742 case EL_EXTRA_TIME:
14743 RaiseScore(level.extra_time_score);
14757 case EL_DC_KEY_WHITE:
14758 RaiseScore(level.score[SC_KEY]);
14761 RaiseScore(element_info[element].collect_score);
14766 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14768 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14770 /* closing door required in case of envelope style request dialogs */
14772 CloseDoor(DOOR_CLOSE_1);
14774 #if defined(NETWORK_AVALIABLE)
14775 if (options.network)
14776 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14781 FadeSkipNextFadeIn();
14783 SetGameStatus(GAME_MODE_MAIN);
14788 else /* continue playing the game */
14790 if (tape.playing && tape.deactivate_display)
14791 TapeDeactivateDisplayOff(TRUE);
14793 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14795 if (tape.playing && tape.deactivate_display)
14796 TapeDeactivateDisplayOn();
14800 void RequestQuitGame(boolean ask_if_really_quit)
14802 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14803 boolean skip_request = AllPlayersGone || quick_quit;
14805 RequestQuitGameExt(skip_request, quick_quit,
14806 "Do you really want to quit the game?");
14810 /* ------------------------------------------------------------------------- */
14811 /* random generator functions */
14812 /* ------------------------------------------------------------------------- */
14814 unsigned int InitEngineRandom_RND(int seed)
14816 game.num_random_calls = 0;
14818 return InitEngineRandom(seed);
14821 unsigned int RND(int max)
14825 game.num_random_calls++;
14827 return GetEngineRandom(max);
14834 /* ------------------------------------------------------------------------- */
14835 /* game engine snapshot handling functions */
14836 /* ------------------------------------------------------------------------- */
14838 struct EngineSnapshotInfo
14840 /* runtime values for custom element collect score */
14841 int collect_score[NUM_CUSTOM_ELEMENTS];
14843 /* runtime values for group element choice position */
14844 int choice_pos[NUM_GROUP_ELEMENTS];
14846 /* runtime values for belt position animations */
14847 int belt_graphic[4][NUM_BELT_PARTS];
14848 int belt_anim_mode[4][NUM_BELT_PARTS];
14851 static struct EngineSnapshotInfo engine_snapshot_rnd;
14852 static char *snapshot_level_identifier = NULL;
14853 static int snapshot_level_nr = -1;
14855 static void SaveEngineSnapshotValues_RND()
14857 static int belt_base_active_element[4] =
14859 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14860 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14861 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14862 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14866 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14868 int element = EL_CUSTOM_START + i;
14870 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14873 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14875 int element = EL_GROUP_START + i;
14877 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14880 for (i = 0; i < 4; i++)
14882 for (j = 0; j < NUM_BELT_PARTS; j++)
14884 int element = belt_base_active_element[i] + j;
14885 int graphic = el2img(element);
14886 int anim_mode = graphic_info[graphic].anim_mode;
14888 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14889 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14894 static void LoadEngineSnapshotValues_RND()
14896 unsigned int num_random_calls = game.num_random_calls;
14899 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14901 int element = EL_CUSTOM_START + i;
14903 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14906 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14908 int element = EL_GROUP_START + i;
14910 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14913 for (i = 0; i < 4; i++)
14915 for (j = 0; j < NUM_BELT_PARTS; j++)
14917 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14918 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14920 graphic_info[graphic].anim_mode = anim_mode;
14924 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14926 InitRND(tape.random_seed);
14927 for (i = 0; i < num_random_calls; i++)
14931 if (game.num_random_calls != num_random_calls)
14933 Error(ERR_INFO, "number of random calls out of sync");
14934 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14935 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14936 Error(ERR_EXIT, "this should not happen -- please debug");
14940 void FreeEngineSnapshotSingle()
14942 FreeSnapshotSingle();
14944 setString(&snapshot_level_identifier, NULL);
14945 snapshot_level_nr = -1;
14948 void FreeEngineSnapshotList()
14950 FreeSnapshotList();
14953 ListNode *SaveEngineSnapshotBuffers()
14955 ListNode *buffers = NULL;
14957 /* copy some special values to a structure better suited for the snapshot */
14959 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14960 SaveEngineSnapshotValues_RND();
14961 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14962 SaveEngineSnapshotValues_EM();
14963 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14964 SaveEngineSnapshotValues_SP(&buffers);
14965 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
14966 SaveEngineSnapshotValues_MM(&buffers);
14968 /* save values stored in special snapshot structure */
14970 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14971 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14972 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14973 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14974 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14975 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14976 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
14977 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
14979 /* save further RND engine values */
14981 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14982 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14983 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14985 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14986 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14987 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14988 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14990 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14991 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14992 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14993 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14994 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14996 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14997 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14998 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15000 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15002 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15004 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15005 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15007 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15008 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15009 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15010 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15011 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15012 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15013 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15014 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15015 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15016 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15017 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15018 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15019 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15020 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15021 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15022 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15023 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15024 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15026 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15027 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15029 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15030 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15031 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15033 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15034 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15036 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15037 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15038 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15039 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15040 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15042 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15043 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15046 ListNode *node = engine_snapshot_list_rnd;
15049 while (node != NULL)
15051 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15056 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15062 void SaveEngineSnapshotSingle()
15064 ListNode *buffers = SaveEngineSnapshotBuffers();
15066 /* finally save all snapshot buffers to single snapshot */
15067 SaveSnapshotSingle(buffers);
15069 /* save level identification information */
15070 setString(&snapshot_level_identifier, leveldir_current->identifier);
15071 snapshot_level_nr = level_nr;
15074 boolean CheckSaveEngineSnapshotToList()
15076 boolean save_snapshot =
15077 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15078 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15079 game.snapshot.changed_action) ||
15080 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15081 game.snapshot.collected_item));
15083 game.snapshot.changed_action = FALSE;
15084 game.snapshot.collected_item = FALSE;
15085 game.snapshot.save_snapshot = save_snapshot;
15087 return save_snapshot;
15090 void SaveEngineSnapshotToList()
15092 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15096 ListNode *buffers = SaveEngineSnapshotBuffers();
15098 /* finally save all snapshot buffers to snapshot list */
15099 SaveSnapshotToList(buffers);
15102 void SaveEngineSnapshotToListInitial()
15104 FreeEngineSnapshotList();
15106 SaveEngineSnapshotToList();
15109 void LoadEngineSnapshotValues()
15111 /* restore special values from snapshot structure */
15113 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15114 LoadEngineSnapshotValues_RND();
15115 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15116 LoadEngineSnapshotValues_EM();
15117 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15118 LoadEngineSnapshotValues_SP();
15119 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15120 LoadEngineSnapshotValues_MM();
15123 void LoadEngineSnapshotSingle()
15125 LoadSnapshotSingle();
15127 LoadEngineSnapshotValues();
15130 void LoadEngineSnapshot_Undo(int steps)
15132 LoadSnapshotFromList_Older(steps);
15134 LoadEngineSnapshotValues();
15137 void LoadEngineSnapshot_Redo(int steps)
15139 LoadSnapshotFromList_Newer(steps);
15141 LoadEngineSnapshotValues();
15144 boolean CheckEngineSnapshotSingle()
15146 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15147 snapshot_level_nr == level_nr);
15150 boolean CheckEngineSnapshotList()
15152 return CheckSnapshotList();
15156 /* ---------- new game button stuff ---------------------------------------- */
15164 } gamebutton_info[NUM_GAME_BUTTONS] =
15167 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15168 GAME_CTRL_ID_STOP, "stop game"
15171 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15172 GAME_CTRL_ID_PAUSE, "pause game"
15175 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15176 GAME_CTRL_ID_PLAY, "play game"
15179 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15180 GAME_CTRL_ID_UNDO, "undo step"
15183 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15184 GAME_CTRL_ID_REDO, "redo step"
15187 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15188 GAME_CTRL_ID_SAVE, "save game"
15191 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15192 GAME_CTRL_ID_PAUSE2, "pause game"
15195 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15196 GAME_CTRL_ID_LOAD, "load game"
15199 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15200 SOUND_CTRL_ID_MUSIC, "background music on/off"
15203 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15204 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
15207 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15208 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
15212 void CreateGameButtons()
15216 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15218 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15219 struct XY *pos = gamebutton_info[i].pos;
15220 struct GadgetInfo *gi;
15223 unsigned int event_mask;
15224 int base_x = (tape.show_game_buttons ? VX : DX);
15225 int base_y = (tape.show_game_buttons ? VY : DY);
15226 int gd_x = gfx->src_x;
15227 int gd_y = gfx->src_y;
15228 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15229 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15230 int gd_xa = gfx->src_x + gfx->active_xoffset;
15231 int gd_ya = gfx->src_y + gfx->active_yoffset;
15232 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15233 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15236 if (gfx->bitmap == NULL)
15238 game_gadget[id] = NULL;
15243 if (id == GAME_CTRL_ID_STOP ||
15244 id == GAME_CTRL_ID_PLAY ||
15245 id == GAME_CTRL_ID_SAVE ||
15246 id == GAME_CTRL_ID_LOAD)
15248 button_type = GD_TYPE_NORMAL_BUTTON;
15250 event_mask = GD_EVENT_RELEASED;
15252 else if (id == GAME_CTRL_ID_UNDO ||
15253 id == GAME_CTRL_ID_REDO)
15255 button_type = GD_TYPE_NORMAL_BUTTON;
15257 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15261 button_type = GD_TYPE_CHECK_BUTTON;
15263 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15264 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15265 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15266 event_mask = GD_EVENT_PRESSED;
15269 gi = CreateGadget(GDI_CUSTOM_ID, id,
15270 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15271 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15272 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15273 GDI_WIDTH, gfx->width,
15274 GDI_HEIGHT, gfx->height,
15275 GDI_TYPE, button_type,
15276 GDI_STATE, GD_BUTTON_UNPRESSED,
15277 GDI_CHECKED, checked,
15278 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15279 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15280 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15281 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15282 GDI_DIRECT_DRAW, FALSE,
15283 GDI_EVENT_MASK, event_mask,
15284 GDI_CALLBACK_ACTION, HandleGameButtons,
15288 Error(ERR_EXIT, "cannot create gadget");
15290 game_gadget[id] = gi;
15294 void FreeGameButtons()
15298 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15299 FreeGadget(game_gadget[i]);
15302 static void UnmapGameButtonsAtSamePosition(int id)
15306 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15308 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15309 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15310 UnmapGadget(game_gadget[i]);
15313 static void UnmapGameButtonsAtSamePosition_All()
15315 if (setup.show_snapshot_buttons)
15317 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15318 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15319 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15323 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15324 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15325 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15329 static void MapGameButtonsAtSamePosition(int id)
15333 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15335 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15336 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15337 MapGadget(game_gadget[i]);
15339 UnmapGameButtonsAtSamePosition_All();
15342 void MapUndoRedoButtons()
15344 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15345 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15347 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15348 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15350 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15353 void UnmapUndoRedoButtons()
15355 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15356 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15358 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15359 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15361 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15364 void MapGameButtons()
15368 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15369 if (i != GAME_CTRL_ID_UNDO &&
15370 i != GAME_CTRL_ID_REDO)
15371 MapGadget(game_gadget[i]);
15373 UnmapGameButtonsAtSamePosition_All();
15375 RedrawGameButtons();
15378 void UnmapGameButtons()
15382 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15383 UnmapGadget(game_gadget[i]);
15386 void RedrawGameButtons()
15390 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15391 RedrawGadget(game_gadget[i]);
15393 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15394 redraw_mask &= ~REDRAW_ALL;
15397 void GameUndoRedoExt()
15399 ClearPlayerAction();
15401 tape.pausing = TRUE;
15404 UpdateAndDisplayGameControlValues();
15406 DrawCompleteVideoDisplay();
15407 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15408 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15409 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15414 void GameUndo(int steps)
15416 if (!CheckEngineSnapshotList())
15419 LoadEngineSnapshot_Undo(steps);
15424 void GameRedo(int steps)
15426 if (!CheckEngineSnapshotList())
15429 LoadEngineSnapshot_Redo(steps);
15434 static void HandleGameButtonsExt(int id, int button)
15436 static boolean game_undo_executed = FALSE;
15437 int steps = BUTTON_STEPSIZE(button);
15438 boolean handle_game_buttons =
15439 (game_status == GAME_MODE_PLAYING ||
15440 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15442 if (!handle_game_buttons)
15447 case GAME_CTRL_ID_STOP:
15448 if (game_status == GAME_MODE_MAIN)
15454 RequestQuitGame(TRUE);
15458 case GAME_CTRL_ID_PAUSE:
15459 case GAME_CTRL_ID_PAUSE2:
15460 if (options.network && game_status == GAME_MODE_PLAYING)
15462 #if defined(NETWORK_AVALIABLE)
15464 SendToServer_ContinuePlaying();
15466 SendToServer_PausePlaying();
15470 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15472 game_undo_executed = FALSE;
15476 case GAME_CTRL_ID_PLAY:
15477 if (game_status == GAME_MODE_MAIN)
15479 StartGameActions(options.network, setup.autorecord, level.random_seed);
15481 else if (tape.pausing)
15483 #if defined(NETWORK_AVALIABLE)
15484 if (options.network)
15485 SendToServer_ContinuePlaying();
15488 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15492 case GAME_CTRL_ID_UNDO:
15493 // Important: When using "save snapshot when collecting an item" mode,
15494 // load last (current) snapshot for first "undo" after pressing "pause"
15495 // (else the last-but-one snapshot would be loaded, because the snapshot
15496 // pointer already points to the last snapshot when pressing "pause",
15497 // which is fine for "every step/move" mode, but not for "every collect")
15498 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15499 !game_undo_executed)
15502 game_undo_executed = TRUE;
15507 case GAME_CTRL_ID_REDO:
15511 case GAME_CTRL_ID_SAVE:
15515 case GAME_CTRL_ID_LOAD:
15519 case SOUND_CTRL_ID_MUSIC:
15520 if (setup.sound_music)
15522 setup.sound_music = FALSE;
15526 else if (audio.music_available)
15528 setup.sound = setup.sound_music = TRUE;
15530 SetAudioMode(setup.sound);
15536 case SOUND_CTRL_ID_LOOPS:
15537 if (setup.sound_loops)
15538 setup.sound_loops = FALSE;
15539 else if (audio.loops_available)
15541 setup.sound = setup.sound_loops = TRUE;
15543 SetAudioMode(setup.sound);
15547 case SOUND_CTRL_ID_SIMPLE:
15548 if (setup.sound_simple)
15549 setup.sound_simple = FALSE;
15550 else if (audio.sound_available)
15552 setup.sound = setup.sound_simple = TRUE;
15554 SetAudioMode(setup.sound);
15563 static void HandleGameButtons(struct GadgetInfo *gi)
15565 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15568 void HandleSoundButtonKeys(Key key)
15571 if (key == setup.shortcut.sound_simple)
15572 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15573 else if (key == setup.shortcut.sound_loops)
15574 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15575 else if (key == setup.shortcut.sound_music)
15576 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);