1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL (DX + XX_LEVEL)
67 #define DY_LEVEL (DY + YY_LEVEL)
68 #define DX_EMERALDS (DX + XX_EMERALDS)
69 #define DY_EMERALDS (DY + YY_EMERALDS)
70 #define DX_DYNAMITE (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE (DY + YY_DYNAMITE)
72 #define DX_KEYS (DX + XX_KEYS)
73 #define DY_KEYS (DY + YY_KEYS)
74 #define DX_SCORE (DX + XX_SCORE)
75 #define DY_SCORE (DY + YY_SCORE)
76 #define DX_TIME (DX + XX_TIME)
77 #define DY_TIME (DY + YY_TIME)
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF -1
81 #define INITIAL_MOVE_DELAY_ON 0
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED 4
87 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
104 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
105 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
107 (DONT_COLLIDE_WITH(e) && \
108 IS_FREE_OR_PLAYER(x, y))))
110 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
111 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
114 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
115 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
117 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
118 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
120 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
121 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
123 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
125 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
126 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
127 Feld[x][y] == EL_DIAMOND))
129 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
130 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
131 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
133 #define PACMAN_CAN_ENTER_FIELD(x, y) \
134 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
135 IS_AMOEBOID(Feld[x][y])))
137 #define PIG_CAN_ENTER_FIELD(x, y) \
138 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 IS_FOOD_PIG(Feld[x][y])))
141 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
142 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
143 IS_FOOD_PENGUIN(Feld[x][y]) || \
144 Feld[x][y] == EL_EXIT_OPEN || \
145 Feld[x][y] == EL_ACID))
148 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
149 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
151 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
153 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
156 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
157 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
159 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
160 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
162 /* game button identifiers */
163 #define GAME_CTRL_ID_STOP 0
164 #define GAME_CTRL_ID_PAUSE 1
165 #define GAME_CTRL_ID_PLAY 2
166 #define SOUND_CTRL_ID_MUSIC 3
167 #define SOUND_CTRL_ID_LOOPS 4
168 #define SOUND_CTRL_ID_SIMPLE 5
170 #define NUM_GAME_BUTTONS 6
173 /* forward declaration for internal use */
175 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
176 static boolean MovePlayer(struct PlayerInfo *, int, int);
177 static void ScrollPlayer(struct PlayerInfo *, int);
178 static void ScrollScreen(struct PlayerInfo *, int);
180 static void InitBeltMovement(void);
181 static void CloseAllOpenTimegates(void);
182 static void CheckGravityMovement(struct PlayerInfo *);
183 static void KillHeroUnlessProtected(int, int);
185 static void TestIfPlayerTouchesCustomElement(int, int);
186 static void TestIfElementTouchesCustomElement(int, int);
188 static void ChangeElement(int, int, int);
189 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
190 static boolean CheckTriggeredElementChange(int, int, int, int);
191 static boolean CheckElementSideChange(int, int, int, int, int, int);
192 static boolean CheckElementChange(int, int, int, int);
194 static void PlayLevelSound(int, int, int);
195 static void PlayLevelSoundNearest(int, int, int);
196 static void PlayLevelSoundAction(int, int, int);
197 static void PlayLevelSoundElementAction(int, int, int, int);
198 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
199 static void PlayLevelSoundActionIfLoop(int, int, int);
200 static void StopLevelSoundActionIfLoop(int, int, int);
201 static void PlayLevelMusic();
203 static void MapGameButtons();
204 static void HandleGameButtons(struct GadgetInfo *);
206 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
209 /* ------------------------------------------------------------------------- */
210 /* definition of elements that automatically change to other elements after */
211 /* a specified time, eventually calling a function when changing */
212 /* ------------------------------------------------------------------------- */
214 /* forward declaration for changer functions */
215 static void InitBuggyBase(int x, int y);
216 static void WarnBuggyBase(int x, int y);
218 static void InitTrap(int x, int y);
219 static void ActivateTrap(int x, int y);
220 static void ChangeActiveTrap(int x, int y);
222 static void InitRobotWheel(int x, int y);
223 static void RunRobotWheel(int x, int y);
224 static void StopRobotWheel(int x, int y);
226 static void InitTimegateWheel(int x, int y);
227 static void RunTimegateWheel(int x, int y);
229 struct ChangingElementInfo
234 void (*pre_change_function)(int x, int y);
235 void (*change_function)(int x, int y);
236 void (*post_change_function)(int x, int y);
239 static struct ChangingElementInfo change_delay_list[] =
290 EL_SWITCHGATE_OPENING,
298 EL_SWITCHGATE_CLOSING,
299 EL_SWITCHGATE_CLOSED,
331 EL_ACID_SPLASH_RIGHT,
340 EL_SP_BUGGY_BASE_ACTIVATING,
347 EL_SP_BUGGY_BASE_ACTIVATING,
348 EL_SP_BUGGY_BASE_ACTIVE,
355 EL_SP_BUGGY_BASE_ACTIVE,
379 EL_ROBOT_WHEEL_ACTIVE,
387 EL_TIMEGATE_SWITCH_ACTIVE,
408 int push_delay_fixed, push_delay_random;
413 { EL_BALLOON, 0, 0 },
415 { EL_SOKOBAN_OBJECT, 2, 0 },
416 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
417 { EL_SATELLITE, 2, 0 },
418 { EL_SP_DISK_YELLOW, 2, 0 },
420 { EL_UNDEFINED, 0, 0 },
428 move_stepsize_list[] =
430 { EL_AMOEBA_DROP, 2 },
431 { EL_AMOEBA_DROPPING, 2 },
432 { EL_QUICKSAND_FILLING, 1 },
433 { EL_QUICKSAND_EMPTYING, 1 },
434 { EL_MAGIC_WALL_FILLING, 2 },
435 { EL_BD_MAGIC_WALL_FILLING, 2 },
436 { EL_MAGIC_WALL_EMPTYING, 2 },
437 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
447 collect_count_list[] =
450 { EL_BD_DIAMOND, 1 },
451 { EL_EMERALD_YELLOW, 1 },
452 { EL_EMERALD_RED, 1 },
453 { EL_EMERALD_PURPLE, 1 },
455 { EL_SP_INFOTRON, 1 },
462 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
464 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
465 CH_EVENT_BIT(CE_DELAY))
466 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
467 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
468 IS_JUST_CHANGING(x, y))
470 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
473 void GetPlayerConfig()
475 if (!audio.sound_available)
476 setup.sound_simple = FALSE;
478 if (!audio.loops_available)
479 setup.sound_loops = FALSE;
481 if (!audio.music_available)
482 setup.sound_music = FALSE;
484 if (!video.fullscreen_available)
485 setup.fullscreen = FALSE;
487 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
489 SetAudioMode(setup.sound);
493 static int getBeltNrFromBeltElement(int element)
495 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
496 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
497 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
500 static int getBeltNrFromBeltActiveElement(int element)
502 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
503 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
504 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
507 static int getBeltNrFromBeltSwitchElement(int element)
509 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
510 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
511 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
514 static int getBeltDirNrFromBeltSwitchElement(int element)
516 static int belt_base_element[4] =
518 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
519 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
520 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
521 EL_CONVEYOR_BELT_4_SWITCH_LEFT
524 int belt_nr = getBeltNrFromBeltSwitchElement(element);
525 int belt_dir_nr = element - belt_base_element[belt_nr];
527 return (belt_dir_nr % 3);
530 static int getBeltDirFromBeltSwitchElement(int element)
532 static int belt_move_dir[3] =
539 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
541 return belt_move_dir[belt_dir_nr];
544 static void InitPlayerField(int x, int y, int element, boolean init_game)
546 if (element == EL_SP_MURPHY)
550 if (stored_player[0].present)
552 Feld[x][y] = EL_SP_MURPHY_CLONE;
558 stored_player[0].use_murphy_graphic = TRUE;
561 Feld[x][y] = EL_PLAYER_1;
567 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
568 int jx = player->jx, jy = player->jy;
570 player->present = TRUE;
572 if (!options.network || player->connected)
574 player->active = TRUE;
576 /* remove potentially duplicate players */
577 if (StorePlayer[jx][jy] == Feld[x][y])
578 StorePlayer[jx][jy] = 0;
580 StorePlayer[x][y] = Feld[x][y];
584 printf("Player %d activated.\n", player->element_nr);
585 printf("[Local player is %d and currently %s.]\n",
586 local_player->element_nr,
587 local_player->active ? "active" : "not active");
591 Feld[x][y] = EL_EMPTY;
592 player->jx = player->last_jx = x;
593 player->jy = player->last_jy = y;
597 static void InitField(int x, int y, boolean init_game)
599 int element = Feld[x][y];
608 InitPlayerField(x, y, element, init_game);
612 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
613 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
614 else if (x > 0 && Feld[x-1][y] == EL_ACID)
615 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
616 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
617 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
618 else if (y > 0 && Feld[x][y-1] == EL_ACID)
619 Feld[x][y] = EL_ACID_POOL_BOTTOM;
620 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
621 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
629 case EL_SPACESHIP_RIGHT:
630 case EL_SPACESHIP_UP:
631 case EL_SPACESHIP_LEFT:
632 case EL_SPACESHIP_DOWN:
634 case EL_BD_BUTTERFLY_RIGHT:
635 case EL_BD_BUTTERFLY_UP:
636 case EL_BD_BUTTERFLY_LEFT:
637 case EL_BD_BUTTERFLY_DOWN:
638 case EL_BD_BUTTERFLY:
639 case EL_BD_FIREFLY_RIGHT:
640 case EL_BD_FIREFLY_UP:
641 case EL_BD_FIREFLY_LEFT:
642 case EL_BD_FIREFLY_DOWN:
644 case EL_PACMAN_RIGHT:
668 if (y == lev_fieldy - 1)
670 Feld[x][y] = EL_AMOEBA_GROWING;
671 Store[x][y] = EL_AMOEBA_WET;
675 case EL_DYNAMITE_ACTIVE:
680 local_player->lights_still_needed++;
683 case EL_SOKOBAN_FIELD_EMPTY:
684 local_player->sokobanfields_still_needed++;
688 local_player->friends_still_needed++;
693 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
698 Feld[x][y] = EL_EMPTY;
703 case EL_EM_KEY_1_FILE:
704 Feld[x][y] = EL_EM_KEY_1;
706 case EL_EM_KEY_2_FILE:
707 Feld[x][y] = EL_EM_KEY_2;
709 case EL_EM_KEY_3_FILE:
710 Feld[x][y] = EL_EM_KEY_3;
712 case EL_EM_KEY_4_FILE:
713 Feld[x][y] = EL_EM_KEY_4;
717 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
718 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
719 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
720 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
721 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
722 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
723 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
724 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
725 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
726 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
727 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
728 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
731 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
732 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
733 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
735 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
737 game.belt_dir[belt_nr] = belt_dir;
738 game.belt_dir_nr[belt_nr] = belt_dir_nr;
740 else /* more than one switch -- set it like the first switch */
742 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
747 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
749 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
752 case EL_LIGHT_SWITCH_ACTIVE:
754 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
758 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
764 void DrawGameDoorValues()
768 for (i = 0; i < MAX_PLAYERS; i++)
769 for (j = 0; j < 4; j++)
770 if (stored_player[i].key[j])
771 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
772 el2edimg(EL_KEY_1 + j));
774 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
775 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
776 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
777 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
778 DrawText(DX + XX_SCORE, DY + YY_SCORE,
779 int2str(local_player->score, 5), FONT_TEXT_2);
780 DrawText(DX + XX_TIME, DY + YY_TIME,
781 int2str(TimeLeft, 3), FONT_TEXT_2);
786 =============================================================================
788 -----------------------------------------------------------------------------
789 initialize game engine due to level / tape version number
790 =============================================================================
793 static void InitGameEngine()
797 /* set game engine from tape file when re-playing, else from level file */
798 game.engine_version = (tape.playing ? tape.engine_version :
801 /* dynamically adjust element properties according to game engine version */
802 InitElementPropertiesEngine(game.engine_version);
805 printf("level %d: level version == %06d\n", level_nr, level.game_version);
806 printf(" tape version == %06d [%s] [file: %06d]\n",
807 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
809 printf(" => game.engine_version == %06d\n", game.engine_version);
812 /* ---------- initialize player's initial move delay --------------------- */
814 /* dynamically adjust player properties according to game engine version */
815 game.initial_move_delay =
816 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
817 INITIAL_MOVE_DELAY_OFF);
819 /* dynamically adjust player properties according to level information */
820 game.initial_move_delay_value =
821 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
823 /* ---------- initialize player's initial push delay --------------------- */
825 /* dynamically adjust player properties according to game engine version */
826 game.initial_push_delay_value =
827 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
829 /* ---------- initialize changing elements ------------------------------- */
831 /* initialize changing elements information */
832 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
834 struct ElementInfo *ei = &element_info[i];
836 /* this pointer might have been changed in the level editor */
837 ei->change = &ei->change_page[0];
839 if (!IS_CUSTOM_ELEMENT(i))
841 ei->change->target_element = EL_EMPTY_SPACE;
842 ei->change->delay_fixed = 0;
843 ei->change->delay_random = 0;
844 ei->change->delay_frames = 1;
847 ei->change_events = CE_BITMASK_DEFAULT;
848 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
850 ei->event_page_nr[j] = 0;
851 ei->event_page[j] = &ei->change_page[0];
855 /* add changing elements from pre-defined list */
856 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
858 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
859 struct ElementInfo *ei = &element_info[ch_delay->element];
861 ei->change->target_element = ch_delay->target_element;
862 ei->change->delay_fixed = ch_delay->change_delay;
864 ei->change->pre_change_function = ch_delay->pre_change_function;
865 ei->change->change_function = ch_delay->change_function;
866 ei->change->post_change_function = ch_delay->post_change_function;
868 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
872 /* add change events from custom element configuration */
873 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
875 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
877 for (j = 0; j < ei->num_change_pages; j++)
879 if (!ei->change_page[j].can_change)
882 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
884 /* only add event page for the first page found with this event */
885 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
886 !(ei->change_events & CH_EVENT_BIT(k)))
888 ei->change_events |= CH_EVENT_BIT(k);
889 ei->event_page_nr[k] = j;
890 ei->event_page[k] = &ei->change_page[j];
898 /* add change events from custom element configuration */
899 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
901 int element = EL_CUSTOM_START + i;
903 /* only add custom elements that change after fixed/random frame delay */
904 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
905 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
909 /* ---------- initialize trigger events ---------------------------------- */
911 /* initialize trigger events information */
912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
913 trigger_events[i] = EP_BITMASK_DEFAULT;
916 /* add trigger events from element change event properties */
917 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
919 struct ElementInfo *ei = &element_info[i];
921 for (j = 0; j < ei->num_change_pages; j++)
923 if (!ei->change_page[j].can_change)
926 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
928 int trigger_element = ei->change_page[j].trigger_element;
930 trigger_events[trigger_element] |= ei->change_page[j].events;
935 /* add trigger events from element change event properties */
936 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
937 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
938 trigger_events[element_info[i].change->trigger_element] |=
939 element_info[i].change->events;
942 /* ---------- initialize push delay -------------------------------------- */
944 /* initialize push delay values to default */
945 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
947 if (!IS_CUSTOM_ELEMENT(i))
949 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
950 element_info[i].push_delay_random = game.default_push_delay_random;
954 /* set push delay value for certain elements from pre-defined list */
955 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
957 int e = push_delay_list[i].element;
959 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
960 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
963 /* ---------- initialize move stepsize ----------------------------------- */
965 /* initialize move stepsize values to default */
966 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
967 if (!IS_CUSTOM_ELEMENT(i))
968 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
970 /* set move stepsize value for certain elements from pre-defined list */
971 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
973 int e = move_stepsize_list[i].element;
975 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
978 /* ---------- initialize gem count --------------------------------------- */
980 /* initialize gem count values for each element */
981 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
982 if (!IS_CUSTOM_ELEMENT(i))
983 element_info[i].collect_count = 0;
985 /* add gem count values for all elements from pre-defined list */
986 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
987 element_info[collect_count_list[i].element].collect_count =
988 collect_count_list[i].count;
993 =============================================================================
995 -----------------------------------------------------------------------------
996 initialize and start new game
997 =============================================================================
1002 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1003 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1004 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1011 #if USE_NEW_AMOEBA_CODE
1012 printf("Using new amoeba code.\n");
1014 printf("Using old amoeba code.\n");
1019 /* don't play tapes over network */
1020 network_playing = (options.network && !tape.playing);
1022 for (i = 0; i < MAX_PLAYERS; i++)
1024 struct PlayerInfo *player = &stored_player[i];
1026 player->index_nr = i;
1027 player->element_nr = EL_PLAYER_1 + i;
1029 player->present = FALSE;
1030 player->active = FALSE;
1033 player->effective_action = 0;
1034 player->programmed_action = 0;
1037 player->gems_still_needed = level.gems_needed;
1038 player->sokobanfields_still_needed = 0;
1039 player->lights_still_needed = 0;
1040 player->friends_still_needed = 0;
1042 for (j = 0; j < 4; j++)
1043 player->key[j] = FALSE;
1045 player->dynabomb_count = 0;
1046 player->dynabomb_size = 1;
1047 player->dynabombs_left = 0;
1048 player->dynabomb_xl = FALSE;
1050 player->MovDir = MV_NO_MOVING;
1053 player->GfxDir = MV_NO_MOVING;
1054 player->GfxAction = ACTION_DEFAULT;
1056 player->StepFrame = 0;
1058 player->use_murphy_graphic = FALSE;
1060 player->actual_frame_counter = 0;
1062 player->step_counter = 0;
1064 player->last_move_dir = MV_NO_MOVING;
1066 player->is_waiting = FALSE;
1067 player->is_moving = FALSE;
1068 player->is_digging = FALSE;
1069 player->is_snapping = FALSE;
1070 player->is_collecting = FALSE;
1071 player->is_pushing = FALSE;
1072 player->is_switching = FALSE;
1074 player->is_bored = FALSE;
1075 player->is_sleeping = FALSE;
1077 player->frame_counter_bored = -1;
1078 player->frame_counter_sleeping = -1;
1080 player->anim_delay_counter = 0;
1081 player->post_delay_counter = 0;
1083 player->action_waiting = ACTION_DEFAULT;
1084 player->last_action_waiting = ACTION_DEFAULT;
1085 player->special_action_bored = ACTION_DEFAULT;
1086 player->special_action_sleeping = ACTION_DEFAULT;
1088 player->num_special_action_bored = 0;
1089 player->num_special_action_sleeping = 0;
1091 /* determine number of special actions for bored and sleeping animation */
1092 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1094 boolean found = FALSE;
1096 for (k = 0; k < NUM_DIRECTIONS; k++)
1097 if (el_act_dir2img(player->element_nr, j, k) !=
1098 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1102 player->num_special_action_bored++;
1106 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1108 boolean found = FALSE;
1110 for (k = 0; k < NUM_DIRECTIONS; k++)
1111 if (el_act_dir2img(player->element_nr, j, k) !=
1112 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1116 player->num_special_action_sleeping++;
1121 player->switch_x = -1;
1122 player->switch_y = -1;
1124 player->show_envelope = 0;
1126 player->move_delay = game.initial_move_delay;
1127 player->move_delay_value = game.initial_move_delay_value;
1129 player->push_delay = 0;
1130 player->push_delay_value = game.initial_push_delay_value;
1132 player->drop_delay = 0;
1134 player->last_jx = player->last_jy = 0;
1135 player->jx = player->jy = 0;
1137 player->shield_normal_time_left = 0;
1138 player->shield_deadly_time_left = 0;
1140 player->inventory_size = 0;
1142 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1143 SnapField(player, 0, 0);
1145 player->LevelSolved = FALSE;
1146 player->GameOver = FALSE;
1149 network_player_action_received = FALSE;
1151 #if defined(PLATFORM_UNIX)
1152 /* initial null action */
1153 if (network_playing)
1154 SendToServer_MovePlayer(MV_NO_MOVING);
1162 TimeLeft = level.time;
1164 ScreenMovDir = MV_NO_MOVING;
1168 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1170 AllPlayersGone = FALSE;
1172 game.yamyam_content_nr = 0;
1173 game.magic_wall_active = FALSE;
1174 game.magic_wall_time_left = 0;
1175 game.light_time_left = 0;
1176 game.timegate_time_left = 0;
1177 game.switchgate_pos = 0;
1178 game.balloon_dir = MV_NO_MOVING;
1179 game.gravity = level.initial_gravity;
1180 game.explosions_delayed = TRUE;
1182 game.envelope_active = FALSE;
1184 for (i = 0; i < 4; i++)
1186 game.belt_dir[i] = MV_NO_MOVING;
1187 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1190 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1191 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1193 for (x = 0; x < lev_fieldx; x++)
1195 for (y = 0; y < lev_fieldy; y++)
1197 Feld[x][y] = level.field[x][y];
1198 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1199 ChangeDelay[x][y] = 0;
1200 ChangePage[x][y] = -1;
1201 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1203 WasJustMoving[x][y] = 0;
1204 WasJustFalling[x][y] = 0;
1206 Pushed[x][y] = FALSE;
1208 Changed[x][y] = CE_BITMASK_DEFAULT;
1209 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1211 ExplodePhase[x][y] = 0;
1212 ExplodeField[x][y] = EX_NO_EXPLOSION;
1214 RunnerVisit[x][y] = 0;
1215 PlayerVisit[x][y] = 0;
1218 GfxRandom[x][y] = INIT_GFX_RANDOM();
1219 GfxElement[x][y] = EL_UNDEFINED;
1220 GfxAction[x][y] = ACTION_DEFAULT;
1221 GfxDir[x][y] = MV_NO_MOVING;
1225 for (y = 0; y < lev_fieldy; y++)
1227 for (x = 0; x < lev_fieldx; x++)
1229 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1231 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1233 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1236 InitField(x, y, TRUE);
1242 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1243 emulate_sb ? EMU_SOKOBAN :
1244 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1246 /* correct non-moving belts to start moving left */
1247 for (i = 0; i < 4; i++)
1248 if (game.belt_dir[i] == MV_NO_MOVING)
1249 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1251 /* check if any connected player was not found in playfield */
1252 for (i = 0; i < MAX_PLAYERS; i++)
1254 struct PlayerInfo *player = &stored_player[i];
1256 if (player->connected && !player->present)
1258 for (j = 0; j < MAX_PLAYERS; j++)
1260 struct PlayerInfo *some_player = &stored_player[j];
1261 int jx = some_player->jx, jy = some_player->jy;
1263 /* assign first free player found that is present in the playfield */
1264 if (some_player->present && !some_player->connected)
1266 player->present = TRUE;
1267 player->active = TRUE;
1268 some_player->present = FALSE;
1270 StorePlayer[jx][jy] = player->element_nr;
1271 player->jx = player->last_jx = jx;
1272 player->jy = player->last_jy = jy;
1282 /* when playing a tape, eliminate all players who do not participate */
1284 for (i = 0; i < MAX_PLAYERS; i++)
1286 if (stored_player[i].active && !tape.player_participates[i])
1288 struct PlayerInfo *player = &stored_player[i];
1289 int jx = player->jx, jy = player->jy;
1291 player->active = FALSE;
1292 StorePlayer[jx][jy] = 0;
1293 Feld[jx][jy] = EL_EMPTY;
1297 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1299 /* when in single player mode, eliminate all but the first active player */
1301 for (i = 0; i < MAX_PLAYERS; i++)
1303 if (stored_player[i].active)
1305 for (j = i + 1; j < MAX_PLAYERS; j++)
1307 if (stored_player[j].active)
1309 struct PlayerInfo *player = &stored_player[j];
1310 int jx = player->jx, jy = player->jy;
1312 player->active = FALSE;
1313 StorePlayer[jx][jy] = 0;
1314 Feld[jx][jy] = EL_EMPTY;
1321 /* when recording the game, store which players take part in the game */
1324 for (i = 0; i < MAX_PLAYERS; i++)
1325 if (stored_player[i].active)
1326 tape.player_participates[i] = TRUE;
1331 for (i = 0; i < MAX_PLAYERS; i++)
1333 struct PlayerInfo *player = &stored_player[i];
1335 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1340 if (local_player == player)
1341 printf("Player %d is local player.\n", i+1);
1345 if (BorderElement == EL_EMPTY)
1348 SBX_Right = lev_fieldx - SCR_FIELDX;
1350 SBY_Lower = lev_fieldy - SCR_FIELDY;
1355 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1357 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1360 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1361 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1363 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1364 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1366 /* if local player not found, look for custom element that might create
1367 the player (make some assumptions about the right custom element) */
1368 if (!local_player->present)
1370 int start_x = 0, start_y = 0;
1371 int found_rating = 0;
1372 int found_element = EL_UNDEFINED;
1374 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1376 int element = Feld[x][y];
1381 if (!IS_CUSTOM_ELEMENT(element))
1384 if (CAN_CHANGE(element))
1386 for (i = 0; i < element_info[element].num_change_pages; i++)
1388 content = element_info[element].change_page[i].target_element;
1389 is_player = ELEM_IS_PLAYER(content);
1391 if (is_player && (found_rating < 3 || element < found_element))
1397 found_element = element;
1402 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1404 content = element_info[element].content[xx][yy];
1405 is_player = ELEM_IS_PLAYER(content);
1407 if (is_player && (found_rating < 2 || element < found_element))
1409 start_x = x + xx - 1;
1410 start_y = y + yy - 1;
1413 found_element = element;
1416 if (!CAN_CHANGE(element))
1419 for (i = 0; i < element_info[element].num_change_pages; i++)
1421 content = element_info[element].change_page[i].content[xx][yy];
1422 is_player = ELEM_IS_PLAYER(content);
1424 if (is_player && (found_rating < 1 || element < found_element))
1426 start_x = x + xx - 1;
1427 start_y = y + yy - 1;
1430 found_element = element;
1436 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1437 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1440 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1441 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1447 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1448 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1449 local_player->jx - MIDPOSX);
1451 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1452 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1453 local_player->jy - MIDPOSY);
1455 scroll_x = SBX_Left;
1456 scroll_y = SBY_Upper;
1457 if (local_player->jx >= SBX_Left + MIDPOSX)
1458 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1459 local_player->jx - MIDPOSX :
1461 if (local_player->jy >= SBY_Upper + MIDPOSY)
1462 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1463 local_player->jy - MIDPOSY :
1468 CloseDoor(DOOR_CLOSE_1);
1473 /* after drawing the level, correct some elements */
1474 if (game.timegate_time_left == 0)
1475 CloseAllOpenTimegates();
1477 if (setup.soft_scrolling)
1478 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1480 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1483 /* copy default game door content to main double buffer */
1484 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1485 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1488 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1491 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1492 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1493 BlitBitmap(drawto, drawto,
1494 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1495 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1496 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1497 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1500 DrawGameDoorValues();
1504 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1505 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1506 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1510 /* copy actual game door content to door double buffer for OpenDoor() */
1511 BlitBitmap(drawto, bitmap_db_door,
1512 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1514 OpenDoor(DOOR_OPEN_ALL);
1516 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1518 if (setup.sound_music)
1521 KeyboardAutoRepeatOffUnlessAutoplay();
1525 for (i = 0; i < 4; i++)
1526 printf("Player %d %sactive.\n",
1527 i + 1, (stored_player[i].active ? "" : "not "));
1531 printf("::: starting game [%d]\n", FrameCounter);
1535 void InitMovDir(int x, int y)
1537 int i, element = Feld[x][y];
1538 static int xy[4][2] =
1545 static int direction[3][4] =
1547 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1548 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1549 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1558 Feld[x][y] = EL_BUG;
1559 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1562 case EL_SPACESHIP_RIGHT:
1563 case EL_SPACESHIP_UP:
1564 case EL_SPACESHIP_LEFT:
1565 case EL_SPACESHIP_DOWN:
1566 Feld[x][y] = EL_SPACESHIP;
1567 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1570 case EL_BD_BUTTERFLY_RIGHT:
1571 case EL_BD_BUTTERFLY_UP:
1572 case EL_BD_BUTTERFLY_LEFT:
1573 case EL_BD_BUTTERFLY_DOWN:
1574 Feld[x][y] = EL_BD_BUTTERFLY;
1575 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1578 case EL_BD_FIREFLY_RIGHT:
1579 case EL_BD_FIREFLY_UP:
1580 case EL_BD_FIREFLY_LEFT:
1581 case EL_BD_FIREFLY_DOWN:
1582 Feld[x][y] = EL_BD_FIREFLY;
1583 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1586 case EL_PACMAN_RIGHT:
1588 case EL_PACMAN_LEFT:
1589 case EL_PACMAN_DOWN:
1590 Feld[x][y] = EL_PACMAN;
1591 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1594 case EL_SP_SNIKSNAK:
1595 MovDir[x][y] = MV_UP;
1598 case EL_SP_ELECTRON:
1599 MovDir[x][y] = MV_LEFT;
1606 Feld[x][y] = EL_MOLE;
1607 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1611 if (IS_CUSTOM_ELEMENT(element))
1613 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1614 MovDir[x][y] = element_info[element].move_direction_initial;
1615 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1616 element_info[element].move_pattern == MV_TURNING_LEFT ||
1617 element_info[element].move_pattern == MV_TURNING_RIGHT ||
1618 element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
1619 element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
1620 element_info[element].move_pattern == MV_TURNING_RANDOM)
1621 MovDir[x][y] = 1 << RND(4);
1622 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1623 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1624 else if (element_info[element].move_pattern == MV_VERTICAL)
1625 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1626 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1627 MovDir[x][y] = element_info[element].move_pattern;
1628 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1629 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1631 for (i = 0; i < 4; i++)
1633 int x1 = x + xy[i][0];
1634 int y1 = y + xy[i][1];
1636 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1638 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1639 MovDir[x][y] = direction[0][i];
1641 MovDir[x][y] = direction[1][i];
1650 MovDir[x][y] = 1 << RND(4);
1652 if (element != EL_BUG &&
1653 element != EL_SPACESHIP &&
1654 element != EL_BD_BUTTERFLY &&
1655 element != EL_BD_FIREFLY)
1658 for (i = 0; i < 4; i++)
1660 int x1 = x + xy[i][0];
1661 int y1 = y + xy[i][1];
1663 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1665 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1667 MovDir[x][y] = direction[0][i];
1670 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1671 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1673 MovDir[x][y] = direction[1][i];
1682 GfxDir[x][y] = MovDir[x][y];
1685 void InitAmoebaNr(int x, int y)
1688 int group_nr = AmoebeNachbarNr(x, y);
1692 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1694 if (AmoebaCnt[i] == 0)
1702 AmoebaNr[x][y] = group_nr;
1703 AmoebaCnt[group_nr]++;
1704 AmoebaCnt2[group_nr]++;
1710 boolean raise_level = FALSE;
1712 if (local_player->MovPos)
1716 if (tape.auto_play) /* tape might already be stopped here */
1717 tape.auto_play_level_solved = TRUE;
1719 if (tape.playing && tape.auto_play)
1720 tape.auto_play_level_solved = TRUE;
1723 local_player->LevelSolved = FALSE;
1725 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1729 if (!tape.playing && setup.sound_loops)
1730 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1731 SND_CTRL_PLAY_LOOP);
1733 while (TimeLeft > 0)
1735 if (!tape.playing && !setup.sound_loops)
1736 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1737 if (TimeLeft > 0 && !(TimeLeft % 10))
1738 RaiseScore(level.score[SC_TIME_BONUS]);
1739 if (TimeLeft > 100 && !(TimeLeft % 10))
1743 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1750 if (!tape.playing && setup.sound_loops)
1751 StopSound(SND_GAME_LEVELTIME_BONUS);
1753 else if (level.time == 0) /* level without time limit */
1755 if (!tape.playing && setup.sound_loops)
1756 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1757 SND_CTRL_PLAY_LOOP);
1759 while (TimePlayed < 999)
1761 if (!tape.playing && !setup.sound_loops)
1762 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1763 if (TimePlayed < 999 && !(TimePlayed % 10))
1764 RaiseScore(level.score[SC_TIME_BONUS]);
1765 if (TimePlayed < 900 && !(TimePlayed % 10))
1769 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1776 if (!tape.playing && setup.sound_loops)
1777 StopSound(SND_GAME_LEVELTIME_BONUS);
1780 /* close exit door after last player */
1781 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1782 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1784 int element = Feld[ExitX][ExitY];
1786 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1787 EL_SP_EXIT_CLOSING);
1789 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1792 /* Hero disappears */
1793 DrawLevelField(ExitX, ExitY);
1799 CloseDoor(DOOR_CLOSE_1);
1804 SaveTape(tape.level_nr); /* Ask to save tape */
1807 if (level_nr == leveldir_current->handicap_level)
1809 leveldir_current->handicap_level++;
1810 SaveLevelSetup_SeriesInfo();
1813 if (level_editor_test_game)
1814 local_player->score = -1; /* no highscore when playing from editor */
1815 else if (level_nr < leveldir_current->last_level)
1816 raise_level = TRUE; /* advance to next level */
1818 if ((hi_pos = NewHiScore()) >= 0)
1820 game_status = GAME_MODE_SCORES;
1821 DrawHallOfFame(hi_pos);
1830 game_status = GAME_MODE_MAIN;
1847 LoadScore(level_nr);
1849 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1850 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1853 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1855 if (local_player->score > highscore[k].Score)
1857 /* player has made it to the hall of fame */
1859 if (k < MAX_SCORE_ENTRIES - 1)
1861 int m = MAX_SCORE_ENTRIES - 1;
1864 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1865 if (!strcmp(setup.player_name, highscore[l].Name))
1867 if (m == k) /* player's new highscore overwrites his old one */
1871 for (l = m; l > k; l--)
1873 strcpy(highscore[l].Name, highscore[l - 1].Name);
1874 highscore[l].Score = highscore[l - 1].Score;
1881 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1882 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1883 highscore[k].Score = local_player->score;
1889 else if (!strncmp(setup.player_name, highscore[k].Name,
1890 MAX_PLAYER_NAME_LEN))
1891 break; /* player already there with a higher score */
1897 SaveScore(level_nr);
1902 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1904 if (player->GfxAction != action || player->GfxDir != dir)
1907 printf("Player frame reset! (%d => %d, %d => %d)\n",
1908 player->GfxAction, action, player->GfxDir, dir);
1911 player->GfxAction = action;
1912 player->GfxDir = dir;
1914 player->StepFrame = 0;
1918 static void ResetRandomAnimationValue(int x, int y)
1920 GfxRandom[x][y] = INIT_GFX_RANDOM();
1923 static void ResetGfxAnimation(int x, int y)
1926 GfxAction[x][y] = ACTION_DEFAULT;
1927 GfxDir[x][y] = MovDir[x][y];
1930 void InitMovingField(int x, int y, int direction)
1932 int element = Feld[x][y];
1933 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1934 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1938 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1939 ResetGfxAnimation(x, y);
1941 MovDir[newx][newy] = MovDir[x][y] = direction;
1942 GfxDir[x][y] = direction;
1944 if (Feld[newx][newy] == EL_EMPTY)
1945 Feld[newx][newy] = EL_BLOCKED;
1947 if (direction == MV_DOWN && CAN_FALL(element))
1948 GfxAction[x][y] = ACTION_FALLING;
1950 GfxAction[x][y] = ACTION_MOVING;
1952 GfxFrame[newx][newy] = GfxFrame[x][y];
1953 GfxRandom[newx][newy] = GfxRandom[x][y];
1954 GfxAction[newx][newy] = GfxAction[x][y];
1955 GfxDir[newx][newy] = GfxDir[x][y];
1958 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1960 int direction = MovDir[x][y];
1961 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1962 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1968 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1970 int oldx = x, oldy = y;
1971 int direction = MovDir[x][y];
1973 if (direction == MV_LEFT)
1975 else if (direction == MV_RIGHT)
1977 else if (direction == MV_UP)
1979 else if (direction == MV_DOWN)
1982 *comes_from_x = oldx;
1983 *comes_from_y = oldy;
1986 int MovingOrBlocked2Element(int x, int y)
1988 int element = Feld[x][y];
1990 if (element == EL_BLOCKED)
1994 Blocked2Moving(x, y, &oldx, &oldy);
1995 return Feld[oldx][oldy];
2001 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2003 /* like MovingOrBlocked2Element(), but if element is moving
2004 and (x,y) is the field the moving element is just leaving,
2005 return EL_BLOCKED instead of the element value */
2006 int element = Feld[x][y];
2008 if (IS_MOVING(x, y))
2010 if (element == EL_BLOCKED)
2014 Blocked2Moving(x, y, &oldx, &oldy);
2015 return Feld[oldx][oldy];
2024 static void RemoveField(int x, int y)
2026 Feld[x][y] = EL_EMPTY;
2033 ChangeDelay[x][y] = 0;
2034 ChangePage[x][y] = -1;
2035 Pushed[x][y] = FALSE;
2037 GfxElement[x][y] = EL_UNDEFINED;
2038 GfxAction[x][y] = ACTION_DEFAULT;
2039 GfxDir[x][y] = MV_NO_MOVING;
2042 void RemoveMovingField(int x, int y)
2044 int oldx = x, oldy = y, newx = x, newy = y;
2045 int element = Feld[x][y];
2046 int next_element = EL_UNDEFINED;
2048 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2051 if (IS_MOVING(x, y))
2053 Moving2Blocked(x, y, &newx, &newy);
2054 if (Feld[newx][newy] != EL_BLOCKED)
2057 else if (element == EL_BLOCKED)
2059 Blocked2Moving(x, y, &oldx, &oldy);
2060 if (!IS_MOVING(oldx, oldy))
2064 if (element == EL_BLOCKED &&
2065 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2066 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2067 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2068 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2069 next_element = get_next_element(Feld[oldx][oldy]);
2071 RemoveField(oldx, oldy);
2072 RemoveField(newx, newy);
2074 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2076 if (next_element != EL_UNDEFINED)
2077 Feld[oldx][oldy] = next_element;
2079 DrawLevelField(oldx, oldy);
2080 DrawLevelField(newx, newy);
2083 void DrawDynamite(int x, int y)
2085 int sx = SCREENX(x), sy = SCREENY(y);
2086 int graphic = el2img(Feld[x][y]);
2089 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2092 if (IS_WALKABLE_INSIDE(Back[x][y]))
2096 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2097 else if (Store[x][y])
2098 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2100 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2103 if (Back[x][y] || Store[x][y])
2104 DrawGraphicThruMask(sx, sy, graphic, frame);
2106 DrawGraphic(sx, sy, graphic, frame);
2108 if (game.emulation == EMU_SUPAPLEX)
2109 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2110 else if (Store[x][y])
2111 DrawGraphicThruMask(sx, sy, graphic, frame);
2113 DrawGraphic(sx, sy, graphic, frame);
2117 void CheckDynamite(int x, int y)
2119 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2123 if (MovDelay[x][y] != 0)
2126 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2133 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2135 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2136 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2137 StopSound(SND_DYNAMITE_ACTIVE);
2139 StopSound(SND_DYNABOMB_ACTIVE);
2145 void RelocatePlayer(int x, int y, int element)
2147 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2149 if (player->GameOver) /* do not reanimate dead player */
2153 RemoveField(x, y); /* temporarily remove newly placed player */
2154 DrawLevelField(x, y);
2157 if (player->present)
2159 while (player->MovPos)
2161 ScrollPlayer(player, SCROLL_GO_ON);
2162 ScrollScreen(NULL, SCROLL_GO_ON);
2168 Delay(GAME_FRAME_DELAY);
2171 DrawPlayer(player); /* needed here only to cleanup last field */
2172 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2174 player->is_moving = FALSE;
2177 Feld[x][y] = element;
2178 InitPlayerField(x, y, element, TRUE);
2180 if (player == local_player)
2182 int scroll_xx = -999, scroll_yy = -999;
2184 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2187 int fx = FX, fy = FY;
2189 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2190 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2191 local_player->jx - MIDPOSX);
2193 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2194 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2195 local_player->jy - MIDPOSY);
2197 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2198 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2203 fx += dx * TILEX / 2;
2204 fy += dy * TILEY / 2;
2206 ScrollLevel(dx, dy);
2209 /* scroll in two steps of half tile size to make things smoother */
2210 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2212 Delay(GAME_FRAME_DELAY);
2214 /* scroll second step to align at full tile size */
2216 Delay(GAME_FRAME_DELAY);
2221 void Explode(int ex, int ey, int phase, int mode)
2225 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2226 int last_phase = num_phase * delay;
2227 int half_phase = (num_phase / 2) * delay;
2228 int first_phase_after_start = EX_PHASE_START + 1;
2230 if (game.explosions_delayed)
2232 ExplodeField[ex][ey] = mode;
2236 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2238 int center_element = Feld[ex][ey];
2241 /* --- This is only really needed (and now handled) in "Impact()". --- */
2242 /* do not explode moving elements that left the explode field in time */
2243 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2244 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2248 if (mode == EX_NORMAL || mode == EX_CENTER)
2249 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2251 /* remove things displayed in background while burning dynamite */
2252 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2255 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2257 /* put moving element to center field (and let it explode there) */
2258 center_element = MovingOrBlocked2Element(ex, ey);
2259 RemoveMovingField(ex, ey);
2260 Feld[ex][ey] = center_element;
2263 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2265 int xx = x - ex + 1;
2266 int yy = y - ey + 1;
2269 if (!IN_LEV_FIELD(x, y) ||
2270 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2271 (x != ex || y != ey)))
2274 element = Feld[x][y];
2276 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2278 element = MovingOrBlocked2Element(x, y);
2280 if (!IS_EXPLOSION_PROOF(element))
2281 RemoveMovingField(x, y);
2287 if (IS_EXPLOSION_PROOF(element))
2290 /* indestructible elements can only explode in center (but not flames) */
2291 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2292 element == EL_FLAMES)
2297 if ((IS_INDESTRUCTIBLE(element) &&
2298 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2299 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2300 element == EL_FLAMES)
2304 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2306 if (IS_ACTIVE_BOMB(element))
2308 /* re-activate things under the bomb like gate or penguin */
2309 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2316 /* save walkable background elements while explosion on same tile */
2318 if (IS_INDESTRUCTIBLE(element))
2319 Back[x][y] = element;
2321 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2322 Back[x][y] = element;
2325 /* ignite explodable elements reached by other explosion */
2326 if (element == EL_EXPLOSION)
2327 element = Store2[x][y];
2330 if (AmoebaNr[x][y] &&
2331 (element == EL_AMOEBA_FULL ||
2332 element == EL_BD_AMOEBA ||
2333 element == EL_AMOEBA_GROWING))
2335 AmoebaCnt[AmoebaNr[x][y]]--;
2336 AmoebaCnt2[AmoebaNr[x][y]]--;
2342 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2344 switch(StorePlayer[ex][ey])
2347 Store[x][y] = EL_EMERALD_RED;
2350 Store[x][y] = EL_EMERALD;
2353 Store[x][y] = EL_EMERALD_PURPLE;
2357 Store[x][y] = EL_EMERALD_YELLOW;
2361 if (game.emulation == EMU_SUPAPLEX)
2362 Store[x][y] = EL_EMPTY;
2364 else if (center_element == EL_MOLE)
2365 Store[x][y] = EL_EMERALD_RED;
2366 else if (center_element == EL_PENGUIN)
2367 Store[x][y] = EL_EMERALD_PURPLE;
2368 else if (center_element == EL_BUG)
2369 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2370 else if (center_element == EL_BD_BUTTERFLY)
2371 Store[x][y] = EL_BD_DIAMOND;
2372 else if (center_element == EL_SP_ELECTRON)
2373 Store[x][y] = EL_SP_INFOTRON;
2374 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2375 Store[x][y] = level.amoeba_content;
2376 else if (center_element == EL_YAMYAM)
2377 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2378 else if (IS_CUSTOM_ELEMENT(center_element) &&
2379 element_info[center_element].content[xx][yy] != EL_EMPTY)
2380 Store[x][y] = element_info[center_element].content[xx][yy];
2381 else if (element == EL_WALL_EMERALD)
2382 Store[x][y] = EL_EMERALD;
2383 else if (element == EL_WALL_DIAMOND)
2384 Store[x][y] = EL_DIAMOND;
2385 else if (element == EL_WALL_BD_DIAMOND)
2386 Store[x][y] = EL_BD_DIAMOND;
2387 else if (element == EL_WALL_EMERALD_YELLOW)
2388 Store[x][y] = EL_EMERALD_YELLOW;
2389 else if (element == EL_WALL_EMERALD_RED)
2390 Store[x][y] = EL_EMERALD_RED;
2391 else if (element == EL_WALL_EMERALD_PURPLE)
2392 Store[x][y] = EL_EMERALD_PURPLE;
2393 else if (element == EL_WALL_PEARL)
2394 Store[x][y] = EL_PEARL;
2395 else if (element == EL_WALL_CRYSTAL)
2396 Store[x][y] = EL_CRYSTAL;
2397 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2398 Store[x][y] = element_info[element].content[1][1];
2400 Store[x][y] = EL_EMPTY;
2402 if (x != ex || y != ey ||
2403 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2404 Store2[x][y] = element;
2407 if (AmoebaNr[x][y] &&
2408 (element == EL_AMOEBA_FULL ||
2409 element == EL_BD_AMOEBA ||
2410 element == EL_AMOEBA_GROWING))
2412 AmoebaCnt[AmoebaNr[x][y]]--;
2413 AmoebaCnt2[AmoebaNr[x][y]]--;
2419 MovDir[x][y] = MovPos[x][y] = 0;
2420 GfxDir[x][y] = MovDir[x][y];
2425 Feld[x][y] = EL_EXPLOSION;
2427 GfxElement[x][y] = center_element;
2429 GfxElement[x][y] = EL_UNDEFINED;
2432 ExplodePhase[x][y] = 1;
2436 if (center_element == EL_YAMYAM)
2437 game.yamyam_content_nr =
2438 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2449 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2453 /* activate this even in non-DEBUG version until cause for crash in
2454 getGraphicAnimationFrame() (see below) is found and eliminated */
2458 if (GfxElement[x][y] == EL_UNDEFINED)
2461 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2462 printf("Explode(): This should never happen!\n");
2465 GfxElement[x][y] = EL_EMPTY;
2469 if (phase == first_phase_after_start)
2471 int element = Store2[x][y];
2473 if (element == EL_BLACK_ORB)
2475 Feld[x][y] = Store2[x][y];
2480 else if (phase == half_phase)
2482 int element = Store2[x][y];
2484 if (IS_PLAYER(x, y))
2485 KillHeroUnlessProtected(x, y);
2486 else if (CAN_EXPLODE_BY_FIRE(element))
2488 Feld[x][y] = Store2[x][y];
2492 else if (element == EL_AMOEBA_TO_DIAMOND)
2493 AmoebeUmwandeln(x, y);
2496 if (phase == last_phase)
2500 element = Feld[x][y] = Store[x][y];
2501 Store[x][y] = Store2[x][y] = 0;
2502 GfxElement[x][y] = EL_UNDEFINED;
2504 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2505 element = Feld[x][y] = Back[x][y];
2508 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2509 GfxDir[x][y] = MV_NO_MOVING;
2510 ChangeDelay[x][y] = 0;
2511 ChangePage[x][y] = -1;
2513 InitField(x, y, FALSE);
2514 if (CAN_MOVE(element))
2516 DrawLevelField(x, y);
2518 TestIfElementTouchesCustomElement(x, y);
2520 if (GFX_CRUMBLED(element))
2521 DrawLevelFieldCrumbledSandNeighbours(x, y);
2523 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2524 StorePlayer[x][y] = 0;
2526 if (ELEM_IS_PLAYER(element))
2527 RelocatePlayer(x, y, element);
2529 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2532 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2534 int stored = Store[x][y];
2535 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2536 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2539 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2542 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2543 element_info[GfxElement[x][y]].token_name,
2548 DrawLevelFieldCrumbledSand(x, y);
2550 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2552 DrawLevelElement(x, y, Back[x][y]);
2553 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2555 else if (IS_WALKABLE_UNDER(Back[x][y]))
2557 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2558 DrawLevelElementThruMask(x, y, Back[x][y]);
2560 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2561 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2565 void DynaExplode(int ex, int ey)
2568 int dynabomb_size = 1;
2569 boolean dynabomb_xl = FALSE;
2570 struct PlayerInfo *player;
2571 static int xy[4][2] =
2579 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2581 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2582 dynabomb_size = player->dynabomb_size;
2583 dynabomb_xl = player->dynabomb_xl;
2584 player->dynabombs_left++;
2587 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2589 for (i = 0; i < 4; i++)
2591 for (j = 1; j <= dynabomb_size; j++)
2593 int x = ex + j * xy[i % 4][0];
2594 int y = ey + j * xy[i % 4][1];
2597 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2600 element = Feld[x][y];
2602 /* do not restart explosions of fields with active bombs */
2603 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2606 Explode(x, y, EX_PHASE_START, EX_BORDER);
2608 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2609 if (element != EL_EMPTY &&
2610 element != EL_SAND &&
2611 element != EL_EXPLOSION &&
2618 void Bang(int x, int y)
2621 int element = MovingOrBlocked2Element(x, y);
2623 int element = Feld[x][y];
2627 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2629 if (IS_PLAYER(x, y))
2632 struct PlayerInfo *player = PLAYERINFO(x, y);
2634 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2635 player->element_nr);
2640 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2642 if (game.emulation == EMU_SUPAPLEX)
2643 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2645 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2650 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2658 case EL_BD_BUTTERFLY:
2661 case EL_DARK_YAMYAM:
2665 RaiseScoreElement(element);
2666 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2668 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2669 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2670 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2671 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2672 case EL_DYNABOMB_INCREASE_NUMBER:
2673 case EL_DYNABOMB_INCREASE_SIZE:
2674 case EL_DYNABOMB_INCREASE_POWER:
2679 case EL_LAMP_ACTIVE:
2680 if (IS_PLAYER(x, y))
2681 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2683 Explode(x, y, EX_PHASE_START, EX_CENTER);
2686 if (CAN_EXPLODE_1X1(element))
2687 Explode(x, y, EX_PHASE_START, EX_CENTER);
2689 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2693 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2696 void SplashAcid(int x, int y)
2698 int element = Feld[x][y];
2700 if (element != EL_ACID_SPLASH_LEFT &&
2701 element != EL_ACID_SPLASH_RIGHT)
2703 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2705 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2706 (!IN_LEV_FIELD(x-1, y-1) ||
2707 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2708 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2710 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2711 (!IN_LEV_FIELD(x+1, y-1) ||
2712 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2713 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2717 static void InitBeltMovement()
2719 static int belt_base_element[4] =
2721 EL_CONVEYOR_BELT_1_LEFT,
2722 EL_CONVEYOR_BELT_2_LEFT,
2723 EL_CONVEYOR_BELT_3_LEFT,
2724 EL_CONVEYOR_BELT_4_LEFT
2726 static int belt_base_active_element[4] =
2728 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2729 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2730 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2731 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2736 /* set frame order for belt animation graphic according to belt direction */
2737 for (i = 0; i < 4; i++)
2741 for (j = 0; j < 3; j++)
2743 int element = belt_base_active_element[belt_nr] + j;
2744 int graphic = el2img(element);
2746 if (game.belt_dir[i] == MV_LEFT)
2747 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2749 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2753 for (y = 0; y < lev_fieldy; y++)
2755 for (x = 0; x < lev_fieldx; x++)
2757 int element = Feld[x][y];
2759 for (i = 0; i < 4; i++)
2761 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2763 int e_belt_nr = getBeltNrFromBeltElement(element);
2766 if (e_belt_nr == belt_nr)
2768 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2770 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2778 static void ToggleBeltSwitch(int x, int y)
2780 static int belt_base_element[4] =
2782 EL_CONVEYOR_BELT_1_LEFT,
2783 EL_CONVEYOR_BELT_2_LEFT,
2784 EL_CONVEYOR_BELT_3_LEFT,
2785 EL_CONVEYOR_BELT_4_LEFT
2787 static int belt_base_active_element[4] =
2789 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2790 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2791 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2792 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2794 static int belt_base_switch_element[4] =
2796 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2797 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2798 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2799 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2801 static int belt_move_dir[4] =
2809 int element = Feld[x][y];
2810 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2811 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2812 int belt_dir = belt_move_dir[belt_dir_nr];
2815 if (!IS_BELT_SWITCH(element))
2818 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2819 game.belt_dir[belt_nr] = belt_dir;
2821 if (belt_dir_nr == 3)
2824 /* set frame order for belt animation graphic according to belt direction */
2825 for (i = 0; i < 3; i++)
2827 int element = belt_base_active_element[belt_nr] + i;
2828 int graphic = el2img(element);
2830 if (belt_dir == MV_LEFT)
2831 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2833 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2836 for (yy = 0; yy < lev_fieldy; yy++)
2838 for (xx = 0; xx < lev_fieldx; xx++)
2840 int element = Feld[xx][yy];
2842 if (IS_BELT_SWITCH(element))
2844 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2846 if (e_belt_nr == belt_nr)
2848 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2849 DrawLevelField(xx, yy);
2852 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2854 int e_belt_nr = getBeltNrFromBeltElement(element);
2856 if (e_belt_nr == belt_nr)
2858 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2860 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2861 DrawLevelField(xx, yy);
2864 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2866 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2868 if (e_belt_nr == belt_nr)
2870 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2872 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2873 DrawLevelField(xx, yy);
2880 static void ToggleSwitchgateSwitch(int x, int y)
2884 game.switchgate_pos = !game.switchgate_pos;
2886 for (yy = 0; yy < lev_fieldy; yy++)
2888 for (xx = 0; xx < lev_fieldx; xx++)
2890 int element = Feld[xx][yy];
2892 if (element == EL_SWITCHGATE_SWITCH_UP ||
2893 element == EL_SWITCHGATE_SWITCH_DOWN)
2895 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2896 DrawLevelField(xx, yy);
2898 else if (element == EL_SWITCHGATE_OPEN ||
2899 element == EL_SWITCHGATE_OPENING)
2901 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2903 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2905 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2908 else if (element == EL_SWITCHGATE_CLOSED ||
2909 element == EL_SWITCHGATE_CLOSING)
2911 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2913 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2915 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2922 static int getInvisibleActiveFromInvisibleElement(int element)
2924 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2925 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2926 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2930 static int getInvisibleFromInvisibleActiveElement(int element)
2932 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2933 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2934 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2938 static void RedrawAllLightSwitchesAndInvisibleElements()
2942 for (y = 0; y < lev_fieldy; y++)
2944 for (x = 0; x < lev_fieldx; x++)
2946 int element = Feld[x][y];
2948 if (element == EL_LIGHT_SWITCH &&
2949 game.light_time_left > 0)
2951 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2952 DrawLevelField(x, y);
2954 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2955 game.light_time_left == 0)
2957 Feld[x][y] = EL_LIGHT_SWITCH;
2958 DrawLevelField(x, y);
2960 else if (element == EL_INVISIBLE_STEELWALL ||
2961 element == EL_INVISIBLE_WALL ||
2962 element == EL_INVISIBLE_SAND)
2964 if (game.light_time_left > 0)
2965 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2967 DrawLevelField(x, y);
2969 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2970 element == EL_INVISIBLE_WALL_ACTIVE ||
2971 element == EL_INVISIBLE_SAND_ACTIVE)
2973 if (game.light_time_left == 0)
2974 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2976 DrawLevelField(x, y);
2982 static void ToggleLightSwitch(int x, int y)
2984 int element = Feld[x][y];
2986 game.light_time_left =
2987 (element == EL_LIGHT_SWITCH ?
2988 level.time_light * FRAMES_PER_SECOND : 0);
2990 RedrawAllLightSwitchesAndInvisibleElements();
2993 static void ActivateTimegateSwitch(int x, int y)
2997 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2999 for (yy = 0; yy < lev_fieldy; yy++)
3001 for (xx = 0; xx < lev_fieldx; xx++)
3003 int element = Feld[xx][yy];
3005 if (element == EL_TIMEGATE_CLOSED ||
3006 element == EL_TIMEGATE_CLOSING)
3008 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3009 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3013 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3015 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3016 DrawLevelField(xx, yy);
3023 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3026 inline static int getElementMoveStepsize(int x, int y)
3028 int element = Feld[x][y];
3029 int direction = MovDir[x][y];
3030 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3031 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3032 int horiz_move = (dx != 0);
3033 int sign = (horiz_move ? dx : dy);
3034 int step = sign * element_info[element].move_stepsize;
3036 /* special values for move stepsize for spring and things on conveyor belt */
3039 if (CAN_FALL(element) &&
3040 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3041 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3042 else if (element == EL_SPRING)
3043 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3049 void Impact(int x, int y)
3051 boolean lastline = (y == lev_fieldy-1);
3052 boolean object_hit = FALSE;
3053 boolean impact = (lastline || object_hit);
3054 int element = Feld[x][y];
3055 int smashed = EL_UNDEFINED;
3057 if (!lastline) /* check if element below was hit */
3059 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3062 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3063 MovDir[x][y + 1] != MV_DOWN ||
3064 MovPos[x][y + 1] <= TILEY / 2));
3066 /* do not smash moving elements that left the smashed field in time */
3067 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3068 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3072 smashed = MovingOrBlocked2Element(x, y + 1);
3074 impact = (lastline || object_hit);
3077 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3083 /* only reset graphic animation if graphic really changes after impact */
3085 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3087 ResetGfxAnimation(x, y);
3088 DrawLevelField(x, y);
3091 if (impact && CAN_EXPLODE_IMPACT(element))
3096 else if (impact && element == EL_PEARL)
3098 Feld[x][y] = EL_PEARL_BREAKING;
3099 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3102 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3104 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3109 if (impact && element == EL_AMOEBA_DROP)
3111 if (object_hit && IS_PLAYER(x, y + 1))
3112 KillHeroUnlessProtected(x, y + 1);
3113 else if (object_hit && smashed == EL_PENGUIN)
3117 Feld[x][y] = EL_AMOEBA_GROWING;
3118 Store[x][y] = EL_AMOEBA_WET;
3120 ResetRandomAnimationValue(x, y);
3125 if (object_hit) /* check which object was hit */
3127 if (CAN_PASS_MAGIC_WALL(element) &&
3128 (smashed == EL_MAGIC_WALL ||
3129 smashed == EL_BD_MAGIC_WALL))
3132 int activated_magic_wall =
3133 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3134 EL_BD_MAGIC_WALL_ACTIVE);
3136 /* activate magic wall / mill */
3137 for (yy = 0; yy < lev_fieldy; yy++)
3138 for (xx = 0; xx < lev_fieldx; xx++)
3139 if (Feld[xx][yy] == smashed)
3140 Feld[xx][yy] = activated_magic_wall;
3142 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3143 game.magic_wall_active = TRUE;
3145 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3146 SND_MAGIC_WALL_ACTIVATING :
3147 SND_BD_MAGIC_WALL_ACTIVATING));
3150 if (IS_PLAYER(x, y + 1))
3152 if (CAN_SMASH_PLAYER(element))
3154 KillHeroUnlessProtected(x, y + 1);
3158 else if (smashed == EL_PENGUIN)
3160 if (CAN_SMASH_PLAYER(element))
3166 else if (element == EL_BD_DIAMOND)
3168 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3174 else if ((element == EL_SP_INFOTRON ||
3175 element == EL_SP_ZONK) &&
3176 (smashed == EL_SP_SNIKSNAK ||
3177 smashed == EL_SP_ELECTRON ||
3178 smashed == EL_SP_DISK_ORANGE))
3184 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3190 else if (CAN_SMASH_EVERYTHING(element))
3192 if (IS_CLASSIC_ENEMY(smashed) ||
3193 CAN_EXPLODE_SMASHED(smashed))
3198 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3200 if (smashed == EL_LAMP ||
3201 smashed == EL_LAMP_ACTIVE)
3206 else if (smashed == EL_NUT)
3208 Feld[x][y + 1] = EL_NUT_BREAKING;
3209 PlayLevelSound(x, y, SND_NUT_BREAKING);
3210 RaiseScoreElement(EL_NUT);
3213 else if (smashed == EL_PEARL)
3215 Feld[x][y + 1] = EL_PEARL_BREAKING;
3216 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3219 else if (smashed == EL_DIAMOND)
3221 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3222 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3225 else if (IS_BELT_SWITCH(smashed))
3227 ToggleBeltSwitch(x, y + 1);
3229 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3230 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3232 ToggleSwitchgateSwitch(x, y + 1);
3234 else if (smashed == EL_LIGHT_SWITCH ||
3235 smashed == EL_LIGHT_SWITCH_ACTIVE)
3237 ToggleLightSwitch(x, y + 1);
3241 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3243 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3244 CE_OTHER_IS_SWITCHING);
3245 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3251 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3256 /* play sound of magic wall / mill */
3258 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3259 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3261 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3262 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3263 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3264 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3269 /* play sound of object that hits the ground */
3270 if (lastline || object_hit)
3271 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3274 inline static void TurnRoundExt(int x, int y)
3286 { 0, 0 }, { 0, 0 }, { 0, 0 },
3291 int left, right, back;
3295 { MV_DOWN, MV_UP, MV_RIGHT },
3296 { MV_UP, MV_DOWN, MV_LEFT },
3298 { MV_LEFT, MV_RIGHT, MV_DOWN },
3302 { MV_RIGHT, MV_LEFT, MV_UP }
3305 int element = Feld[x][y];
3306 int move_pattern = element_info[element].move_pattern;
3308 int old_move_dir = MovDir[x][y];
3309 int left_dir = turn[old_move_dir].left;
3310 int right_dir = turn[old_move_dir].right;
3311 int back_dir = turn[old_move_dir].back;
3313 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3314 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3315 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3316 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3318 int left_x = x + left_dx, left_y = y + left_dy;
3319 int right_x = x + right_dx, right_y = y + right_dy;
3320 int move_x = x + move_dx, move_y = y + move_dy;
3324 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3326 TestIfBadThingTouchesOtherBadThing(x, y);
3328 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3329 MovDir[x][y] = right_dir;
3330 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3331 MovDir[x][y] = left_dir;
3333 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3335 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3338 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3339 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3341 TestIfBadThingTouchesOtherBadThing(x, y);
3343 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3344 MovDir[x][y] = left_dir;
3345 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3346 MovDir[x][y] = right_dir;
3348 if ((element == EL_SPACESHIP ||
3349 element == EL_SP_SNIKSNAK ||
3350 element == EL_SP_ELECTRON)
3351 && MovDir[x][y] != old_move_dir)
3353 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3356 else if (element == EL_YAMYAM)
3358 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3359 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3361 if (can_turn_left && can_turn_right)
3362 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3363 else if (can_turn_left)
3364 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3365 else if (can_turn_right)
3366 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3368 MovDir[x][y] = back_dir;
3370 MovDelay[x][y] = 16 + 16 * RND(3);
3372 else if (element == EL_DARK_YAMYAM)
3374 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3375 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3377 if (can_turn_left && can_turn_right)
3378 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3379 else if (can_turn_left)
3380 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3381 else if (can_turn_right)
3382 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3384 MovDir[x][y] = back_dir;
3386 MovDelay[x][y] = 16 + 16 * RND(3);
3388 else if (element == EL_PACMAN)
3390 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3391 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3393 if (can_turn_left && can_turn_right)
3394 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3395 else if (can_turn_left)
3396 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3397 else if (can_turn_right)
3398 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3400 MovDir[x][y] = back_dir;
3402 MovDelay[x][y] = 6 + RND(40);
3404 else if (element == EL_PIG)
3406 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3407 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3408 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3409 boolean should_turn_left, should_turn_right, should_move_on;
3411 int rnd = RND(rnd_value);
3413 should_turn_left = (can_turn_left &&
3415 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3416 y + back_dy + left_dy)));
3417 should_turn_right = (can_turn_right &&
3419 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3420 y + back_dy + right_dy)));
3421 should_move_on = (can_move_on &&
3424 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3425 y + move_dy + left_dy) ||
3426 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3427 y + move_dy + right_dy)));
3429 if (should_turn_left || should_turn_right || should_move_on)
3431 if (should_turn_left && should_turn_right && should_move_on)
3432 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3433 rnd < 2 * rnd_value / 3 ? right_dir :
3435 else if (should_turn_left && should_turn_right)
3436 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3437 else if (should_turn_left && should_move_on)
3438 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3439 else if (should_turn_right && should_move_on)
3440 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3441 else if (should_turn_left)
3442 MovDir[x][y] = left_dir;
3443 else if (should_turn_right)
3444 MovDir[x][y] = right_dir;
3445 else if (should_move_on)
3446 MovDir[x][y] = old_move_dir;
3448 else if (can_move_on && rnd > rnd_value / 8)
3449 MovDir[x][y] = old_move_dir;
3450 else if (can_turn_left && can_turn_right)
3451 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3452 else if (can_turn_left && rnd > rnd_value / 8)
3453 MovDir[x][y] = left_dir;
3454 else if (can_turn_right && rnd > rnd_value/8)
3455 MovDir[x][y] = right_dir;
3457 MovDir[x][y] = back_dir;
3459 xx = x + move_xy[MovDir[x][y]].x;
3460 yy = y + move_xy[MovDir[x][y]].y;
3462 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3463 MovDir[x][y] = old_move_dir;
3467 else if (element == EL_DRAGON)
3469 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3470 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3471 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3473 int rnd = RND(rnd_value);
3476 if (FrameCounter < 1 && x == 0 && y == 29)
3477 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3480 if (can_move_on && rnd > rnd_value / 8)
3481 MovDir[x][y] = old_move_dir;
3482 else if (can_turn_left && can_turn_right)
3483 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3484 else if (can_turn_left && rnd > rnd_value / 8)
3485 MovDir[x][y] = left_dir;
3486 else if (can_turn_right && rnd > rnd_value / 8)
3487 MovDir[x][y] = right_dir;
3489 MovDir[x][y] = back_dir;
3491 xx = x + move_xy[MovDir[x][y]].x;
3492 yy = y + move_xy[MovDir[x][y]].y;
3495 if (FrameCounter < 1 && x == 0 && y == 29)
3496 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3497 xx, yy, Feld[xx][yy],
3502 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3503 MovDir[x][y] = old_move_dir;
3505 if (!IS_FREE(xx, yy))
3506 MovDir[x][y] = old_move_dir;
3510 if (FrameCounter < 1 && x == 0 && y == 29)
3511 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3516 else if (element == EL_MOLE)
3518 boolean can_move_on =
3519 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3520 IS_AMOEBOID(Feld[move_x][move_y]) ||
3521 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3524 boolean can_turn_left =
3525 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3526 IS_AMOEBOID(Feld[left_x][left_y])));
3528 boolean can_turn_right =
3529 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3530 IS_AMOEBOID(Feld[right_x][right_y])));
3532 if (can_turn_left && can_turn_right)
3533 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3534 else if (can_turn_left)
3535 MovDir[x][y] = left_dir;
3537 MovDir[x][y] = right_dir;
3540 if (MovDir[x][y] != old_move_dir)
3543 else if (element == EL_BALLOON)
3545 MovDir[x][y] = game.balloon_dir;
3548 else if (element == EL_SPRING)
3550 if (MovDir[x][y] & MV_HORIZONTAL &&
3551 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3552 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3553 MovDir[x][y] = MV_NO_MOVING;
3557 else if (element == EL_ROBOT ||
3558 element == EL_SATELLITE ||
3559 element == EL_PENGUIN)
3561 int attr_x = -1, attr_y = -1;
3572 for (i = 0; i < MAX_PLAYERS; i++)
3574 struct PlayerInfo *player = &stored_player[i];
3575 int jx = player->jx, jy = player->jy;
3577 if (!player->active)
3581 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3589 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3595 if (element == EL_PENGUIN)
3598 static int xy[4][2] =
3606 for (i = 0; i < 4; i++)
3608 int ex = x + xy[i % 4][0];
3609 int ey = y + xy[i % 4][1];
3611 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3620 MovDir[x][y] = MV_NO_MOVING;
3622 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3623 else if (attr_x > x)
3624 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3626 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3627 else if (attr_y > y)
3628 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3630 if (element == EL_ROBOT)
3634 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3635 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3636 Moving2Blocked(x, y, &newx, &newy);
3638 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3639 MovDelay[x][y] = 8 + 8 * !RND(3);
3641 MovDelay[x][y] = 16;
3643 else if (element == EL_PENGUIN)
3649 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3651 boolean first_horiz = RND(2);
3652 int new_move_dir = MovDir[x][y];
3655 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3656 Moving2Blocked(x, y, &newx, &newy);
3658 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3662 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3663 Moving2Blocked(x, y, &newx, &newy);
3665 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3668 MovDir[x][y] = old_move_dir;
3672 else /* (element == EL_SATELLITE) */
3678 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3680 boolean first_horiz = RND(2);
3681 int new_move_dir = MovDir[x][y];
3684 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3685 Moving2Blocked(x, y, &newx, &newy);
3687 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3691 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3692 Moving2Blocked(x, y, &newx, &newy);
3694 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3697 MovDir[x][y] = old_move_dir;
3702 else if (move_pattern == MV_TURNING_LEFT ||
3703 move_pattern == MV_TURNING_RIGHT ||
3704 move_pattern == MV_TURNING_LEFT_RIGHT ||
3705 move_pattern == MV_TURNING_RIGHT_LEFT ||
3706 move_pattern == MV_TURNING_RANDOM ||
3707 move_pattern == MV_ALL_DIRECTIONS)
3709 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3710 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3712 if (move_pattern == MV_TURNING_LEFT)
3713 MovDir[x][y] = left_dir;
3714 else if (move_pattern == MV_TURNING_RIGHT)
3715 MovDir[x][y] = right_dir;
3716 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3717 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3718 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3719 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3720 else if (move_pattern == MV_TURNING_RANDOM)
3721 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3722 can_turn_right && !can_turn_left ? right_dir :
3723 RND(2) ? left_dir : right_dir);
3724 else if (can_turn_left && can_turn_right)
3725 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3726 else if (can_turn_left)
3727 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3728 else if (can_turn_right)
3729 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3731 MovDir[x][y] = back_dir;
3733 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3735 else if (move_pattern == MV_HORIZONTAL ||
3736 move_pattern == MV_VERTICAL)
3738 if (move_pattern & old_move_dir)
3739 MovDir[x][y] = back_dir;
3740 else if (move_pattern == MV_HORIZONTAL)
3741 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3742 else if (move_pattern == MV_VERTICAL)
3743 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3745 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3747 else if (move_pattern & MV_ANY_DIRECTION)
3749 MovDir[x][y] = move_pattern;
3750 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3752 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3754 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3755 MovDir[x][y] = left_dir;
3756 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3757 MovDir[x][y] = right_dir;
3759 if (MovDir[x][y] != old_move_dir)
3760 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3762 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3764 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3765 MovDir[x][y] = right_dir;
3766 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3767 MovDir[x][y] = left_dir;
3769 if (MovDir[x][y] != old_move_dir)
3770 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3772 else if (move_pattern == MV_TOWARDS_PLAYER ||
3773 move_pattern == MV_AWAY_FROM_PLAYER)
3775 int attr_x = -1, attr_y = -1;
3777 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3788 for (i = 0; i < MAX_PLAYERS; i++)
3790 struct PlayerInfo *player = &stored_player[i];
3791 int jx = player->jx, jy = player->jy;
3793 if (!player->active)
3797 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3805 MovDir[x][y] = MV_NO_MOVING;
3807 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3808 else if (attr_x > x)
3809 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3811 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3812 else if (attr_y > y)
3813 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3815 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3817 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3819 boolean first_horiz = RND(2);
3820 int new_move_dir = MovDir[x][y];
3823 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3824 Moving2Blocked(x, y, &newx, &newy);
3826 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3830 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3831 Moving2Blocked(x, y, &newx, &newy);
3833 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3836 MovDir[x][y] = old_move_dir;
3839 else if (move_pattern == MV_WHEN_PUSHED)
3841 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3842 MovDir[x][y] = MV_NO_MOVING;
3846 else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
3847 element == EL_MAZE_RUNNER)
3849 static int test_xy[7][2] =
3859 static int test_dir[7] =
3869 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3870 int move_preference = -1000000; /* start with very low preference */
3871 int new_move_dir = MV_NO_MOVING;
3872 int start_test = RND(4);
3875 for (i = 0; i < 4; i++)
3877 int move_dir = test_dir[start_test + i];
3878 int move_dir_preference;
3880 xx = x + test_xy[start_test + i][0];
3881 yy = y + test_xy[start_test + i][1];
3883 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
3884 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
3886 new_move_dir = move_dir;
3891 if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
3894 move_dir_preference = -1 * RunnerVisit[xx][yy];
3895 if (hunter_mode && PlayerVisit[xx][yy] > 0)
3896 move_dir_preference = PlayerVisit[xx][yy];
3898 if (move_dir_preference > move_preference)
3900 /* prefer field that has not been visited for the longest time */
3901 move_preference = move_dir_preference;
3902 new_move_dir = move_dir;
3904 else if (move_dir_preference == move_preference &&
3905 move_dir == old_move_dir)
3907 /* prefer last direction when all directions are preferred equally */
3908 move_preference = move_dir_preference;
3909 new_move_dir = move_dir;
3913 MovDir[x][y] = new_move_dir;
3914 if (old_move_dir != new_move_dir)
3919 static void TurnRound(int x, int y)
3921 int direction = MovDir[x][y];
3924 GfxDir[x][y] = MovDir[x][y];
3930 GfxDir[x][y] = MovDir[x][y];
3933 if (direction != MovDir[x][y])
3938 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3941 GfxAction[x][y] = ACTION_WAITING;
3945 static boolean JustBeingPushed(int x, int y)
3949 for (i = 0; i < MAX_PLAYERS; i++)
3951 struct PlayerInfo *player = &stored_player[i];
3953 if (player->active && player->is_pushing && player->MovPos)
3955 int next_jx = player->jx + (player->jx - player->last_jx);
3956 int next_jy = player->jy + (player->jy - player->last_jy);
3958 if (x == next_jx && y == next_jy)
3966 void StartMoving(int x, int y)
3968 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3969 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3970 int element = Feld[x][y];
3976 if (MovDelay[x][y] == 0)
3977 GfxAction[x][y] = ACTION_DEFAULT;
3979 /* !!! this should be handled more generic (not only for mole) !!! */
3980 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3981 GfxAction[x][y] = ACTION_DEFAULT;
3984 if (CAN_FALL(element) && y < lev_fieldy - 1)
3986 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3987 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3988 if (JustBeingPushed(x, y))
3991 if (element == EL_QUICKSAND_FULL)
3993 if (IS_FREE(x, y + 1))
3995 InitMovingField(x, y, MV_DOWN);
3996 started_moving = TRUE;
3998 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3999 Store[x][y] = EL_ROCK;
4001 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4003 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4006 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4008 if (!MovDelay[x][y])
4009 MovDelay[x][y] = TILEY + 1;
4018 Feld[x][y] = EL_QUICKSAND_EMPTY;
4019 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4020 Store[x][y + 1] = Store[x][y];
4023 PlayLevelSoundAction(x, y, ACTION_FILLING);
4025 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4029 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4030 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4032 InitMovingField(x, y, MV_DOWN);
4033 started_moving = TRUE;
4035 Feld[x][y] = EL_QUICKSAND_FILLING;
4036 Store[x][y] = element;
4038 PlayLevelSoundAction(x, y, ACTION_FILLING);
4040 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4043 else if (element == EL_MAGIC_WALL_FULL)
4045 if (IS_FREE(x, y + 1))
4047 InitMovingField(x, y, MV_DOWN);
4048 started_moving = TRUE;
4050 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4051 Store[x][y] = EL_CHANGED(Store[x][y]);
4053 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4055 if (!MovDelay[x][y])
4056 MovDelay[x][y] = TILEY/4 + 1;
4065 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4066 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4067 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4071 else if (element == EL_BD_MAGIC_WALL_FULL)
4073 if (IS_FREE(x, y + 1))
4075 InitMovingField(x, y, MV_DOWN);
4076 started_moving = TRUE;
4078 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4079 Store[x][y] = EL_CHANGED2(Store[x][y]);
4081 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4083 if (!MovDelay[x][y])
4084 MovDelay[x][y] = TILEY/4 + 1;
4093 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4094 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4095 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4099 else if (CAN_PASS_MAGIC_WALL(element) &&
4100 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4101 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4103 InitMovingField(x, y, MV_DOWN);
4104 started_moving = TRUE;
4107 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4108 EL_BD_MAGIC_WALL_FILLING);
4109 Store[x][y] = element;
4112 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4114 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4119 InitMovingField(x, y, MV_DOWN);
4120 started_moving = TRUE;
4122 Store[x][y] = EL_ACID;
4124 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4125 GfxAction[x][y + 1] = ACTION_ACTIVE;
4129 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4130 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4131 (Feld[x][y + 1] == EL_BLOCKED)) ||
4132 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4133 CAN_SMASH(element) && WasJustFalling[x][y] &&
4134 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4138 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4139 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4140 WasJustMoving[x][y] && !Pushed[x][y + 1])
4142 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4143 WasJustMoving[x][y])
4148 /* this is needed for a special case not covered by calling "Impact()"
4149 from "ContinueMoving()": if an element moves to a tile directly below
4150 another element which was just falling on that tile (which was empty
4151 in the previous frame), the falling element above would just stop
4152 instead of smashing the element below (in previous version, the above
4153 element was just checked for "moving" instead of "falling", resulting
4154 in incorrect smashes caused by horizontal movement of the above
4155 element; also, the case of the player being the element to smash was
4156 simply not covered here... :-/ ) */
4160 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4162 if (MovDir[x][y] == MV_NO_MOVING)
4164 InitMovingField(x, y, MV_DOWN);
4165 started_moving = TRUE;
4168 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4170 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4171 MovDir[x][y] = MV_DOWN;
4173 InitMovingField(x, y, MV_DOWN);
4174 started_moving = TRUE;
4176 else if (element == EL_AMOEBA_DROP)
4178 Feld[x][y] = EL_AMOEBA_GROWING;
4179 Store[x][y] = EL_AMOEBA_WET;
4181 /* Store[x][y + 1] must be zero, because:
4182 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4185 #if OLD_GAME_BEHAVIOUR
4186 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4188 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4189 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4190 element != EL_DX_SUPABOMB)
4193 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4194 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4195 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4196 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4199 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4200 (IS_FREE(x - 1, y + 1) ||
4201 Feld[x - 1][y + 1] == EL_ACID));
4202 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4203 (IS_FREE(x + 1, y + 1) ||
4204 Feld[x + 1][y + 1] == EL_ACID));
4205 boolean can_fall_any = (can_fall_left || can_fall_right);
4206 boolean can_fall_both = (can_fall_left && can_fall_right);
4208 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4210 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4212 if (slippery_type == SLIPPERY_ONLY_LEFT)
4213 can_fall_right = FALSE;
4214 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4215 can_fall_left = FALSE;
4216 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4217 can_fall_right = FALSE;
4218 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4219 can_fall_left = FALSE;
4221 can_fall_any = (can_fall_left || can_fall_right);
4222 can_fall_both = (can_fall_left && can_fall_right);
4227 if (can_fall_both &&
4228 (game.emulation != EMU_BOULDERDASH &&
4229 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4230 can_fall_left = !(can_fall_right = RND(2));
4232 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4233 started_moving = TRUE;
4236 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4238 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4239 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4240 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4241 int belt_dir = game.belt_dir[belt_nr];
4243 if ((belt_dir == MV_LEFT && left_is_free) ||
4244 (belt_dir == MV_RIGHT && right_is_free))
4246 InitMovingField(x, y, belt_dir);
4247 started_moving = TRUE;
4249 GfxAction[x][y] = ACTION_DEFAULT;
4254 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4255 if (CAN_MOVE(element) && !started_moving)
4257 int move_pattern = element_info[element].move_pattern;
4261 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4264 if ((element == EL_SATELLITE ||
4265 element == EL_BALLOON ||
4266 element == EL_SPRING)
4267 && JustBeingPushed(x, y))
4273 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4274 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4276 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4278 Moving2Blocked(x, y, &newx, &newy);
4279 if (Feld[newx][newy] == EL_BLOCKED)
4280 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4286 if (FrameCounter < 1 && x == 0 && y == 29)
4287 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4290 if (!MovDelay[x][y]) /* start new movement phase */
4292 /* all objects that can change their move direction after each step
4293 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4295 if (element != EL_YAMYAM &&
4296 element != EL_DARK_YAMYAM &&
4297 element != EL_PACMAN &&
4298 !(move_pattern & MV_ANY_DIRECTION) &&
4299 move_pattern != MV_TURNING_LEFT &&
4300 move_pattern != MV_TURNING_RIGHT &&
4301 move_pattern != MV_TURNING_LEFT_RIGHT &&
4302 move_pattern != MV_TURNING_RIGHT_LEFT &&
4303 move_pattern != MV_TURNING_RANDOM)
4308 if (FrameCounter < 1 && x == 0 && y == 29)
4309 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4312 if (MovDelay[x][y] && (element == EL_BUG ||
4313 element == EL_SPACESHIP ||
4314 element == EL_SP_SNIKSNAK ||
4315 element == EL_SP_ELECTRON ||
4316 element == EL_MOLE))
4317 DrawLevelField(x, y);
4321 if (MovDelay[x][y]) /* wait some time before next movement */
4326 if (element == EL_YAMYAM)
4329 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4330 DrawLevelElementAnimation(x, y, element);
4334 if (MovDelay[x][y]) /* element still has to wait some time */
4337 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4338 ResetGfxAnimation(x, y);
4342 if (GfxAction[x][y] != ACTION_WAITING)
4343 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4345 GfxAction[x][y] = ACTION_WAITING;
4349 if (element == EL_ROBOT ||
4351 element == EL_PACMAN ||
4353 element == EL_YAMYAM ||
4354 element == EL_DARK_YAMYAM)
4357 DrawLevelElementAnimation(x, y, element);
4359 DrawLevelElementAnimationIfNeeded(x, y, element);
4361 PlayLevelSoundAction(x, y, ACTION_WAITING);
4363 else if (element == EL_SP_ELECTRON)
4364 DrawLevelElementAnimationIfNeeded(x, y, element);
4365 else if (element == EL_DRAGON)
4368 int dir = MovDir[x][y];
4369 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4370 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4371 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4372 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4373 dir == MV_UP ? IMG_FLAMES_1_UP :
4374 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4375 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4378 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4381 GfxAction[x][y] = ACTION_ATTACKING;
4383 if (IS_PLAYER(x, y))
4384 DrawPlayerField(x, y);
4386 DrawLevelField(x, y);
4388 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4390 for (i = 1; i <= 3; i++)
4392 int xx = x + i * dx;
4393 int yy = y + i * dy;
4394 int sx = SCREENX(xx);
4395 int sy = SCREENY(yy);
4396 int flame_graphic = graphic + (i - 1);
4398 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4403 int flamed = MovingOrBlocked2Element(xx, yy);
4405 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4408 RemoveMovingField(xx, yy);
4410 Feld[xx][yy] = EL_FLAMES;
4411 if (IN_SCR_FIELD(sx, sy))
4413 DrawLevelFieldCrumbledSand(xx, yy);
4414 DrawGraphic(sx, sy, flame_graphic, frame);
4419 if (Feld[xx][yy] == EL_FLAMES)
4420 Feld[xx][yy] = EL_EMPTY;
4421 DrawLevelField(xx, yy);
4426 if (MovDelay[x][y]) /* element still has to wait some time */
4428 PlayLevelSoundAction(x, y, ACTION_WAITING);
4434 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4435 for all other elements GfxAction will be set by InitMovingField() */
4436 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4437 GfxAction[x][y] = ACTION_MOVING;
4441 /* now make next step */
4443 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4445 if (DONT_COLLIDE_WITH(element) &&
4446 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4447 !PLAYER_PROTECTED(newx, newy))
4450 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4453 /* player killed by element which is deadly when colliding with */
4455 KillHero(PLAYERINFO(newx, newy));
4460 else if ((element == EL_PENGUIN ||
4461 element == EL_ROBOT ||
4462 element == EL_SATELLITE ||
4463 element == EL_BALLOON ||
4464 IS_CUSTOM_ELEMENT(element)) &&
4465 IN_LEV_FIELD(newx, newy) &&
4466 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4469 Store[x][y] = EL_ACID;
4471 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4473 if (Feld[newx][newy] == EL_EXIT_OPEN)
4477 DrawLevelField(x, y);
4479 Feld[x][y] = EL_EMPTY;
4480 DrawLevelField(x, y);
4483 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4484 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4485 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4487 local_player->friends_still_needed--;
4488 if (!local_player->friends_still_needed &&
4489 !local_player->GameOver && AllPlayersGone)
4490 local_player->LevelSolved = local_player->GameOver = TRUE;
4494 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4496 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4497 DrawLevelField(newx, newy);
4499 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4501 else if (!IS_FREE(newx, newy))
4503 GfxAction[x][y] = ACTION_WAITING;
4505 if (IS_PLAYER(x, y))
4506 DrawPlayerField(x, y);
4508 DrawLevelField(x, y);
4512 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4514 if (IS_FOOD_PIG(Feld[newx][newy]))
4516 if (IS_MOVING(newx, newy))
4517 RemoveMovingField(newx, newy);
4520 Feld[newx][newy] = EL_EMPTY;
4521 DrawLevelField(newx, newy);
4524 PlayLevelSound(x, y, SND_PIG_DIGGING);
4526 else if (!IS_FREE(newx, newy))
4528 if (IS_PLAYER(x, y))
4529 DrawPlayerField(x, y);
4531 DrawLevelField(x, y);
4535 else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4536 element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4539 if (IS_FREE(newx, newy))
4541 if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4544 if (IS_MOVING(newx, newy))
4545 RemoveMovingField(newx, newy);
4548 Feld[newx][newy] = EL_EMPTY;
4549 DrawLevelField(newx, newy);
4552 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4554 else if (!IS_FREE(newx, newy))
4557 if (IS_PLAYER(x, y))
4558 DrawPlayerField(x, y);
4560 DrawLevelField(x, y);
4565 RunnerVisit[x][y] = FrameCounter;
4566 PlayerVisit[x][y] /= 8; /* expire player visit path */
4568 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4570 if (!IS_FREE(newx, newy))
4572 if (IS_PLAYER(x, y))
4573 DrawPlayerField(x, y);
4575 DrawLevelField(x, y);
4581 boolean wanna_flame = !RND(10);
4582 int dx = newx - x, dy = newy - y;
4583 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4584 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4585 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4586 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4587 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4588 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4591 IS_CLASSIC_ENEMY(element1) ||
4592 IS_CLASSIC_ENEMY(element2)) &&
4593 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4594 element1 != EL_FLAMES && element2 != EL_FLAMES)
4597 ResetGfxAnimation(x, y);
4598 GfxAction[x][y] = ACTION_ATTACKING;
4601 if (IS_PLAYER(x, y))
4602 DrawPlayerField(x, y);
4604 DrawLevelField(x, y);
4606 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4608 MovDelay[x][y] = 50;
4610 Feld[newx][newy] = EL_FLAMES;
4611 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4612 Feld[newx1][newy1] = EL_FLAMES;
4613 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4614 Feld[newx2][newy2] = EL_FLAMES;
4620 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4621 Feld[newx][newy] == EL_DIAMOND)
4623 if (IS_MOVING(newx, newy))
4624 RemoveMovingField(newx, newy);
4627 Feld[newx][newy] = EL_EMPTY;
4628 DrawLevelField(newx, newy);
4631 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4633 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4634 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4636 if (AmoebaNr[newx][newy])
4638 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4639 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4640 Feld[newx][newy] == EL_BD_AMOEBA)
4641 AmoebaCnt[AmoebaNr[newx][newy]]--;
4644 if (IS_MOVING(newx, newy))
4645 RemoveMovingField(newx, newy);
4648 Feld[newx][newy] = EL_EMPTY;
4649 DrawLevelField(newx, newy);
4652 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4654 else if ((element == EL_PACMAN || element == EL_MOLE)
4655 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4657 if (AmoebaNr[newx][newy])
4659 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4660 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4661 Feld[newx][newy] == EL_BD_AMOEBA)
4662 AmoebaCnt[AmoebaNr[newx][newy]]--;
4665 if (element == EL_MOLE)
4667 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4668 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4670 ResetGfxAnimation(x, y);
4671 GfxAction[x][y] = ACTION_DIGGING;
4672 DrawLevelField(x, y);
4674 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4675 return; /* wait for shrinking amoeba */
4677 else /* element == EL_PACMAN */
4679 Feld[newx][newy] = EL_EMPTY;
4680 DrawLevelField(newx, newy);
4681 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4684 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4685 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4686 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4688 /* wait for shrinking amoeba to completely disappear */
4691 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4693 /* object was running against a wall */
4698 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4699 DrawLevelElementAnimation(x, y, element);
4701 if (element == EL_BUG ||
4702 element == EL_SPACESHIP ||
4703 element == EL_SP_SNIKSNAK)
4704 DrawLevelField(x, y);
4705 else if (element == EL_MOLE)
4706 DrawLevelField(x, y);
4707 else if (element == EL_BD_BUTTERFLY ||
4708 element == EL_BD_FIREFLY)
4709 DrawLevelElementAnimationIfNeeded(x, y, element);
4710 else if (element == EL_SATELLITE)
4711 DrawLevelElementAnimationIfNeeded(x, y, element);
4712 else if (element == EL_SP_ELECTRON)
4713 DrawLevelElementAnimationIfNeeded(x, y, element);
4716 if (DONT_TOUCH(element))
4717 TestIfBadThingTouchesHero(x, y);
4720 PlayLevelSoundAction(x, y, ACTION_WAITING);
4726 InitMovingField(x, y, MovDir[x][y]);
4728 PlayLevelSoundAction(x, y, ACTION_MOVING);
4732 ContinueMoving(x, y);
4735 void ContinueMoving(int x, int y)
4737 int element = Feld[x][y];
4738 int direction = MovDir[x][y];
4739 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4740 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4741 int newx = x + dx, newy = y + dy;
4742 int nextx = newx + dx, nexty = newy + dy;
4743 boolean pushed = Pushed[x][y];
4745 MovPos[x][y] += getElementMoveStepsize(x, y);
4747 if (pushed) /* special case: moving object pushed by player */
4748 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4750 if (ABS(MovPos[x][y]) < TILEX)
4752 DrawLevelField(x, y);
4754 return; /* element is still moving */
4757 /* element reached destination field */
4759 Feld[x][y] = EL_EMPTY;
4760 Feld[newx][newy] = element;
4761 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4763 if (element == EL_MOLE)
4765 Feld[x][y] = EL_SAND;
4767 DrawLevelFieldCrumbledSandNeighbours(x, y);
4769 else if (element == EL_QUICKSAND_FILLING)
4771 element = Feld[newx][newy] = get_next_element(element);
4772 Store[newx][newy] = Store[x][y];
4774 else if (element == EL_QUICKSAND_EMPTYING)
4776 Feld[x][y] = get_next_element(element);
4777 element = Feld[newx][newy] = Store[x][y];
4779 else if (element == EL_MAGIC_WALL_FILLING)
4781 element = Feld[newx][newy] = get_next_element(element);
4782 if (!game.magic_wall_active)
4783 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4784 Store[newx][newy] = Store[x][y];
4786 else if (element == EL_MAGIC_WALL_EMPTYING)
4788 Feld[x][y] = get_next_element(element);
4789 if (!game.magic_wall_active)
4790 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4791 element = Feld[newx][newy] = Store[x][y];
4793 else if (element == EL_BD_MAGIC_WALL_FILLING)
4795 element = Feld[newx][newy] = get_next_element(element);
4796 if (!game.magic_wall_active)
4797 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4798 Store[newx][newy] = Store[x][y];
4800 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4802 Feld[x][y] = get_next_element(element);
4803 if (!game.magic_wall_active)
4804 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4805 element = Feld[newx][newy] = Store[x][y];
4807 else if (element == EL_AMOEBA_DROPPING)
4809 Feld[x][y] = get_next_element(element);
4810 element = Feld[newx][newy] = Store[x][y];
4812 else if (element == EL_SOKOBAN_OBJECT)
4815 Feld[x][y] = Back[x][y];
4817 if (Back[newx][newy])
4818 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4820 Back[x][y] = Back[newx][newy] = 0;
4822 else if (Store[x][y] == EL_ACID)
4824 element = Feld[newx][newy] = EL_ACID;
4828 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4829 MovDelay[newx][newy] = 0;
4831 /* copy element change control values to new field */
4832 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4833 ChangePage[newx][newy] = ChangePage[x][y];
4834 Changed[newx][newy] = Changed[x][y];
4835 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4837 ChangeDelay[x][y] = 0;
4838 ChangePage[x][y] = -1;
4839 Changed[x][y] = CE_BITMASK_DEFAULT;
4840 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4842 /* copy animation control values to new field */
4843 GfxFrame[newx][newy] = GfxFrame[x][y];
4844 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4845 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4846 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4848 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4850 ResetGfxAnimation(x, y); /* reset animation values for old field */
4853 /* 2.1.1 (does not work correctly for spring) */
4854 if (!CAN_MOVE(element))
4855 MovDir[newx][newy] = 0;
4859 /* (does not work for falling objects that slide horizontally) */
4860 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4861 MovDir[newx][newy] = 0;
4864 if (!CAN_MOVE(element) ||
4865 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4866 MovDir[newx][newy] = 0;
4869 if (!CAN_MOVE(element) ||
4870 (CAN_FALL(element) && direction == MV_DOWN))
4871 GfxDir[x][y] = MovDir[newx][newy] = 0;
4876 DrawLevelField(x, y);
4877 DrawLevelField(newx, newy);
4879 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4881 /* prevent pushed element from moving on in pushed direction */
4882 if (pushed && CAN_MOVE(element) &&
4883 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4884 !(element_info[element].move_pattern & direction))
4885 TurnRound(newx, newy);
4887 if (!pushed) /* special case: moving object pushed by player */
4889 WasJustMoving[newx][newy] = 3;
4891 if (CAN_FALL(element) && direction == MV_DOWN)
4892 WasJustFalling[newx][newy] = 3;
4895 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4897 TestIfBadThingTouchesHero(newx, newy);
4898 TestIfBadThingTouchesFriend(newx, newy);
4900 if (!IS_CUSTOM_ELEMENT(element))
4901 TestIfBadThingTouchesOtherBadThing(newx, newy);
4903 else if (element == EL_PENGUIN)
4904 TestIfFriendTouchesBadThing(newx, newy);
4906 if (CAN_FALL(element) && direction == MV_DOWN &&
4907 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4911 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4915 if (ChangePage[newx][newy] != -1) /* delayed change */
4916 ChangeElement(newx, newy, ChangePage[newx][newy]);
4919 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4921 int hitting_element = Feld[newx][newy];
4923 /* !!! fix side (direction) orientation here and elsewhere !!! */
4924 CheckElementSideChange(newx, newy, hitting_element,
4925 direction, CE_COLLISION_ACTIVE, -1);
4928 if (IN_LEV_FIELD(nextx, nexty))
4930 static int opposite_directions[] =
4937 int move_dir_bit = MV_DIR_BIT(direction);
4938 int opposite_direction = opposite_directions[move_dir_bit];
4939 int hitting_side = direction;
4940 int touched_side = opposite_direction;
4941 int touched_element = MovingOrBlocked2Element(nextx, nexty);
4942 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
4943 MovDir[nextx][nexty] != direction ||
4944 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
4950 CheckElementSideChange(nextx, nexty, touched_element,
4951 opposite_direction, CE_COLLISION_PASSIVE, -1);
4953 if (IS_CUSTOM_ELEMENT(hitting_element) &&
4954 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_COLL_ACTIVE))
4956 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
4958 struct ElementChangeInfo *change =
4959 &element_info[hitting_element].change_page[i];
4961 if (change->can_change &&
4962 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_ACTIVE) &&
4963 change->sides & touched_side &&
4964 change->trigger_element == touched_element)
4966 CheckElementSideChange(newx, newy, hitting_element,
4967 CH_SIDE_ANY, CE_OTHER_IS_COLL_ACTIVE, i);
4973 if (IS_CUSTOM_ELEMENT(touched_element) &&
4974 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_IS_COLL_PASSIVE))
4976 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
4978 struct ElementChangeInfo *change =
4979 &element_info[touched_element].change_page[i];
4981 if (change->can_change &&
4982 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_PASSIVE) &&
4983 change->sides & hitting_side &&
4984 change->trigger_element == hitting_element)
4986 CheckElementSideChange(nextx, nexty, touched_element,
4987 CH_SIDE_ANY, CE_OTHER_IS_COLL_PASSIVE, i);
4997 TestIfPlayerTouchesCustomElement(newx, newy);
4998 TestIfElementTouchesCustomElement(newx, newy);
5001 int AmoebeNachbarNr(int ax, int ay)
5004 int element = Feld[ax][ay];
5006 static int xy[4][2] =
5014 for (i = 0; i < 4; i++)
5016 int x = ax + xy[i][0];
5017 int y = ay + xy[i][1];
5019 if (!IN_LEV_FIELD(x, y))
5022 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5023 group_nr = AmoebaNr[x][y];
5029 void AmoebenVereinigen(int ax, int ay)
5031 int i, x, y, xx, yy;
5032 int new_group_nr = AmoebaNr[ax][ay];
5033 static int xy[4][2] =
5041 if (new_group_nr == 0)
5044 for (i = 0; i < 4; i++)
5049 if (!IN_LEV_FIELD(x, y))
5052 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5053 Feld[x][y] == EL_BD_AMOEBA ||
5054 Feld[x][y] == EL_AMOEBA_DEAD) &&
5055 AmoebaNr[x][y] != new_group_nr)
5057 int old_group_nr = AmoebaNr[x][y];
5059 if (old_group_nr == 0)
5062 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5063 AmoebaCnt[old_group_nr] = 0;
5064 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5065 AmoebaCnt2[old_group_nr] = 0;
5067 for (yy = 0; yy < lev_fieldy; yy++)
5069 for (xx = 0; xx < lev_fieldx; xx++)
5071 if (AmoebaNr[xx][yy] == old_group_nr)
5072 AmoebaNr[xx][yy] = new_group_nr;
5079 void AmoebeUmwandeln(int ax, int ay)
5083 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5085 int group_nr = AmoebaNr[ax][ay];
5090 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5091 printf("AmoebeUmwandeln(): This should never happen!\n");
5096 for (y = 0; y < lev_fieldy; y++)
5098 for (x = 0; x < lev_fieldx; x++)
5100 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5103 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5107 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5108 SND_AMOEBA_TURNING_TO_GEM :
5109 SND_AMOEBA_TURNING_TO_ROCK));
5114 static int xy[4][2] =
5122 for (i = 0; i < 4; i++)
5127 if (!IN_LEV_FIELD(x, y))
5130 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5132 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5133 SND_AMOEBA_TURNING_TO_GEM :
5134 SND_AMOEBA_TURNING_TO_ROCK));
5141 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5144 int group_nr = AmoebaNr[ax][ay];
5145 boolean done = FALSE;
5150 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5151 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5156 for (y = 0; y < lev_fieldy; y++)
5158 for (x = 0; x < lev_fieldx; x++)
5160 if (AmoebaNr[x][y] == group_nr &&
5161 (Feld[x][y] == EL_AMOEBA_DEAD ||
5162 Feld[x][y] == EL_BD_AMOEBA ||
5163 Feld[x][y] == EL_AMOEBA_GROWING))
5166 Feld[x][y] = new_element;
5167 InitField(x, y, FALSE);
5168 DrawLevelField(x, y);
5175 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5176 SND_BD_AMOEBA_TURNING_TO_ROCK :
5177 SND_BD_AMOEBA_TURNING_TO_GEM));
5180 void AmoebeWaechst(int x, int y)
5182 static unsigned long sound_delay = 0;
5183 static unsigned long sound_delay_value = 0;
5185 if (!MovDelay[x][y]) /* start new growing cycle */
5189 if (DelayReached(&sound_delay, sound_delay_value))
5192 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5194 if (Store[x][y] == EL_BD_AMOEBA)
5195 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5197 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5199 sound_delay_value = 30;
5203 if (MovDelay[x][y]) /* wait some time before growing bigger */
5206 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5208 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5209 6 - MovDelay[x][y]);
5211 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5214 if (!MovDelay[x][y])
5216 Feld[x][y] = Store[x][y];
5218 DrawLevelField(x, y);
5223 void AmoebaDisappearing(int x, int y)
5225 static unsigned long sound_delay = 0;
5226 static unsigned long sound_delay_value = 0;
5228 if (!MovDelay[x][y]) /* start new shrinking cycle */
5232 if (DelayReached(&sound_delay, sound_delay_value))
5233 sound_delay_value = 30;
5236 if (MovDelay[x][y]) /* wait some time before shrinking */
5239 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5241 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5242 6 - MovDelay[x][y]);
5244 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5247 if (!MovDelay[x][y])
5249 Feld[x][y] = EL_EMPTY;
5250 DrawLevelField(x, y);
5252 /* don't let mole enter this field in this cycle;
5253 (give priority to objects falling to this field from above) */
5259 void AmoebeAbleger(int ax, int ay)
5262 int element = Feld[ax][ay];
5263 int graphic = el2img(element);
5264 int newax = ax, neway = ay;
5265 static int xy[4][2] =
5273 if (!level.amoeba_speed)
5275 Feld[ax][ay] = EL_AMOEBA_DEAD;
5276 DrawLevelField(ax, ay);
5280 if (IS_ANIMATED(graphic))
5281 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5283 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5284 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5286 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5289 if (MovDelay[ax][ay])
5293 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5296 int x = ax + xy[start][0];
5297 int y = ay + xy[start][1];
5299 if (!IN_LEV_FIELD(x, y))
5302 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5303 if (IS_FREE(x, y) ||
5304 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5310 if (newax == ax && neway == ay)
5313 else /* normal or "filled" (BD style) amoeba */
5316 boolean waiting_for_player = FALSE;
5318 for (i = 0; i < 4; i++)
5320 int j = (start + i) % 4;
5321 int x = ax + xy[j][0];
5322 int y = ay + xy[j][1];
5324 if (!IN_LEV_FIELD(x, y))
5327 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5328 if (IS_FREE(x, y) ||
5329 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5335 else if (IS_PLAYER(x, y))
5336 waiting_for_player = TRUE;
5339 if (newax == ax && neway == ay) /* amoeba cannot grow */
5341 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5343 Feld[ax][ay] = EL_AMOEBA_DEAD;
5344 DrawLevelField(ax, ay);
5345 AmoebaCnt[AmoebaNr[ax][ay]]--;
5347 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5349 if (element == EL_AMOEBA_FULL)
5350 AmoebeUmwandeln(ax, ay);
5351 else if (element == EL_BD_AMOEBA)
5352 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5357 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5359 /* amoeba gets larger by growing in some direction */
5361 int new_group_nr = AmoebaNr[ax][ay];
5364 if (new_group_nr == 0)
5366 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5367 printf("AmoebeAbleger(): This should never happen!\n");
5372 AmoebaNr[newax][neway] = new_group_nr;
5373 AmoebaCnt[new_group_nr]++;
5374 AmoebaCnt2[new_group_nr]++;
5376 /* if amoeba touches other amoeba(s) after growing, unify them */
5377 AmoebenVereinigen(newax, neway);
5379 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5381 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5387 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5388 (neway == lev_fieldy - 1 && newax != ax))
5390 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5391 Store[newax][neway] = element;
5393 else if (neway == ay)
5395 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5397 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5399 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5404 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5405 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5406 Store[ax][ay] = EL_AMOEBA_DROP;
5407 ContinueMoving(ax, ay);
5411 DrawLevelField(newax, neway);
5414 void Life(int ax, int ay)
5417 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5419 int element = Feld[ax][ay];
5420 int graphic = el2img(element);
5421 boolean changed = FALSE;
5423 if (IS_ANIMATED(graphic))
5424 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5429 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5430 MovDelay[ax][ay] = life_time;
5432 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5435 if (MovDelay[ax][ay])
5439 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5441 int xx = ax+x1, yy = ay+y1;
5444 if (!IN_LEV_FIELD(xx, yy))
5447 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5449 int x = xx+x2, y = yy+y2;
5451 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5454 if (((Feld[x][y] == element ||
5455 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5457 (IS_FREE(x, y) && Stop[x][y]))
5461 if (xx == ax && yy == ay) /* field in the middle */
5463 if (nachbarn < life[0] || nachbarn > life[1])
5465 Feld[xx][yy] = EL_EMPTY;
5467 DrawLevelField(xx, yy);
5468 Stop[xx][yy] = TRUE;
5472 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5473 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5474 { /* free border field */
5475 if (nachbarn >= life[2] && nachbarn <= life[3])
5477 Feld[xx][yy] = element;
5478 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5480 DrawLevelField(xx, yy);
5481 Stop[xx][yy] = TRUE;
5488 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5489 SND_GAME_OF_LIFE_GROWING);
5492 static void InitRobotWheel(int x, int y)
5494 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5497 static void RunRobotWheel(int x, int y)
5499 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5502 static void StopRobotWheel(int x, int y)
5504 if (ZX == x && ZY == y)
5508 static void InitTimegateWheel(int x, int y)
5510 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5513 static void RunTimegateWheel(int x, int y)
5515 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5518 void CheckExit(int x, int y)
5520 if (local_player->gems_still_needed > 0 ||
5521 local_player->sokobanfields_still_needed > 0 ||
5522 local_player->lights_still_needed > 0)
5524 int element = Feld[x][y];
5525 int graphic = el2img(element);
5527 if (IS_ANIMATED(graphic))
5528 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5533 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5536 Feld[x][y] = EL_EXIT_OPENING;
5538 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5541 void CheckExitSP(int x, int y)
5543 if (local_player->gems_still_needed > 0)
5545 int element = Feld[x][y];
5546 int graphic = el2img(element);
5548 if (IS_ANIMATED(graphic))
5549 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5554 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5557 Feld[x][y] = EL_SP_EXIT_OPENING;
5559 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5562 static void CloseAllOpenTimegates()
5566 for (y = 0; y < lev_fieldy; y++)
5568 for (x = 0; x < lev_fieldx; x++)
5570 int element = Feld[x][y];
5572 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5574 Feld[x][y] = EL_TIMEGATE_CLOSING;
5576 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5578 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5585 void EdelsteinFunkeln(int x, int y)
5587 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5590 if (Feld[x][y] == EL_BD_DIAMOND)
5593 if (MovDelay[x][y] == 0) /* next animation frame */
5594 MovDelay[x][y] = 11 * !SimpleRND(500);
5596 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5600 if (setup.direct_draw && MovDelay[x][y])
5601 SetDrawtoField(DRAW_BUFFERED);
5603 DrawLevelElementAnimation(x, y, Feld[x][y]);
5605 if (MovDelay[x][y] != 0)
5607 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5608 10 - MovDelay[x][y]);
5610 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5612 if (setup.direct_draw)
5616 dest_x = FX + SCREENX(x) * TILEX;
5617 dest_y = FY + SCREENY(y) * TILEY;
5619 BlitBitmap(drawto_field, window,
5620 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5621 SetDrawtoField(DRAW_DIRECT);
5627 void MauerWaechst(int x, int y)
5631 if (!MovDelay[x][y]) /* next animation frame */
5632 MovDelay[x][y] = 3 * delay;
5634 if (MovDelay[x][y]) /* wait some time before next frame */
5638 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5640 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5641 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5643 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5646 if (!MovDelay[x][y])
5648 if (MovDir[x][y] == MV_LEFT)
5650 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5651 DrawLevelField(x - 1, y);
5653 else if (MovDir[x][y] == MV_RIGHT)
5655 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5656 DrawLevelField(x + 1, y);
5658 else if (MovDir[x][y] == MV_UP)
5660 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5661 DrawLevelField(x, y - 1);
5665 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5666 DrawLevelField(x, y + 1);
5669 Feld[x][y] = Store[x][y];
5671 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5672 DrawLevelField(x, y);
5677 void MauerAbleger(int ax, int ay)
5679 int element = Feld[ax][ay];
5680 int graphic = el2img(element);
5681 boolean oben_frei = FALSE, unten_frei = FALSE;
5682 boolean links_frei = FALSE, rechts_frei = FALSE;
5683 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5684 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5685 boolean new_wall = FALSE;
5687 if (IS_ANIMATED(graphic))
5688 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5690 if (!MovDelay[ax][ay]) /* start building new wall */
5691 MovDelay[ax][ay] = 6;
5693 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5696 if (MovDelay[ax][ay])
5700 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5702 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5704 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5706 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5709 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5710 element == EL_EXPANDABLE_WALL_ANY)
5714 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5715 Store[ax][ay-1] = element;
5716 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5717 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5718 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5719 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5724 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5725 Store[ax][ay+1] = element;
5726 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5727 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5728 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5729 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5734 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5735 element == EL_EXPANDABLE_WALL_ANY ||
5736 element == EL_EXPANDABLE_WALL)
5740 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5741 Store[ax-1][ay] = element;
5742 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5743 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5744 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5745 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5751 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5752 Store[ax+1][ay] = element;
5753 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5754 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5755 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5756 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5761 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5762 DrawLevelField(ax, ay);
5764 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5766 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5767 unten_massiv = TRUE;
5768 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5769 links_massiv = TRUE;
5770 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5771 rechts_massiv = TRUE;
5773 if (((oben_massiv && unten_massiv) ||
5774 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5775 element == EL_EXPANDABLE_WALL) &&
5776 ((links_massiv && rechts_massiv) ||
5777 element == EL_EXPANDABLE_WALL_VERTICAL))
5778 Feld[ax][ay] = EL_WALL;
5782 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5784 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5788 void CheckForDragon(int x, int y)
5791 boolean dragon_found = FALSE;
5792 static int xy[4][2] =
5800 for (i = 0; i < 4; i++)
5802 for (j = 0; j < 4; j++)
5804 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5806 if (IN_LEV_FIELD(xx, yy) &&
5807 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5809 if (Feld[xx][yy] == EL_DRAGON)
5810 dragon_found = TRUE;
5819 for (i = 0; i < 4; i++)
5821 for (j = 0; j < 3; j++)
5823 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5825 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5827 Feld[xx][yy] = EL_EMPTY;
5828 DrawLevelField(xx, yy);
5837 static void InitBuggyBase(int x, int y)
5839 int element = Feld[x][y];
5840 int activating_delay = FRAMES_PER_SECOND / 4;
5843 (element == EL_SP_BUGGY_BASE ?
5844 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5845 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5847 element == EL_SP_BUGGY_BASE_ACTIVE ?
5848 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5851 static void WarnBuggyBase(int x, int y)
5854 static int xy[4][2] =
5862 for (i = 0; i < 4; i++)
5864 int xx = x + xy[i][0], yy = y + xy[i][1];
5866 if (IS_PLAYER(xx, yy))
5868 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5875 static void InitTrap(int x, int y)
5877 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5880 static void ActivateTrap(int x, int y)
5882 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5885 static void ChangeActiveTrap(int x, int y)
5887 int graphic = IMG_TRAP_ACTIVE;
5889 /* if new animation frame was drawn, correct crumbled sand border */
5890 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5891 DrawLevelFieldCrumbledSand(x, y);
5894 static void ChangeElementNowExt(int x, int y, int target_element)
5896 /* check if element under player changes from accessible to unaccessible
5897 (needed for special case of dropping element which then changes) */
5898 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5899 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5906 Feld[x][y] = target_element;
5908 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5910 ResetGfxAnimation(x, y);
5911 ResetRandomAnimationValue(x, y);
5913 InitField(x, y, FALSE);
5914 if (CAN_MOVE(Feld[x][y]))
5917 DrawLevelField(x, y);
5919 if (GFX_CRUMBLED(Feld[x][y]))
5920 DrawLevelFieldCrumbledSandNeighbours(x, y);
5922 TestIfBadThingTouchesHero(x, y);
5923 TestIfPlayerTouchesCustomElement(x, y);
5924 TestIfElementTouchesCustomElement(x, y);
5926 if (ELEM_IS_PLAYER(target_element))
5927 RelocatePlayer(x, y, target_element);
5930 static boolean ChangeElementNow(int x, int y, int element, int page)
5932 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5934 /* always use default change event to prevent running into a loop */
5935 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5936 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5938 /* do not change already changed elements with same change event */
5940 if (Changed[x][y] & ChangeEvent[x][y])
5947 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5949 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5951 if (change->explode)
5958 if (change->use_content)
5960 boolean complete_change = TRUE;
5961 boolean can_change[3][3];
5964 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5966 boolean half_destructible;
5967 int ex = x + xx - 1;
5968 int ey = y + yy - 1;
5971 can_change[xx][yy] = TRUE;
5973 if (ex == x && ey == y) /* do not check changing element itself */
5976 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5978 can_change[xx][yy] = FALSE; /* do not change empty borders */
5983 if (!IN_LEV_FIELD(ex, ey))
5985 can_change[xx][yy] = FALSE;
5986 complete_change = FALSE;
5993 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5994 e = MovingOrBlocked2Element(ex, ey);
5996 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5998 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5999 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6000 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6002 can_change[xx][yy] = FALSE;
6003 complete_change = FALSE;
6007 if (!change->only_complete || complete_change)
6009 boolean something_has_changed = FALSE;
6011 if (change->only_complete && change->use_random_change &&
6012 RND(100) < change->random)
6015 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6017 int ex = x + xx - 1;
6018 int ey = y + yy - 1;
6020 if (can_change[xx][yy] && (!change->use_random_change ||
6021 RND(100) < change->random))
6023 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6024 RemoveMovingField(ex, ey);
6026 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6028 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6030 something_has_changed = TRUE;
6032 /* for symmetry reasons, freeze newly created border elements */
6033 if (ex != x || ey != y)
6034 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6038 if (something_has_changed)
6039 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6044 ChangeElementNowExt(x, y, change->target_element);
6046 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6052 static void ChangeElement(int x, int y, int page)
6054 int element = MovingOrBlocked2Element(x, y);
6055 struct ElementInfo *ei = &element_info[element];
6056 struct ElementChangeInfo *change = &ei->change_page[page];
6060 if (!CAN_CHANGE(element))
6063 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6064 x, y, element, element_info[element].token_name);
6065 printf("ChangeElement(): This should never happen!\n");
6071 if (ChangeDelay[x][y] == 0) /* initialize element change */
6073 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6074 RND(change->delay_random * change->delay_frames)) + 1;
6076 ResetGfxAnimation(x, y);
6077 ResetRandomAnimationValue(x, y);
6079 if (change->pre_change_function)
6080 change->pre_change_function(x, y);
6083 ChangeDelay[x][y]--;
6085 if (ChangeDelay[x][y] != 0) /* continue element change */
6087 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6089 if (IS_ANIMATED(graphic))
6090 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6092 if (change->change_function)
6093 change->change_function(x, y);
6095 else /* finish element change */
6097 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6099 page = ChangePage[x][y];
6100 ChangePage[x][y] = -1;
6103 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6105 ChangeDelay[x][y] = 1; /* try change after next move step */
6106 ChangePage[x][y] = page; /* remember page to use for change */
6111 if (ChangeElementNow(x, y, element, page))
6113 if (change->post_change_function)
6114 change->post_change_function(x, y);
6119 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6120 int trigger_element,
6126 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6129 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6131 int element = EL_CUSTOM_START + i;
6133 boolean change_element = FALSE;
6136 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6139 for (j = 0; j < element_info[element].num_change_pages; j++)
6141 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6143 if (change->can_change &&
6145 change->events & CH_EVENT_BIT(trigger_event) &&
6147 change->sides & trigger_side &&
6148 change->trigger_element == trigger_element)
6151 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6152 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6153 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6156 change_element = TRUE;
6163 if (!change_element)
6166 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6169 if (x == lx && y == ly) /* do not change trigger element itself */
6173 if (Feld[x][y] == element)
6175 ChangeDelay[x][y] = 1;
6176 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6177 ChangeElement(x, y, page);
6185 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6188 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6192 static boolean CheckElementSideChange(int x, int y, int element, int side,
6193 int trigger_event, int page)
6195 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6198 if (Feld[x][y] == EL_BLOCKED)
6200 Blocked2Moving(x, y, &x, &y);
6201 element = Feld[x][y];
6205 page = element_info[element].event_page_nr[trigger_event];
6207 if (!(element_info[element].change_page[page].sides & side))
6210 ChangeDelay[x][y] = 1;
6211 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6212 ChangeElement(x, y, page);
6217 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6219 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6222 static void PlayPlayerSound(struct PlayerInfo *player)
6224 int jx = player->jx, jy = player->jy;
6225 int element = player->element_nr;
6226 int last_action = player->last_action_waiting;
6227 int action = player->action_waiting;
6229 if (player->is_waiting)
6231 if (action != last_action)
6232 PlayLevelSoundElementAction(jx, jy, element, action);
6234 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6238 if (action != last_action)
6239 StopSound(element_info[element].sound[last_action]);
6241 if (last_action == ACTION_SLEEPING)
6242 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6246 static void PlayAllPlayersSound()
6250 for (i = 0; i < MAX_PLAYERS; i++)
6251 if (stored_player[i].active)
6252 PlayPlayerSound(&stored_player[i]);
6255 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6257 boolean last_waiting = player->is_waiting;
6258 int move_dir = player->MovDir;
6260 player->last_action_waiting = player->action_waiting;
6264 if (!last_waiting) /* not waiting -> waiting */
6266 player->is_waiting = TRUE;
6268 player->frame_counter_bored =
6270 game.player_boring_delay_fixed +
6271 SimpleRND(game.player_boring_delay_random);
6272 player->frame_counter_sleeping =
6274 game.player_sleeping_delay_fixed +
6275 SimpleRND(game.player_sleeping_delay_random);
6277 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6280 if (game.player_sleeping_delay_fixed +
6281 game.player_sleeping_delay_random > 0 &&
6282 player->anim_delay_counter == 0 &&
6283 player->post_delay_counter == 0 &&
6284 FrameCounter >= player->frame_counter_sleeping)
6285 player->is_sleeping = TRUE;
6286 else if (game.player_boring_delay_fixed +
6287 game.player_boring_delay_random > 0 &&
6288 FrameCounter >= player->frame_counter_bored)
6289 player->is_bored = TRUE;
6291 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6292 player->is_bored ? ACTION_BORING :
6295 if (player->is_sleeping)
6297 if (player->num_special_action_sleeping > 0)
6299 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6301 int last_special_action = player->special_action_sleeping;
6302 int num_special_action = player->num_special_action_sleeping;
6303 int special_action =
6304 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6305 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6306 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6307 last_special_action + 1 : ACTION_SLEEPING);
6308 int special_graphic =
6309 el_act_dir2img(player->element_nr, special_action, move_dir);
6311 player->anim_delay_counter =
6312 graphic_info[special_graphic].anim_delay_fixed +
6313 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6314 player->post_delay_counter =
6315 graphic_info[special_graphic].post_delay_fixed +
6316 SimpleRND(graphic_info[special_graphic].post_delay_random);
6318 player->special_action_sleeping = special_action;
6321 if (player->anim_delay_counter > 0)
6323 player->action_waiting = player->special_action_sleeping;
6324 player->anim_delay_counter--;
6326 else if (player->post_delay_counter > 0)
6328 player->post_delay_counter--;
6332 else if (player->is_bored)
6334 if (player->num_special_action_bored > 0)
6336 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6338 int special_action =
6339 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6340 int special_graphic =
6341 el_act_dir2img(player->element_nr, special_action, move_dir);
6343 player->anim_delay_counter =
6344 graphic_info[special_graphic].anim_delay_fixed +
6345 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6346 player->post_delay_counter =
6347 graphic_info[special_graphic].post_delay_fixed +
6348 SimpleRND(graphic_info[special_graphic].post_delay_random);
6350 player->special_action_bored = special_action;
6353 if (player->anim_delay_counter > 0)
6355 player->action_waiting = player->special_action_bored;
6356 player->anim_delay_counter--;
6358 else if (player->post_delay_counter > 0)
6360 player->post_delay_counter--;
6365 else if (last_waiting) /* waiting -> not waiting */
6367 player->is_waiting = FALSE;
6368 player->is_bored = FALSE;
6369 player->is_sleeping = FALSE;
6371 player->frame_counter_bored = -1;
6372 player->frame_counter_sleeping = -1;
6374 player->anim_delay_counter = 0;
6375 player->post_delay_counter = 0;
6377 player->action_waiting = ACTION_DEFAULT;
6379 player->special_action_bored = ACTION_DEFAULT;
6380 player->special_action_sleeping = ACTION_DEFAULT;
6385 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6388 static byte stored_player_action[MAX_PLAYERS];
6389 static int num_stored_actions = 0;
6391 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6392 int left = player_action & JOY_LEFT;
6393 int right = player_action & JOY_RIGHT;
6394 int up = player_action & JOY_UP;
6395 int down = player_action & JOY_DOWN;
6396 int button1 = player_action & JOY_BUTTON_1;
6397 int button2 = player_action & JOY_BUTTON_2;
6398 int dx = (left ? -1 : right ? 1 : 0);
6399 int dy = (up ? -1 : down ? 1 : 0);
6402 stored_player_action[player->index_nr] = 0;
6403 num_stored_actions++;
6407 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6410 if (!player->active || tape.pausing)
6416 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6420 snapped = SnapField(player, dx, dy);
6424 dropped = DropElement(player);
6426 moved = MovePlayer(player, dx, dy);
6429 if (tape.single_step && tape.recording && !tape.pausing)
6431 if (button1 || (dropped && !moved))
6433 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6434 SnapField(player, 0, 0); /* stop snapping */
6438 SetPlayerWaiting(player, FALSE);
6441 return player_action;
6443 stored_player_action[player->index_nr] = player_action;
6449 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6452 /* no actions for this player (no input at player's configured device) */
6454 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6455 SnapField(player, 0, 0);
6456 CheckGravityMovement(player);
6458 if (player->MovPos == 0)
6459 SetPlayerWaiting(player, TRUE);
6461 if (player->MovPos == 0) /* needed for tape.playing */
6462 player->is_moving = FALSE;
6468 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6470 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6472 TapeRecordAction(stored_player_action);
6473 num_stored_actions = 0;
6480 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6482 static byte stored_player_action[MAX_PLAYERS];
6483 static int num_stored_actions = 0;
6484 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6485 int left = player_action & JOY_LEFT;
6486 int right = player_action & JOY_RIGHT;
6487 int up = player_action & JOY_UP;
6488 int down = player_action & JOY_DOWN;
6489 int button1 = player_action & JOY_BUTTON_1;
6490 int button2 = player_action & JOY_BUTTON_2;
6491 int dx = (left ? -1 : right ? 1 : 0);
6492 int dy = (up ? -1 : down ? 1 : 0);
6494 stored_player_action[player->index_nr] = 0;
6495 num_stored_actions++;
6497 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6499 if (!player->active || tape.pausing)
6504 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6507 snapped = SnapField(player, dx, dy);
6511 dropped = DropElement(player);
6513 moved = MovePlayer(player, dx, dy);
6516 if (tape.single_step && tape.recording && !tape.pausing)
6518 if (button1 || (dropped && !moved))
6520 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6521 SnapField(player, 0, 0); /* stop snapping */
6525 stored_player_action[player->index_nr] = player_action;
6529 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6531 /* no actions for this player (no input at player's configured device) */
6533 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6534 SnapField(player, 0, 0);
6535 CheckGravityMovement(player);
6537 if (player->MovPos == 0)
6538 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6540 if (player->MovPos == 0) /* needed for tape.playing */
6541 player->is_moving = FALSE;
6544 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6546 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6548 TapeRecordAction(stored_player_action);
6549 num_stored_actions = 0;
6556 static unsigned long action_delay = 0;
6557 unsigned long action_delay_value;
6558 int magic_wall_x = 0, magic_wall_y = 0;
6559 int i, x, y, element, graphic;
6560 byte *recorded_player_action;
6561 byte summarized_player_action = 0;
6563 byte tape_action[MAX_PLAYERS];
6566 if (game_status != GAME_MODE_PLAYING)
6569 action_delay_value =
6570 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6572 if (tape.playing && tape.index_search && !tape.pausing)
6573 action_delay_value = 0;
6575 /* ---------- main game synchronization point ---------- */
6577 WaitUntilDelayReached(&action_delay, action_delay_value);
6579 if (network_playing && !network_player_action_received)
6583 printf("DEBUG: try to get network player actions in time\n");
6587 #if defined(PLATFORM_UNIX)
6588 /* last chance to get network player actions without main loop delay */
6592 if (game_status != GAME_MODE_PLAYING)
6595 if (!network_player_action_received)
6599 printf("DEBUG: failed to get network player actions in time\n");
6610 printf("::: getting new tape action [%d]\n", FrameCounter);
6613 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6615 for (i = 0; i < MAX_PLAYERS; i++)
6617 summarized_player_action |= stored_player[i].action;
6619 if (!network_playing)
6620 stored_player[i].effective_action = stored_player[i].action;
6623 #if defined(PLATFORM_UNIX)
6624 if (network_playing)
6625 SendToServer_MovePlayer(summarized_player_action);
6628 if (!options.network && !setup.team_mode)
6629 local_player->effective_action = summarized_player_action;
6631 for (i = 0; i < MAX_PLAYERS; i++)
6633 int actual_player_action = stored_player[i].effective_action;
6635 if (stored_player[i].programmed_action)
6636 actual_player_action = stored_player[i].programmed_action;
6638 if (recorded_player_action)
6639 actual_player_action = recorded_player_action[i];
6641 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6643 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6644 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6646 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6651 TapeRecordAction(tape_action);
6654 network_player_action_received = FALSE;
6656 ScrollScreen(NULL, SCROLL_GO_ON);
6662 for (i = 0; i < MAX_PLAYERS; i++)
6663 stored_player[i].Frame++;
6667 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6669 for (i = 0; i < MAX_PLAYERS; i++)
6671 struct PlayerInfo *player = &stored_player[i];
6675 if (player->active && player->is_pushing && player->is_moving &&
6678 ContinueMoving(x, y);
6680 /* continue moving after pushing (this is actually a bug) */
6681 if (!IS_MOVING(x, y))
6690 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6692 Changed[x][y] = CE_BITMASK_DEFAULT;
6693 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6696 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6698 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6699 printf("GameActions(): This should never happen!\n");
6701 ChangePage[x][y] = -1;
6706 if (WasJustMoving[x][y] > 0)
6707 WasJustMoving[x][y]--;
6708 if (WasJustFalling[x][y] > 0)
6709 WasJustFalling[x][y]--;
6714 /* reset finished pushing action (not done in ContinueMoving() to allow
6715 continous pushing animation for elements with zero push delay) */
6716 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6718 ResetGfxAnimation(x, y);
6719 DrawLevelField(x, y);
6724 if (IS_BLOCKED(x, y))
6728 Blocked2Moving(x, y, &oldx, &oldy);
6729 if (!IS_MOVING(oldx, oldy))
6731 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6732 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6733 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6734 printf("GameActions(): This should never happen!\n");
6740 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6742 element = Feld[x][y];
6744 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6746 graphic = el2img(element);
6752 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6754 element = graphic = 0;
6758 if (graphic_info[graphic].anim_global_sync)
6759 GfxFrame[x][y] = FrameCounter;
6761 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6762 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6763 ResetRandomAnimationValue(x, y);
6765 SetRandomAnimationValue(x, y);
6768 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6771 if (IS_INACTIVE(element))
6773 if (IS_ANIMATED(graphic))
6774 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6780 /* this may take place after moving, so 'element' may have changed */
6782 if (IS_CHANGING(x, y))
6784 if (IS_CHANGING(x, y) &&
6785 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6789 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6790 element_info[element].event_page_nr[CE_DELAY]);
6792 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6795 element = Feld[x][y];
6796 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6800 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6805 element = Feld[x][y];
6806 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6808 if (element == EL_MOLE)
6809 printf("::: %d, %d, %d [%d]\n",
6810 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6814 if (element == EL_YAMYAM)
6815 printf("::: %d, %d, %d\n",
6816 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6820 if (IS_ANIMATED(graphic) &&
6824 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6827 if (element == EL_BUG)
6828 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6832 if (element == EL_MOLE)
6833 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6837 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6838 EdelsteinFunkeln(x, y);
6840 else if ((element == EL_ACID ||
6841 element == EL_EXIT_OPEN ||
6842 element == EL_SP_EXIT_OPEN ||
6843 element == EL_SP_TERMINAL ||
6844 element == EL_SP_TERMINAL_ACTIVE ||
6845 element == EL_EXTRA_TIME ||
6846 element == EL_SHIELD_NORMAL ||
6847 element == EL_SHIELD_DEADLY) &&
6848 IS_ANIMATED(graphic))
6849 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6850 else if (IS_MOVING(x, y))
6851 ContinueMoving(x, y);
6852 else if (IS_ACTIVE_BOMB(element))
6853 CheckDynamite(x, y);
6855 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6856 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6858 else if (element == EL_AMOEBA_GROWING)
6859 AmoebeWaechst(x, y);
6860 else if (element == EL_AMOEBA_SHRINKING)
6861 AmoebaDisappearing(x, y);
6863 #if !USE_NEW_AMOEBA_CODE
6864 else if (IS_AMOEBALIVE(element))
6865 AmoebeAbleger(x, y);
6868 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6870 else if (element == EL_EXIT_CLOSED)
6872 else if (element == EL_SP_EXIT_CLOSED)
6874 else if (element == EL_EXPANDABLE_WALL_GROWING)
6876 else if (element == EL_EXPANDABLE_WALL ||
6877 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6878 element == EL_EXPANDABLE_WALL_VERTICAL ||
6879 element == EL_EXPANDABLE_WALL_ANY)
6881 else if (element == EL_FLAMES)
6882 CheckForDragon(x, y);
6884 else if (IS_AUTO_CHANGING(element))
6885 ChangeElement(x, y);
6887 else if (element == EL_EXPLOSION)
6888 ; /* drawing of correct explosion animation is handled separately */
6889 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6890 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6893 /* this may take place after moving, so 'element' may have changed */
6894 if (IS_AUTO_CHANGING(Feld[x][y]))
6895 ChangeElement(x, y);
6898 if (IS_BELT_ACTIVE(element))
6899 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6901 if (game.magic_wall_active)
6903 int jx = local_player->jx, jy = local_player->jy;
6905 /* play the element sound at the position nearest to the player */
6906 if ((element == EL_MAGIC_WALL_FULL ||
6907 element == EL_MAGIC_WALL_ACTIVE ||
6908 element == EL_MAGIC_WALL_EMPTYING ||
6909 element == EL_BD_MAGIC_WALL_FULL ||
6910 element == EL_BD_MAGIC_WALL_ACTIVE ||
6911 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6912 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6920 #if USE_NEW_AMOEBA_CODE
6921 /* new experimental amoeba growth stuff */
6923 if (!(FrameCounter % 8))
6926 static unsigned long random = 1684108901;
6928 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6931 x = (random >> 10) % lev_fieldx;
6932 y = (random >> 20) % lev_fieldy;
6934 x = RND(lev_fieldx);
6935 y = RND(lev_fieldy);
6937 element = Feld[x][y];
6939 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6940 if (!IS_PLAYER(x,y) &&
6941 (element == EL_EMPTY ||
6942 element == EL_SAND ||
6943 element == EL_QUICKSAND_EMPTY ||
6944 element == EL_ACID_SPLASH_LEFT ||
6945 element == EL_ACID_SPLASH_RIGHT))
6947 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6948 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6949 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6950 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6951 Feld[x][y] = EL_AMOEBA_DROP;
6954 random = random * 129 + 1;
6960 if (game.explosions_delayed)
6963 game.explosions_delayed = FALSE;
6965 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6967 element = Feld[x][y];
6969 if (ExplodeField[x][y])
6970 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6971 else if (element == EL_EXPLOSION)
6972 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6974 ExplodeField[x][y] = EX_NO_EXPLOSION;
6977 game.explosions_delayed = TRUE;
6980 if (game.magic_wall_active)
6982 if (!(game.magic_wall_time_left % 4))
6984 int element = Feld[magic_wall_x][magic_wall_y];
6986 if (element == EL_BD_MAGIC_WALL_FULL ||
6987 element == EL_BD_MAGIC_WALL_ACTIVE ||
6988 element == EL_BD_MAGIC_WALL_EMPTYING)
6989 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6991 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6994 if (game.magic_wall_time_left > 0)
6996 game.magic_wall_time_left--;
6997 if (!game.magic_wall_time_left)
6999 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7001 element = Feld[x][y];
7003 if (element == EL_MAGIC_WALL_ACTIVE ||
7004 element == EL_MAGIC_WALL_FULL)
7006 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7007 DrawLevelField(x, y);
7009 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7010 element == EL_BD_MAGIC_WALL_FULL)
7012 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7013 DrawLevelField(x, y);
7017 game.magic_wall_active = FALSE;
7022 if (game.light_time_left > 0)
7024 game.light_time_left--;
7026 if (game.light_time_left == 0)
7027 RedrawAllLightSwitchesAndInvisibleElements();
7030 if (game.timegate_time_left > 0)
7032 game.timegate_time_left--;
7034 if (game.timegate_time_left == 0)
7035 CloseAllOpenTimegates();
7038 for (i = 0; i < MAX_PLAYERS; i++)
7040 struct PlayerInfo *player = &stored_player[i];
7042 if (SHIELD_ON(player))
7044 if (player->shield_deadly_time_left)
7045 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7046 else if (player->shield_normal_time_left)
7047 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7051 if (TimeFrames >= FRAMES_PER_SECOND)
7056 for (i = 0; i < MAX_PLAYERS; i++)
7058 struct PlayerInfo *player = &stored_player[i];
7060 if (SHIELD_ON(player))
7062 player->shield_normal_time_left--;
7064 if (player->shield_deadly_time_left > 0)
7065 player->shield_deadly_time_left--;
7069 if (tape.recording || tape.playing)
7070 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7076 if (TimeLeft <= 10 && setup.time_limit)
7077 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7079 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7081 if (!TimeLeft && setup.time_limit)
7082 for (i = 0; i < MAX_PLAYERS; i++)
7083 KillHero(&stored_player[i]);
7085 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7086 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7090 PlayAllPlayersSound();
7092 if (options.debug) /* calculate frames per second */
7094 static unsigned long fps_counter = 0;
7095 static int fps_frames = 0;
7096 unsigned long fps_delay_ms = Counter() - fps_counter;
7100 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7102 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7105 fps_counter = Counter();
7108 redraw_mask |= REDRAW_FPS;
7112 if (stored_player[0].jx != stored_player[0].last_jx ||
7113 stored_player[0].jy != stored_player[0].last_jy)
7114 printf("::: %d, %d, %d, %d, %d\n",
7115 stored_player[0].MovDir,
7116 stored_player[0].MovPos,
7117 stored_player[0].GfxPos,
7118 stored_player[0].Frame,
7119 stored_player[0].StepFrame);
7126 for (i = 0; i < MAX_PLAYERS; i++)
7129 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7131 stored_player[i].Frame += move_frames;
7133 if (stored_player[i].MovPos != 0)
7134 stored_player[i].StepFrame += move_frames;
7136 if (stored_player[i].drop_delay > 0)
7137 stored_player[i].drop_delay--;
7142 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7144 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7146 local_player->show_envelope = 0;
7151 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7153 int min_x = x, min_y = y, max_x = x, max_y = y;
7156 for (i = 0; i < MAX_PLAYERS; i++)
7158 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7160 if (!stored_player[i].active || &stored_player[i] == player)
7163 min_x = MIN(min_x, jx);
7164 min_y = MIN(min_y, jy);
7165 max_x = MAX(max_x, jx);
7166 max_y = MAX(max_y, jy);
7169 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7172 static boolean AllPlayersInVisibleScreen()
7176 for (i = 0; i < MAX_PLAYERS; i++)
7178 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7180 if (!stored_player[i].active)
7183 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7190 void ScrollLevel(int dx, int dy)
7192 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7195 BlitBitmap(drawto_field, drawto_field,
7196 FX + TILEX * (dx == -1) - softscroll_offset,
7197 FY + TILEY * (dy == -1) - softscroll_offset,
7198 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7199 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7200 FX + TILEX * (dx == 1) - softscroll_offset,
7201 FY + TILEY * (dy == 1) - softscroll_offset);
7205 x = (dx == 1 ? BX1 : BX2);
7206 for (y = BY1; y <= BY2; y++)
7207 DrawScreenField(x, y);
7212 y = (dy == 1 ? BY1 : BY2);
7213 for (x = BX1; x <= BX2; x++)
7214 DrawScreenField(x, y);
7217 redraw_mask |= REDRAW_FIELD;
7220 static void CheckGravityMovement(struct PlayerInfo *player)
7222 if (game.gravity && !player->programmed_action)
7224 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7225 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7227 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7228 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7229 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7230 int jx = player->jx, jy = player->jy;
7231 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7232 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7233 int new_jx = jx + dx, new_jy = jy + dy;
7234 boolean field_under_player_is_free =
7235 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7236 boolean player_is_moving_to_valid_field =
7237 (IN_LEV_FIELD(new_jx, new_jy) &&
7238 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7239 Feld[new_jx][new_jy] == EL_SAND));
7240 /* !!! extend EL_SAND to anything diggable !!! */
7242 if (field_under_player_is_free &&
7243 !player_is_moving_to_valid_field &&
7244 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7245 player->programmed_action = MV_DOWN;
7251 -----------------------------------------------------------------------------
7252 dx, dy: direction (non-diagonal) to try to move the player to
7253 real_dx, real_dy: direction as read from input device (can be diagonal)
7256 boolean MovePlayerOneStep(struct PlayerInfo *player,
7257 int dx, int dy, int real_dx, int real_dy)
7260 static int change_sides[4][2] =
7262 /* enter side leave side */
7263 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7264 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7265 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7266 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7268 int move_direction = (dx == -1 ? MV_LEFT :
7269 dx == +1 ? MV_RIGHT :
7271 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7272 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7273 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7275 int jx = player->jx, jy = player->jy;
7276 int new_jx = jx + dx, new_jy = jy + dy;
7280 if (!player->active || (!dx && !dy))
7281 return MF_NO_ACTION;
7283 player->MovDir = (dx < 0 ? MV_LEFT :
7286 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7288 if (!IN_LEV_FIELD(new_jx, new_jy))
7289 return MF_NO_ACTION;
7291 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7292 return MF_NO_ACTION;
7295 element = MovingOrBlocked2Element(new_jx, new_jy);
7297 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7300 if (DONT_RUN_INTO(element))
7302 if (element == EL_ACID && dx == 0 && dy == 1)
7305 Feld[jx][jy] = EL_PLAYER_1;
7306 InitMovingField(jx, jy, MV_DOWN);
7307 Store[jx][jy] = EL_ACID;
7308 ContinueMoving(jx, jy);
7312 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7317 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7318 if (can_move != MF_MOVING)
7321 /* check if DigField() has caused relocation of the player */
7322 if (player->jx != jx || player->jy != jy)
7323 return MF_NO_ACTION;
7325 StorePlayer[jx][jy] = 0;
7326 player->last_jx = jx;
7327 player->last_jy = jy;
7328 player->jx = new_jx;
7329 player->jy = new_jy;
7330 StorePlayer[new_jx][new_jy] = player->element_nr;
7333 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7335 player->step_counter++;
7337 player->drop_delay = 0;
7339 PlayerVisit[jx][jy] = FrameCounter;
7341 ScrollPlayer(player, SCROLL_INIT);
7344 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7346 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7347 CE_OTHER_GETS_LEFT);
7348 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7349 CE_LEFT_BY_PLAYER, -1);
7352 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7354 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7355 enter_side, CE_OTHER_GETS_ENTERED);
7356 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7357 CE_ENTERED_BY_PLAYER, -1);
7364 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7366 int jx = player->jx, jy = player->jy;
7367 int old_jx = jx, old_jy = jy;
7368 int moved = MF_NO_ACTION;
7370 if (!player->active || (!dx && !dy))
7374 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7378 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7379 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7383 /* remove the last programmed player action */
7384 player->programmed_action = 0;
7388 /* should only happen if pre-1.2 tape recordings are played */
7389 /* this is only for backward compatibility */
7391 int original_move_delay_value = player->move_delay_value;
7394 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7398 /* scroll remaining steps with finest movement resolution */
7399 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7401 while (player->MovPos)
7403 ScrollPlayer(player, SCROLL_GO_ON);
7404 ScrollScreen(NULL, SCROLL_GO_ON);
7410 player->move_delay_value = original_move_delay_value;
7413 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7415 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7416 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7420 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7421 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7427 if (moved & MF_MOVING && !ScreenMovPos &&
7428 (player == local_player || !options.network))
7430 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7431 int offset = (setup.scroll_delay ? 3 : 0);
7433 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7435 /* actual player has left the screen -- scroll in that direction */
7436 if (jx != old_jx) /* player has moved horizontally */
7437 scroll_x += (jx - old_jx);
7438 else /* player has moved vertically */
7439 scroll_y += (jy - old_jy);
7443 if (jx != old_jx) /* player has moved horizontally */
7445 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7446 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7447 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7449 /* don't scroll over playfield boundaries */
7450 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7451 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7453 /* don't scroll more than one field at a time */
7454 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7456 /* don't scroll against the player's moving direction */
7457 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7458 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7459 scroll_x = old_scroll_x;
7461 else /* player has moved vertically */
7463 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7464 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7465 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7467 /* don't scroll over playfield boundaries */
7468 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7469 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7471 /* don't scroll more than one field at a time */
7472 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7474 /* don't scroll against the player's moving direction */
7475 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7476 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7477 scroll_y = old_scroll_y;
7481 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7483 if (!options.network && !AllPlayersInVisibleScreen())
7485 scroll_x = old_scroll_x;
7486 scroll_y = old_scroll_y;
7490 ScrollScreen(player, SCROLL_INIT);
7491 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7498 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7500 if (!(moved & MF_MOVING) && !player->is_pushing)
7505 player->StepFrame = 0;
7507 if (moved & MF_MOVING)
7509 if (old_jx != jx && old_jy == jy)
7510 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7511 else if (old_jx == jx && old_jy != jy)
7512 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7514 DrawLevelField(jx, jy); /* for "crumbled sand" */
7516 player->last_move_dir = player->MovDir;
7517 player->is_moving = TRUE;
7519 player->is_snapping = FALSE;
7523 player->is_switching = FALSE;
7529 static int change_sides[4][2] =
7531 /* enter side leave side */
7532 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7533 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7534 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7535 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7537 int move_direction = player->MovDir;
7538 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7539 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7542 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7544 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7545 leave_side, CE_OTHER_GETS_LEFT);
7546 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7547 leave_side, CE_LEFT_BY_PLAYER, -1);
7550 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7552 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7553 enter_side, CE_OTHER_GETS_ENTERED);
7554 CheckElementSideChange(jx, jy, Feld[jx][jy],
7555 enter_side, CE_ENTERED_BY_PLAYER, -1);
7566 CheckGravityMovement(player);
7569 player->last_move_dir = MV_NO_MOVING;
7571 player->is_moving = FALSE;
7574 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7576 TestIfHeroTouchesBadThing(jx, jy);
7577 TestIfPlayerTouchesCustomElement(jx, jy);
7580 if (!player->active)
7586 void ScrollPlayer(struct PlayerInfo *player, int mode)
7588 int jx = player->jx, jy = player->jy;
7589 int last_jx = player->last_jx, last_jy = player->last_jy;
7590 int move_stepsize = TILEX / player->move_delay_value;
7592 if (!player->active || !player->MovPos)
7595 if (mode == SCROLL_INIT)
7597 player->actual_frame_counter = FrameCounter;
7598 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7600 if (Feld[last_jx][last_jy] == EL_EMPTY)
7601 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7608 else if (!FrameReached(&player->actual_frame_counter, 1))
7611 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7612 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7614 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7615 Feld[last_jx][last_jy] = EL_EMPTY;
7617 /* before DrawPlayer() to draw correct player graphic for this case */
7618 if (player->MovPos == 0)
7619 CheckGravityMovement(player);
7622 DrawPlayer(player); /* needed here only to cleanup last field */
7625 if (player->MovPos == 0) /* player reached destination field */
7627 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7629 /* continue with normal speed after quickly moving through gate */
7630 HALVE_PLAYER_SPEED(player);
7632 /* be able to make the next move without delay */
7633 player->move_delay = 0;
7636 player->last_jx = jx;
7637 player->last_jy = jy;
7639 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7640 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7641 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7643 DrawPlayer(player); /* needed here only to cleanup last field */
7646 if (local_player->friends_still_needed == 0 ||
7647 IS_SP_ELEMENT(Feld[jx][jy]))
7648 player->LevelSolved = player->GameOver = TRUE;
7651 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7653 TestIfHeroTouchesBadThing(jx, jy);
7654 TestIfPlayerTouchesCustomElement(jx, jy);
7656 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7659 if (!player->active)
7663 if (tape.single_step && tape.recording && !tape.pausing &&
7664 !player->programmed_action)
7665 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7669 void ScrollScreen(struct PlayerInfo *player, int mode)
7671 static unsigned long screen_frame_counter = 0;
7673 if (mode == SCROLL_INIT)
7675 /* set scrolling step size according to actual player's moving speed */
7676 ScrollStepSize = TILEX / player->move_delay_value;
7678 screen_frame_counter = FrameCounter;
7679 ScreenMovDir = player->MovDir;
7680 ScreenMovPos = player->MovPos;
7681 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7684 else if (!FrameReached(&screen_frame_counter, 1))
7689 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7690 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7691 redraw_mask |= REDRAW_FIELD;
7694 ScreenMovDir = MV_NO_MOVING;
7697 void TestIfPlayerTouchesCustomElement(int x, int y)
7699 static int xy[4][2] =
7706 static int change_sides[4][2] =
7708 /* center side border side */
7709 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7710 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7711 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7712 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7714 static int touch_dir[4] =
7721 int center_element = Feld[x][y]; /* should always be non-moving! */
7724 for (i = 0; i < 4; i++)
7726 int xx = x + xy[i][0];
7727 int yy = y + xy[i][1];
7728 int center_side = change_sides[i][0];
7729 int border_side = change_sides[i][1];
7732 if (!IN_LEV_FIELD(xx, yy))
7735 if (IS_PLAYER(x, y))
7737 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7738 border_element = Feld[xx][yy]; /* may be moving! */
7739 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7740 border_element = Feld[xx][yy];
7741 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7742 border_element = MovingOrBlocked2Element(xx, yy);
7744 continue; /* center and border element do not touch */
7746 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7747 CE_OTHER_GETS_TOUCHED);
7748 CheckElementSideChange(xx, yy, border_element, border_side,
7749 CE_TOUCHED_BY_PLAYER, -1);
7751 else if (IS_PLAYER(xx, yy))
7753 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7755 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7757 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7758 continue; /* center and border element do not touch */
7761 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7762 CE_OTHER_GETS_TOUCHED);
7763 CheckElementSideChange(x, y, center_element, center_side,
7764 CE_TOUCHED_BY_PLAYER, -1);
7771 void TestIfElementTouchesCustomElement(int x, int y)
7773 static int xy[4][2] =
7780 static int change_sides[4][2] =
7782 /* center side border side */
7783 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7784 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7785 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7786 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7788 static int touch_dir[4] =
7795 boolean change_center_element = FALSE;
7796 int center_element_change_page = 0;
7797 int center_element = Feld[x][y]; /* should always be non-moving! */
7800 for (i = 0; i < 4; i++)
7802 int xx = x + xy[i][0];
7803 int yy = y + xy[i][1];
7804 int center_side = change_sides[i][0];
7805 int border_side = change_sides[i][1];
7808 if (!IN_LEV_FIELD(xx, yy))
7811 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7812 border_element = Feld[xx][yy]; /* may be moving! */
7813 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7814 border_element = Feld[xx][yy];
7815 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7816 border_element = MovingOrBlocked2Element(xx, yy);
7818 continue; /* center and border element do not touch */
7820 /* check for change of center element (but change it only once) */
7821 if (IS_CUSTOM_ELEMENT(center_element) &&
7822 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7823 !change_center_element)
7825 for (j = 0; j < element_info[center_element].num_change_pages; j++)
7827 struct ElementChangeInfo *change =
7828 &element_info[center_element].change_page[j];
7830 if (change->can_change &&
7831 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7832 change->sides & border_side &&
7833 change->trigger_element == border_element)
7835 change_center_element = TRUE;
7836 center_element_change_page = j;
7843 /* check for change of border element */
7844 if (IS_CUSTOM_ELEMENT(border_element) &&
7845 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7847 for (j = 0; j < element_info[border_element].num_change_pages; j++)
7849 struct ElementChangeInfo *change =
7850 &element_info[border_element].change_page[j];
7852 if (change->can_change &&
7853 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7854 change->sides & center_side &&
7855 change->trigger_element == center_element)
7857 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7858 CE_OTHER_IS_TOUCHING, j);
7865 if (change_center_element)
7866 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7867 CE_OTHER_IS_TOUCHING, center_element_change_page);
7870 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7872 int i, kill_x = -1, kill_y = -1;
7873 static int test_xy[4][2] =
7880 static int test_dir[4] =
7888 for (i = 0; i < 4; i++)
7890 int test_x, test_y, test_move_dir, test_element;
7892 test_x = good_x + test_xy[i][0];
7893 test_y = good_y + test_xy[i][1];
7894 if (!IN_LEV_FIELD(test_x, test_y))
7898 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7901 test_element = Feld[test_x][test_y];
7903 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7906 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7907 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7909 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7910 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7918 if (kill_x != -1 || kill_y != -1)
7920 if (IS_PLAYER(good_x, good_y))
7922 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7924 if (player->shield_deadly_time_left > 0)
7925 Bang(kill_x, kill_y);
7926 else if (!PLAYER_PROTECTED(good_x, good_y))
7930 Bang(good_x, good_y);
7934 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7936 int i, kill_x = -1, kill_y = -1;
7937 int bad_element = Feld[bad_x][bad_y];
7938 static int test_xy[4][2] =
7945 static int touch_dir[4] =
7952 static int test_dir[4] =
7960 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7963 for (i = 0; i < 4; i++)
7965 int test_x, test_y, test_move_dir, test_element;
7967 test_x = bad_x + test_xy[i][0];
7968 test_y = bad_y + test_xy[i][1];
7969 if (!IN_LEV_FIELD(test_x, test_y))
7973 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7975 test_element = Feld[test_x][test_y];
7977 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7978 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7980 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7981 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7983 /* good thing is player or penguin that does not move away */
7984 if (IS_PLAYER(test_x, test_y))
7986 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7988 if (bad_element == EL_ROBOT && player->is_moving)
7989 continue; /* robot does not kill player if he is moving */
7991 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7993 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7994 continue; /* center and border element do not touch */
8001 else if (test_element == EL_PENGUIN)
8010 if (kill_x != -1 || kill_y != -1)
8012 if (IS_PLAYER(kill_x, kill_y))
8014 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8016 if (player->shield_deadly_time_left > 0)
8018 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8022 Bang(kill_x, kill_y);
8026 void TestIfHeroTouchesBadThing(int x, int y)
8028 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8031 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8033 TestIfGoodThingHitsBadThing(x, y, move_dir);
8036 void TestIfBadThingTouchesHero(int x, int y)
8038 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8041 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8043 TestIfBadThingHitsGoodThing(x, y, move_dir);
8046 void TestIfFriendTouchesBadThing(int x, int y)
8048 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8051 void TestIfBadThingTouchesFriend(int x, int y)
8053 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8056 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8058 int i, kill_x = bad_x, kill_y = bad_y;
8059 static int xy[4][2] =
8067 for (i = 0; i < 4; i++)
8071 x = bad_x + xy[i][0];
8072 y = bad_y + xy[i][1];
8073 if (!IN_LEV_FIELD(x, y))
8076 element = Feld[x][y];
8077 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8078 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8086 if (kill_x != bad_x || kill_y != bad_y)
8090 void KillHero(struct PlayerInfo *player)
8092 int jx = player->jx, jy = player->jy;
8094 if (!player->active)
8097 /* remove accessible field at the player's position */
8098 Feld[jx][jy] = EL_EMPTY;
8100 /* deactivate shield (else Bang()/Explode() would not work right) */
8101 player->shield_normal_time_left = 0;
8102 player->shield_deadly_time_left = 0;
8108 static void KillHeroUnlessProtected(int x, int y)
8110 if (!PLAYER_PROTECTED(x, y))
8111 KillHero(PLAYERINFO(x, y));
8114 void BuryHero(struct PlayerInfo *player)
8116 int jx = player->jx, jy = player->jy;
8118 if (!player->active)
8122 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8124 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8126 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8128 player->GameOver = TRUE;
8132 void RemoveHero(struct PlayerInfo *player)
8134 int jx = player->jx, jy = player->jy;
8135 int i, found = FALSE;
8137 player->present = FALSE;
8138 player->active = FALSE;
8140 if (!ExplodeField[jx][jy])
8141 StorePlayer[jx][jy] = 0;
8143 for (i = 0; i < MAX_PLAYERS; i++)
8144 if (stored_player[i].active)
8148 AllPlayersGone = TRUE;
8155 =============================================================================
8156 checkDiagonalPushing()
8157 -----------------------------------------------------------------------------
8158 check if diagonal input device direction results in pushing of object
8159 (by checking if the alternative direction is walkable, diggable, ...)
8160 =============================================================================
8163 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8164 int x, int y, int real_dx, int real_dy)
8166 int jx, jy, dx, dy, xx, yy;
8168 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8171 /* diagonal direction: check alternative direction */
8176 xx = jx + (dx == 0 ? real_dx : 0);
8177 yy = jy + (dy == 0 ? real_dy : 0);
8179 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8183 =============================================================================
8185 -----------------------------------------------------------------------------
8186 x, y: field next to player (non-diagonal) to try to dig to
8187 real_dx, real_dy: direction as read from input device (can be diagonal)
8188 =============================================================================
8191 int DigField(struct PlayerInfo *player,
8192 int x, int y, int real_dx, int real_dy, int mode)
8194 static int change_sides[4] =
8196 CH_SIDE_RIGHT, /* moving left */
8197 CH_SIDE_LEFT, /* moving right */
8198 CH_SIDE_BOTTOM, /* moving up */
8199 CH_SIDE_TOP, /* moving down */
8201 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8202 int jx = player->jx, jy = player->jy;
8203 int dx = x - jx, dy = y - jy;
8204 int nextx = x + dx, nexty = y + dy;
8205 int move_direction = (dx == -1 ? MV_LEFT :
8206 dx == +1 ? MV_RIGHT :
8208 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8209 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8212 if (player->MovPos == 0)
8214 player->is_digging = FALSE;
8215 player->is_collecting = FALSE;
8218 if (player->MovPos == 0) /* last pushing move finished */
8219 player->is_pushing = FALSE;
8221 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8223 player->is_switching = FALSE;
8224 player->push_delay = 0;
8226 return MF_NO_ACTION;
8229 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8230 return MF_NO_ACTION;
8233 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8235 if (IS_TUBE(Feld[jx][jy]) ||
8236 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8240 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8241 int tube_leave_directions[][2] =
8243 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8244 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8245 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8246 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8247 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8248 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8249 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8250 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8251 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8252 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8253 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8254 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8257 while (tube_leave_directions[i][0] != tube_element)
8260 if (tube_leave_directions[i][0] == -1) /* should not happen */
8264 if (!(tube_leave_directions[i][1] & move_direction))
8265 return MF_NO_ACTION; /* tube has no opening in this direction */
8268 element = Feld[x][y];
8270 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8271 game.engine_version >= VERSION_IDENT(2,2,0,0))
8272 return MF_NO_ACTION;
8276 case EL_SP_PORT_LEFT:
8277 case EL_SP_PORT_RIGHT:
8279 case EL_SP_PORT_DOWN:
8280 case EL_SP_PORT_HORIZONTAL:
8281 case EL_SP_PORT_VERTICAL:
8282 case EL_SP_PORT_ANY:
8283 case EL_SP_GRAVITY_PORT_LEFT:
8284 case EL_SP_GRAVITY_PORT_RIGHT:
8285 case EL_SP_GRAVITY_PORT_UP:
8286 case EL_SP_GRAVITY_PORT_DOWN:
8288 element != EL_SP_PORT_LEFT &&
8289 element != EL_SP_GRAVITY_PORT_LEFT &&
8290 element != EL_SP_PORT_HORIZONTAL &&
8291 element != EL_SP_PORT_ANY) ||
8293 element != EL_SP_PORT_RIGHT &&
8294 element != EL_SP_GRAVITY_PORT_RIGHT &&
8295 element != EL_SP_PORT_HORIZONTAL &&
8296 element != EL_SP_PORT_ANY) ||
8298 element != EL_SP_PORT_UP &&
8299 element != EL_SP_GRAVITY_PORT_UP &&
8300 element != EL_SP_PORT_VERTICAL &&
8301 element != EL_SP_PORT_ANY) ||
8303 element != EL_SP_PORT_DOWN &&
8304 element != EL_SP_GRAVITY_PORT_DOWN &&
8305 element != EL_SP_PORT_VERTICAL &&
8306 element != EL_SP_PORT_ANY) ||
8307 !IN_LEV_FIELD(nextx, nexty) ||
8308 !IS_FREE(nextx, nexty))
8309 return MF_NO_ACTION;
8311 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8312 element == EL_SP_GRAVITY_PORT_RIGHT ||
8313 element == EL_SP_GRAVITY_PORT_UP ||
8314 element == EL_SP_GRAVITY_PORT_DOWN)
8315 game.gravity = !game.gravity;
8317 /* automatically move to the next field with double speed */
8318 player->programmed_action = move_direction;
8319 DOUBLE_PLAYER_SPEED(player);
8321 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8325 case EL_TUBE_VERTICAL:
8326 case EL_TUBE_HORIZONTAL:
8327 case EL_TUBE_VERTICAL_LEFT:
8328 case EL_TUBE_VERTICAL_RIGHT:
8329 case EL_TUBE_HORIZONTAL_UP:
8330 case EL_TUBE_HORIZONTAL_DOWN:
8331 case EL_TUBE_LEFT_UP:
8332 case EL_TUBE_LEFT_DOWN:
8333 case EL_TUBE_RIGHT_UP:
8334 case EL_TUBE_RIGHT_DOWN:
8337 int tube_enter_directions[][2] =
8339 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8340 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8341 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8342 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8343 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8344 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8345 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8346 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8347 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8348 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8349 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8350 { -1, MV_NO_MOVING }
8353 while (tube_enter_directions[i][0] != element)
8356 if (tube_enter_directions[i][0] == -1) /* should not happen */
8360 if (!(tube_enter_directions[i][1] & move_direction))
8361 return MF_NO_ACTION; /* tube has no opening in this direction */
8363 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8369 if (IS_WALKABLE(element))
8371 int sound_action = ACTION_WALKING;
8373 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8375 if (!player->key[element - EL_GATE_1])
8376 return MF_NO_ACTION;
8378 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8380 if (!player->key[element - EL_GATE_1_GRAY])
8381 return MF_NO_ACTION;
8383 else if (element == EL_EXIT_OPEN ||
8384 element == EL_SP_EXIT_OPEN ||
8385 element == EL_SP_EXIT_OPENING)
8387 sound_action = ACTION_PASSING; /* player is passing exit */
8389 else if (element == EL_EMPTY)
8391 sound_action = ACTION_MOVING; /* nothing to walk on */
8394 /* play sound from background or player, whatever is available */
8395 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8396 PlayLevelSoundElementAction(x, y, element, sound_action);
8398 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8402 else if (IS_PASSABLE(element))
8404 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8405 return MF_NO_ACTION;
8408 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8409 return MF_NO_ACTION;
8412 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8414 if (!player->key[element - EL_EM_GATE_1])
8415 return MF_NO_ACTION;
8417 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8419 if (!player->key[element - EL_EM_GATE_1_GRAY])
8420 return MF_NO_ACTION;
8423 /* automatically move to the next field with double speed */
8424 player->programmed_action = move_direction;
8425 DOUBLE_PLAYER_SPEED(player);
8427 PlayLevelSoundAction(x, y, ACTION_PASSING);
8431 else if (IS_DIGGABLE(element))
8435 if (mode != DF_SNAP)
8438 GfxElement[x][y] = GFX_ELEMENT(element);
8441 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8443 player->is_digging = TRUE;
8446 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8448 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8451 if (mode == DF_SNAP)
8452 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8457 else if (IS_COLLECTIBLE(element))
8461 if (mode != DF_SNAP)
8463 GfxElement[x][y] = element;
8464 player->is_collecting = TRUE;
8467 if (element == EL_SPEED_PILL)
8468 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8469 else if (element == EL_EXTRA_TIME && level.time > 0)
8472 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8474 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8476 player->shield_normal_time_left += 10;
8477 if (element == EL_SHIELD_DEADLY)
8478 player->shield_deadly_time_left += 10;
8480 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8482 if (player->inventory_size < MAX_INVENTORY_SIZE)
8483 player->inventory_element[player->inventory_size++] = element;
8485 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8486 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8488 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8490 player->dynabomb_count++;
8491 player->dynabombs_left++;
8493 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8495 player->dynabomb_size++;
8497 else if (element == EL_DYNABOMB_INCREASE_POWER)
8499 player->dynabomb_xl = TRUE;
8501 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8502 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8504 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8505 element - EL_KEY_1 : element - EL_EM_KEY_1);
8507 player->key[key_nr] = TRUE;
8509 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8510 el2edimg(EL_KEY_1 + key_nr));
8511 redraw_mask |= REDRAW_DOOR_1;
8513 else if (IS_ENVELOPE(element))
8516 player->show_envelope = element;
8518 ShowEnvelope(element - EL_ENVELOPE_1);
8521 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8525 for (i = 0; i < element_info[element].collect_count; i++)
8526 if (player->inventory_size < MAX_INVENTORY_SIZE)
8527 player->inventory_element[player->inventory_size++] = element;
8529 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8530 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8532 else if (element_info[element].collect_count > 0)
8534 local_player->gems_still_needed -=
8535 element_info[element].collect_count;
8536 if (local_player->gems_still_needed < 0)
8537 local_player->gems_still_needed = 0;
8539 DrawText(DX_EMERALDS, DY_EMERALDS,
8540 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8543 RaiseScoreElement(element);
8544 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8546 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8549 if (mode == DF_SNAP)
8550 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8555 else if (IS_PUSHABLE(element))
8557 if (mode == DF_SNAP && element != EL_BD_ROCK)
8558 return MF_NO_ACTION;
8560 if (CAN_FALL(element) && dy)
8561 return MF_NO_ACTION;
8563 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8564 !(element == EL_SPRING && use_spring_bug))
8565 return MF_NO_ACTION;
8568 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8569 ((move_direction & MV_VERTICAL &&
8570 ((element_info[element].move_pattern & MV_LEFT &&
8571 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8572 (element_info[element].move_pattern & MV_RIGHT &&
8573 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8574 (move_direction & MV_HORIZONTAL &&
8575 ((element_info[element].move_pattern & MV_UP &&
8576 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8577 (element_info[element].move_pattern & MV_DOWN &&
8578 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8579 return MF_NO_ACTION;
8583 /* do not push elements already moving away faster than player */
8584 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8585 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8586 return MF_NO_ACTION;
8588 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8589 return MF_NO_ACTION;
8593 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8595 if (player->push_delay_value == -1)
8596 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8598 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8600 if (!player->is_pushing)
8601 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8605 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8606 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8607 !player_is_pushing))
8608 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8611 if (!player->is_pushing &&
8612 game.engine_version >= VERSION_IDENT(2,2,0,7))
8613 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8617 printf("::: push delay: %ld [%d, %d] [%d]\n",
8618 player->push_delay_value, FrameCounter, game.engine_version,
8619 player->is_pushing);
8622 player->is_pushing = TRUE;
8624 if (!(IN_LEV_FIELD(nextx, nexty) &&
8625 (IS_FREE(nextx, nexty) ||
8626 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8627 IS_SB_ELEMENT(element)))))
8628 return MF_NO_ACTION;
8630 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8631 return MF_NO_ACTION;
8633 if (player->push_delay == 0) /* new pushing; restart delay */
8634 player->push_delay = FrameCounter;
8636 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8637 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8638 element != EL_SPRING && element != EL_BALLOON)
8640 /* make sure that there is no move delay before next try to push */
8641 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8642 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8644 return MF_NO_ACTION;
8648 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8651 if (IS_SB_ELEMENT(element))
8653 if (element == EL_SOKOBAN_FIELD_FULL)
8655 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8656 local_player->sokobanfields_still_needed++;
8659 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8661 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8662 local_player->sokobanfields_still_needed--;
8665 Feld[x][y] = EL_SOKOBAN_OBJECT;
8667 if (Back[x][y] == Back[nextx][nexty])
8668 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8669 else if (Back[x][y] != 0)
8670 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8673 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8676 if (local_player->sokobanfields_still_needed == 0 &&
8677 game.emulation == EMU_SOKOBAN)
8679 player->LevelSolved = player->GameOver = TRUE;
8680 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8684 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8686 InitMovingField(x, y, move_direction);
8687 GfxAction[x][y] = ACTION_PUSHING;
8689 if (mode == DF_SNAP)
8690 ContinueMoving(x, y);
8692 MovPos[x][y] = (dx != 0 ? dx : dy);
8694 Pushed[x][y] = TRUE;
8695 Pushed[nextx][nexty] = TRUE;
8697 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8698 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8700 player->push_delay_value = -1; /* get new value later */
8702 CheckTriggeredElementSideChange(x, y, element, dig_side,
8703 CE_OTHER_GETS_PUSHED);
8704 CheckElementSideChange(x, y, element, dig_side,
8705 CE_PUSHED_BY_PLAYER, -1);
8709 else if (IS_SWITCHABLE(element))
8711 if (PLAYER_SWITCHING(player, x, y))
8714 player->is_switching = TRUE;
8715 player->switch_x = x;
8716 player->switch_y = y;
8718 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8720 if (element == EL_ROBOT_WHEEL)
8722 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8726 DrawLevelField(x, y);
8728 else if (element == EL_SP_TERMINAL)
8732 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8734 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8736 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8737 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8740 else if (IS_BELT_SWITCH(element))
8742 ToggleBeltSwitch(x, y);
8744 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8745 element == EL_SWITCHGATE_SWITCH_DOWN)
8747 ToggleSwitchgateSwitch(x, y);
8749 else if (element == EL_LIGHT_SWITCH ||
8750 element == EL_LIGHT_SWITCH_ACTIVE)
8752 ToggleLightSwitch(x, y);
8755 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8756 SND_LIGHT_SWITCH_ACTIVATING :
8757 SND_LIGHT_SWITCH_DEACTIVATING);
8760 else if (element == EL_TIMEGATE_SWITCH)
8762 ActivateTimegateSwitch(x, y);
8764 else if (element == EL_BALLOON_SWITCH_LEFT ||
8765 element == EL_BALLOON_SWITCH_RIGHT ||
8766 element == EL_BALLOON_SWITCH_UP ||
8767 element == EL_BALLOON_SWITCH_DOWN ||
8768 element == EL_BALLOON_SWITCH_ANY)
8770 if (element == EL_BALLOON_SWITCH_ANY)
8771 game.balloon_dir = move_direction;
8773 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8774 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8775 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8776 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8779 else if (element == EL_LAMP)
8781 Feld[x][y] = EL_LAMP_ACTIVE;
8782 local_player->lights_still_needed--;
8784 DrawLevelField(x, y);
8786 else if (element == EL_TIME_ORB_FULL)
8788 Feld[x][y] = EL_TIME_ORB_EMPTY;
8790 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8792 DrawLevelField(x, y);
8795 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8803 if (!PLAYER_SWITCHING(player, x, y))
8805 player->is_switching = TRUE;
8806 player->switch_x = x;
8807 player->switch_y = y;
8809 CheckTriggeredElementSideChange(x, y, element, dig_side,
8810 CE_OTHER_IS_SWITCHING);
8811 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8814 CheckTriggeredElementSideChange(x, y, element, dig_side,
8815 CE_OTHER_GETS_PRESSED);
8816 CheckElementSideChange(x, y, element, dig_side,
8817 CE_PRESSED_BY_PLAYER, -1);
8820 return MF_NO_ACTION;
8823 player->push_delay = 0;
8825 if (Feld[x][y] != element) /* really digged/collected something */
8826 player->is_collecting = !player->is_digging;
8831 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8833 int jx = player->jx, jy = player->jy;
8834 int x = jx + dx, y = jy + dy;
8835 int snap_direction = (dx == -1 ? MV_LEFT :
8836 dx == +1 ? MV_RIGHT :
8838 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8840 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8843 if (!player->active || !IN_LEV_FIELD(x, y))
8851 if (player->MovPos == 0)
8852 player->is_pushing = FALSE;
8854 player->is_snapping = FALSE;
8856 if (player->MovPos == 0)
8858 player->is_moving = FALSE;
8859 player->is_digging = FALSE;
8860 player->is_collecting = FALSE;
8866 if (player->is_snapping)
8869 player->MovDir = snap_direction;
8871 player->is_moving = FALSE;
8872 player->is_digging = FALSE;
8873 player->is_collecting = FALSE;
8875 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8878 player->is_snapping = TRUE;
8880 player->is_moving = FALSE;
8881 player->is_digging = FALSE;
8882 player->is_collecting = FALSE;
8884 DrawLevelField(x, y);
8890 boolean DropElement(struct PlayerInfo *player)
8892 int jx = player->jx, jy = player->jy;
8896 if (!player->active || player->MovPos || player->drop_delay > 0)
8899 old_element = Feld[jx][jy];
8901 /* check if player has anything that can be dropped */
8902 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8905 /* check if anything can be dropped at the current position */
8906 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8909 /* collected custom elements can only be dropped on empty fields */
8910 if (player->inventory_size > 0 &&
8911 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8912 && old_element != EL_EMPTY)
8915 if (old_element != EL_EMPTY)
8916 Back[jx][jy] = old_element; /* store old element on this field */
8920 /* !!! CHANGE !!! CHANGE !!! */
8923 MovDelay[jx][jy] = 96;
8926 /* !!! CHANGE !!! CHANGE !!! */
8930 ResetGfxAnimation(jx, jy);
8931 ResetRandomAnimationValue(jx, jy);
8933 if (player->inventory_size > 0)
8935 new_element = player->inventory_element[--player->inventory_size];
8937 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8938 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8941 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8942 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8944 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8945 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8947 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8949 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8950 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8952 TestIfElementTouchesCustomElement(jx, jy);
8954 else /* player is dropping a dyna bomb */
8956 player->dynabombs_left--;
8959 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8961 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8962 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8964 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8966 MovDelay[jx][jy] = 96;
8972 InitField(jx, jy, FALSE);
8973 if (CAN_MOVE(Feld[jx][jy]))
8976 new_element = Feld[jx][jy];
8978 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
8979 element_info[new_element].move_pattern == MV_PROJECTILE)
8981 int direction, dx, dy, nextx, nexty;
8983 if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
8984 MovDir[jx][jy] = player->MovDir;
8986 direction = MovDir[jx][jy];
8987 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8988 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8992 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
8994 InitMovingField(jx, jy, MovDir[jx][jy]);
8995 ContinueMoving(jx, jy);
8999 Changed[jx][jy] = 0; /* allow another change */
9000 CheckElementSideChange(jx, jy, new_element,
9001 direction, CE_COLLISION_ACTIVE, -1);
9007 player->drop_delay = 8 + 8 + 8;
9014 /* ------------------------------------------------------------------------- */
9015 /* game sound playing functions */
9016 /* ------------------------------------------------------------------------- */
9018 static int *loop_sound_frame = NULL;
9019 static int *loop_sound_volume = NULL;
9021 void InitPlayLevelSound()
9023 int num_sounds = getSoundListSize();
9025 checked_free(loop_sound_frame);
9026 checked_free(loop_sound_volume);
9028 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9029 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9032 static void PlayLevelSound(int x, int y, int nr)
9034 int sx = SCREENX(x), sy = SCREENY(y);
9035 int volume, stereo_position;
9036 int max_distance = 8;
9037 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9039 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9040 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9043 if (!IN_LEV_FIELD(x, y) ||
9044 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9045 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9048 volume = SOUND_MAX_VOLUME;
9050 if (!IN_SCR_FIELD(sx, sy))
9052 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9053 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9055 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9058 stereo_position = (SOUND_MAX_LEFT +
9059 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9060 (SCR_FIELDX + 2 * max_distance));
9062 if (IS_LOOP_SOUND(nr))
9064 /* This assures that quieter loop sounds do not overwrite louder ones,
9065 while restarting sound volume comparison with each new game frame. */
9067 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9070 loop_sound_volume[nr] = volume;
9071 loop_sound_frame[nr] = FrameCounter;
9074 PlaySoundExt(nr, volume, stereo_position, type);
9077 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9079 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9080 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9081 y < LEVELY(BY1) ? LEVELY(BY1) :
9082 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9086 static void PlayLevelSoundAction(int x, int y, int action)
9088 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9091 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9093 int sound_effect = element_info[element].sound[action];
9095 if (sound_effect != SND_UNDEFINED)
9096 PlayLevelSound(x, y, sound_effect);
9099 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9102 int sound_effect = element_info[element].sound[action];
9104 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9105 PlayLevelSound(x, y, sound_effect);
9108 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9110 int sound_effect = element_info[Feld[x][y]].sound[action];
9112 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9113 PlayLevelSound(x, y, sound_effect);
9116 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9118 int sound_effect = element_info[Feld[x][y]].sound[action];
9120 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9121 StopSound(sound_effect);
9124 static void PlayLevelMusic()
9126 if (levelset.music[level_nr] != MUS_UNDEFINED)
9127 PlayMusic(levelset.music[level_nr]); /* from config file */
9129 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9132 void RaiseScore(int value)
9134 local_player->score += value;
9135 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9138 void RaiseScoreElement(int element)
9144 case EL_EMERALD_YELLOW:
9145 case EL_EMERALD_RED:
9146 case EL_EMERALD_PURPLE:
9147 case EL_SP_INFOTRON:
9148 RaiseScore(level.score[SC_EMERALD]);
9151 RaiseScore(level.score[SC_DIAMOND]);
9154 RaiseScore(level.score[SC_CRYSTAL]);
9157 RaiseScore(level.score[SC_PEARL]);
9160 case EL_BD_BUTTERFLY:
9161 case EL_SP_ELECTRON:
9162 RaiseScore(level.score[SC_BUG]);
9166 case EL_SP_SNIKSNAK:
9167 RaiseScore(level.score[SC_SPACESHIP]);
9170 case EL_DARK_YAMYAM:
9171 RaiseScore(level.score[SC_YAMYAM]);
9174 RaiseScore(level.score[SC_ROBOT]);
9177 RaiseScore(level.score[SC_PACMAN]);
9180 RaiseScore(level.score[SC_NUT]);
9183 case EL_SP_DISK_RED:
9184 case EL_DYNABOMB_INCREASE_NUMBER:
9185 case EL_DYNABOMB_INCREASE_SIZE:
9186 case EL_DYNABOMB_INCREASE_POWER:
9187 RaiseScore(level.score[SC_DYNAMITE]);
9189 case EL_SHIELD_NORMAL:
9190 case EL_SHIELD_DEADLY:
9191 RaiseScore(level.score[SC_SHIELD]);
9194 RaiseScore(level.score[SC_TIME_BONUS]);
9200 RaiseScore(level.score[SC_KEY]);
9203 RaiseScore(element_info[element].collect_score);
9208 void RequestQuitGame(boolean ask_if_really_quit)
9210 if (AllPlayersGone ||
9211 !ask_if_really_quit ||
9212 level_editor_test_game ||
9213 Request("Do you really want to quit the game ?",
9214 REQ_ASK | REQ_STAY_CLOSED))
9216 #if defined(PLATFORM_UNIX)
9217 if (options.network)
9218 SendToServer_StopPlaying();
9222 game_status = GAME_MODE_MAIN;
9228 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9233 /* ---------- new game button stuff ---------------------------------------- */
9235 /* graphic position values for game buttons */
9236 #define GAME_BUTTON_XSIZE 30
9237 #define GAME_BUTTON_YSIZE 30
9238 #define GAME_BUTTON_XPOS 5
9239 #define GAME_BUTTON_YPOS 215
9240 #define SOUND_BUTTON_XPOS 5
9241 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9243 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9244 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9245 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9246 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9247 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9248 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9255 } gamebutton_info[NUM_GAME_BUTTONS] =
9258 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9263 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9268 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9273 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9274 SOUND_CTRL_ID_MUSIC,
9275 "background music on/off"
9278 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9279 SOUND_CTRL_ID_LOOPS,
9280 "sound loops on/off"
9283 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9284 SOUND_CTRL_ID_SIMPLE,
9285 "normal sounds on/off"
9289 void CreateGameButtons()
9293 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9295 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9296 struct GadgetInfo *gi;
9299 unsigned long event_mask;
9300 int gd_xoffset, gd_yoffset;
9301 int gd_x1, gd_x2, gd_y1, gd_y2;
9304 gd_xoffset = gamebutton_info[i].x;
9305 gd_yoffset = gamebutton_info[i].y;
9306 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9307 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9309 if (id == GAME_CTRL_ID_STOP ||
9310 id == GAME_CTRL_ID_PAUSE ||
9311 id == GAME_CTRL_ID_PLAY)
9313 button_type = GD_TYPE_NORMAL_BUTTON;
9315 event_mask = GD_EVENT_RELEASED;
9316 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9317 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9321 button_type = GD_TYPE_CHECK_BUTTON;
9323 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9324 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9325 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9326 event_mask = GD_EVENT_PRESSED;
9327 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9328 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9331 gi = CreateGadget(GDI_CUSTOM_ID, id,
9332 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9333 GDI_X, DX + gd_xoffset,
9334 GDI_Y, DY + gd_yoffset,
9335 GDI_WIDTH, GAME_BUTTON_XSIZE,
9336 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9337 GDI_TYPE, button_type,
9338 GDI_STATE, GD_BUTTON_UNPRESSED,
9339 GDI_CHECKED, checked,
9340 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9341 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9342 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9343 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9344 GDI_EVENT_MASK, event_mask,
9345 GDI_CALLBACK_ACTION, HandleGameButtons,
9349 Error(ERR_EXIT, "cannot create gadget");
9351 game_gadget[id] = gi;
9355 void FreeGameButtons()
9359 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9360 FreeGadget(game_gadget[i]);
9363 static void MapGameButtons()
9367 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9368 MapGadget(game_gadget[i]);
9371 void UnmapGameButtons()
9375 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9376 UnmapGadget(game_gadget[i]);
9379 static void HandleGameButtons(struct GadgetInfo *gi)
9381 int id = gi->custom_id;
9383 if (game_status != GAME_MODE_PLAYING)
9388 case GAME_CTRL_ID_STOP:
9389 RequestQuitGame(TRUE);
9392 case GAME_CTRL_ID_PAUSE:
9393 if (options.network)
9395 #if defined(PLATFORM_UNIX)
9397 SendToServer_ContinuePlaying();
9399 SendToServer_PausePlaying();
9403 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9406 case GAME_CTRL_ID_PLAY:
9409 #if defined(PLATFORM_UNIX)
9410 if (options.network)
9411 SendToServer_ContinuePlaying();
9415 tape.pausing = FALSE;
9416 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9421 case SOUND_CTRL_ID_MUSIC:
9422 if (setup.sound_music)
9424 setup.sound_music = FALSE;
9427 else if (audio.music_available)
9429 setup.sound = setup.sound_music = TRUE;
9431 SetAudioMode(setup.sound);
9437 case SOUND_CTRL_ID_LOOPS:
9438 if (setup.sound_loops)
9439 setup.sound_loops = FALSE;
9440 else if (audio.loops_available)
9442 setup.sound = setup.sound_loops = TRUE;
9443 SetAudioMode(setup.sound);
9447 case SOUND_CTRL_ID_SIMPLE:
9448 if (setup.sound_simple)
9449 setup.sound_simple = FALSE;
9450 else if (audio.sound_available)
9452 setup.sound = setup.sound_simple = TRUE;
9453 SetAudioMode(setup.sound);