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))
147 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
148 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
150 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
151 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
153 /* game button identifiers */
154 #define GAME_CTRL_ID_STOP 0
155 #define GAME_CTRL_ID_PAUSE 1
156 #define GAME_CTRL_ID_PLAY 2
157 #define SOUND_CTRL_ID_MUSIC 3
158 #define SOUND_CTRL_ID_LOOPS 4
159 #define SOUND_CTRL_ID_SIMPLE 5
161 #define NUM_GAME_BUTTONS 6
164 /* forward declaration for internal use */
166 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
167 static boolean MovePlayer(struct PlayerInfo *, int, int);
168 static void ScrollPlayer(struct PlayerInfo *, int);
169 static void ScrollScreen(struct PlayerInfo *, int);
171 static void InitBeltMovement(void);
172 static void CloseAllOpenTimegates(void);
173 static void CheckGravityMovement(struct PlayerInfo *);
174 static void KillHeroUnlessProtected(int, int);
176 static void TestIfPlayerTouchesCustomElement(int, int);
177 static void TestIfElementTouchesCustomElement(int, int);
179 static void ChangeElement(int, int, int);
180 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
181 static boolean CheckTriggeredElementChange(int, int, int, int);
182 static boolean CheckElementSideChange(int, int, int, int, int, int);
183 static boolean CheckElementChange(int, int, int, int);
185 static void PlaySoundLevel(int, int, int);
186 static void PlaySoundLevelNearest(int, int, int);
187 static void PlaySoundLevelAction(int, int, int);
188 static void PlaySoundLevelElementAction(int, int, int, int);
189 static void PlaySoundLevelActionIfLoop(int, int, int);
190 static void StopSoundLevelActionIfLoop(int, int, int);
192 static void MapGameButtons();
193 static void HandleGameButtons(struct GadgetInfo *);
195 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
198 /* ------------------------------------------------------------------------- */
199 /* definition of elements that automatically change to other elements after */
200 /* a specified time, eventually calling a function when changing */
201 /* ------------------------------------------------------------------------- */
203 /* forward declaration for changer functions */
204 static void InitBuggyBase(int x, int y);
205 static void WarnBuggyBase(int x, int y);
207 static void InitTrap(int x, int y);
208 static void ActivateTrap(int x, int y);
209 static void ChangeActiveTrap(int x, int y);
211 static void InitRobotWheel(int x, int y);
212 static void RunRobotWheel(int x, int y);
213 static void StopRobotWheel(int x, int y);
215 static void InitTimegateWheel(int x, int y);
216 static void RunTimegateWheel(int x, int y);
218 struct ChangingElementInfo
223 void (*pre_change_function)(int x, int y);
224 void (*change_function)(int x, int y);
225 void (*post_change_function)(int x, int y);
228 static struct ChangingElementInfo change_delay_list[] =
279 EL_SWITCHGATE_OPENING,
287 EL_SWITCHGATE_CLOSING,
288 EL_SWITCHGATE_CLOSED,
320 EL_ACID_SPLASH_RIGHT,
329 EL_SP_BUGGY_BASE_ACTIVATING,
336 EL_SP_BUGGY_BASE_ACTIVATING,
337 EL_SP_BUGGY_BASE_ACTIVE,
344 EL_SP_BUGGY_BASE_ACTIVE,
368 EL_ROBOT_WHEEL_ACTIVE,
376 EL_TIMEGATE_SWITCH_ACTIVE,
397 int push_delay_fixed, push_delay_random;
402 { EL_BALLOON, 0, 0 },
404 { EL_SOKOBAN_OBJECT, 2, 0 },
405 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
406 { EL_SATELLITE, 2, 0 },
407 { EL_SP_DISK_YELLOW, 2, 0 },
409 { EL_UNDEFINED, 0, 0 },
417 move_stepsize_list[] =
419 { EL_AMOEBA_DROP, 2 },
420 { EL_AMOEBA_DROPPING, 2 },
421 { EL_QUICKSAND_FILLING, 1 },
422 { EL_QUICKSAND_EMPTYING, 1 },
423 { EL_MAGIC_WALL_FILLING, 2 },
424 { EL_BD_MAGIC_WALL_FILLING, 2 },
425 { EL_MAGIC_WALL_EMPTYING, 2 },
426 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
436 collect_count_list[] =
439 { EL_BD_DIAMOND, 1 },
440 { EL_EMERALD_YELLOW, 1 },
441 { EL_EMERALD_RED, 1 },
442 { EL_EMERALD_PURPLE, 1 },
444 { EL_SP_INFOTRON, 1 },
451 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
453 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
454 CH_EVENT_BIT(CE_DELAY))
455 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
456 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
457 IS_JUST_CHANGING(x, y))
459 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
462 void GetPlayerConfig()
464 if (!audio.sound_available)
465 setup.sound_simple = FALSE;
467 if (!audio.loops_available)
468 setup.sound_loops = FALSE;
470 if (!audio.music_available)
471 setup.sound_music = FALSE;
473 if (!video.fullscreen_available)
474 setup.fullscreen = FALSE;
476 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
478 SetAudioMode(setup.sound);
482 static int getBeltNrFromBeltElement(int element)
484 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
485 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
486 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
489 static int getBeltNrFromBeltActiveElement(int element)
491 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
492 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
493 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
496 static int getBeltNrFromBeltSwitchElement(int element)
498 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
499 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
500 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
503 static int getBeltDirNrFromBeltSwitchElement(int element)
505 static int belt_base_element[4] =
507 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
508 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
509 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
510 EL_CONVEYOR_BELT_4_SWITCH_LEFT
513 int belt_nr = getBeltNrFromBeltSwitchElement(element);
514 int belt_dir_nr = element - belt_base_element[belt_nr];
516 return (belt_dir_nr % 3);
519 static int getBeltDirFromBeltSwitchElement(int element)
521 static int belt_move_dir[3] =
528 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
530 return belt_move_dir[belt_dir_nr];
533 static void InitPlayerField(int x, int y, int element, boolean init_game)
535 if (element == EL_SP_MURPHY)
539 if (stored_player[0].present)
541 Feld[x][y] = EL_SP_MURPHY_CLONE;
547 stored_player[0].use_murphy_graphic = TRUE;
550 Feld[x][y] = EL_PLAYER_1;
556 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
557 int jx = player->jx, jy = player->jy;
559 player->present = TRUE;
561 if (!options.network || player->connected)
563 player->active = TRUE;
565 /* remove potentially duplicate players */
566 if (StorePlayer[jx][jy] == Feld[x][y])
567 StorePlayer[jx][jy] = 0;
569 StorePlayer[x][y] = Feld[x][y];
573 printf("Player %d activated.\n", player->element_nr);
574 printf("[Local player is %d and currently %s.]\n",
575 local_player->element_nr,
576 local_player->active ? "active" : "not active");
580 Feld[x][y] = EL_EMPTY;
581 player->jx = player->last_jx = x;
582 player->jy = player->last_jy = y;
586 static void InitField(int x, int y, boolean init_game)
588 int element = Feld[x][y];
597 InitPlayerField(x, y, element, init_game);
601 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
602 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
603 else if (x > 0 && Feld[x-1][y] == EL_ACID)
604 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
605 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
606 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
607 else if (y > 0 && Feld[x][y-1] == EL_ACID)
608 Feld[x][y] = EL_ACID_POOL_BOTTOM;
609 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
610 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
618 case EL_SPACESHIP_RIGHT:
619 case EL_SPACESHIP_UP:
620 case EL_SPACESHIP_LEFT:
621 case EL_SPACESHIP_DOWN:
623 case EL_BD_BUTTERFLY_RIGHT:
624 case EL_BD_BUTTERFLY_UP:
625 case EL_BD_BUTTERFLY_LEFT:
626 case EL_BD_BUTTERFLY_DOWN:
627 case EL_BD_BUTTERFLY:
628 case EL_BD_FIREFLY_RIGHT:
629 case EL_BD_FIREFLY_UP:
630 case EL_BD_FIREFLY_LEFT:
631 case EL_BD_FIREFLY_DOWN:
633 case EL_PACMAN_RIGHT:
657 if (y == lev_fieldy - 1)
659 Feld[x][y] = EL_AMOEBA_GROWING;
660 Store[x][y] = EL_AMOEBA_WET;
664 case EL_DYNAMITE_ACTIVE:
669 local_player->lights_still_needed++;
672 case EL_SOKOBAN_FIELD_EMPTY:
673 local_player->sokobanfields_still_needed++;
677 local_player->friends_still_needed++;
682 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
687 Feld[x][y] = EL_EMPTY;
692 case EL_EM_KEY_1_FILE:
693 Feld[x][y] = EL_EM_KEY_1;
695 case EL_EM_KEY_2_FILE:
696 Feld[x][y] = EL_EM_KEY_2;
698 case EL_EM_KEY_3_FILE:
699 Feld[x][y] = EL_EM_KEY_3;
701 case EL_EM_KEY_4_FILE:
702 Feld[x][y] = EL_EM_KEY_4;
706 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
707 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
708 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
709 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
710 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
711 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
712 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
713 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
714 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
715 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
716 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
717 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
720 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
721 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
722 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
724 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
726 game.belt_dir[belt_nr] = belt_dir;
727 game.belt_dir_nr[belt_nr] = belt_dir_nr;
729 else /* more than one switch -- set it like the first switch */
731 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
736 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
738 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
741 case EL_LIGHT_SWITCH_ACTIVE:
743 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
747 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
753 void DrawGameDoorValues()
757 for (i=0; i<MAX_PLAYERS; i++)
759 if (stored_player[i].key[j])
760 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
761 el2edimg(EL_KEY_1 + j));
763 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
764 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
765 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
766 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
767 DrawText(DX + XX_SCORE, DY + YY_SCORE,
768 int2str(local_player->score, 5), FONT_TEXT_2);
769 DrawText(DX + XX_TIME, DY + YY_TIME,
770 int2str(TimeLeft, 3), FONT_TEXT_2);
775 =============================================================================
777 -----------------------------------------------------------------------------
778 initialize game engine due to level / tape version number
779 =============================================================================
782 static void InitGameEngine()
786 /* set game engine from tape file when re-playing, else from level file */
787 game.engine_version = (tape.playing ? tape.engine_version :
790 /* dynamically adjust element properties according to game engine version */
791 InitElementPropertiesEngine(game.engine_version);
794 printf("level %d: level version == %06d\n", level_nr, level.game_version);
795 printf(" tape version == %06d [%s] [file: %06d]\n",
796 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
798 printf(" => game.engine_version == %06d\n", game.engine_version);
801 /* ---------- initialize player's initial move delay --------------------- */
803 /* dynamically adjust player properties according to game engine version */
804 game.initial_move_delay =
805 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
806 INITIAL_MOVE_DELAY_OFF);
808 /* dynamically adjust player properties according to level information */
809 game.initial_move_delay_value =
810 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
812 /* ---------- initialize player's initial push delay --------------------- */
814 /* dynamically adjust player properties according to game engine version */
815 game.initial_push_delay_value =
816 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
818 /* ---------- initialize changing elements ------------------------------- */
820 /* initialize changing elements information */
821 for (i=0; i < MAX_NUM_ELEMENTS; i++)
823 struct ElementInfo *ei = &element_info[i];
825 /* this pointer might have been changed in the level editor */
826 ei->change = &ei->change_page[0];
828 if (!IS_CUSTOM_ELEMENT(i))
830 ei->change->target_element = EL_EMPTY_SPACE;
831 ei->change->delay_fixed = 0;
832 ei->change->delay_random = 0;
833 ei->change->delay_frames = 1;
836 ei->change_events = CE_BITMASK_DEFAULT;
837 for (j=0; j < NUM_CHANGE_EVENTS; j++)
839 ei->event_page_nr[j] = 0;
840 ei->event_page[j] = &ei->change_page[0];
844 /* add changing elements from pre-defined list */
845 for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
847 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
848 struct ElementInfo *ei = &element_info[ch_delay->element];
850 ei->change->target_element = ch_delay->target_element;
851 ei->change->delay_fixed = ch_delay->change_delay;
853 ei->change->pre_change_function = ch_delay->pre_change_function;
854 ei->change->change_function = ch_delay->change_function;
855 ei->change->post_change_function = ch_delay->post_change_function;
857 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
861 /* add change events from custom element configuration */
862 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
864 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
866 for (j=0; j < ei->num_change_pages; j++)
868 if (!ei->change_page[j].can_change)
871 for (k=0; k < NUM_CHANGE_EVENTS; k++)
873 /* only add event page for the first page found with this event */
874 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
875 !(ei->change_events & CH_EVENT_BIT(k)))
877 ei->change_events |= CH_EVENT_BIT(k);
878 ei->event_page_nr[k] = j;
879 ei->event_page[k] = &ei->change_page[j];
887 /* add change events from custom element configuration */
888 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
890 int element = EL_CUSTOM_START + i;
892 /* only add custom elements that change after fixed/random frame delay */
893 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
894 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
898 /* ---------- initialize trigger events ---------------------------------- */
900 /* initialize trigger events information */
901 for (i=0; i<MAX_NUM_ELEMENTS; i++)
902 trigger_events[i] = EP_BITMASK_DEFAULT;
905 /* add trigger events from element change event properties */
906 for (i=0; i<MAX_NUM_ELEMENTS; i++)
908 struct ElementInfo *ei = &element_info[i];
910 for (j=0; j < ei->num_change_pages; j++)
912 if (!ei->change_page[j].can_change)
915 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
917 int trigger_element = ei->change_page[j].trigger_element;
919 trigger_events[trigger_element] |= ei->change_page[j].events;
924 /* add trigger events from element change event properties */
925 for (i=0; i<MAX_NUM_ELEMENTS; i++)
926 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
927 trigger_events[element_info[i].change->trigger_element] |=
928 element_info[i].change->events;
931 /* ---------- initialize push delay -------------------------------------- */
933 /* initialize push delay values to default */
934 for (i=0; i<MAX_NUM_ELEMENTS; i++)
936 if (!IS_CUSTOM_ELEMENT(i))
938 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
939 element_info[i].push_delay_random = game.default_push_delay_random;
943 /* set push delay value for certain elements from pre-defined list */
944 for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
946 int e = push_delay_list[i].element;
948 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
949 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
952 /* ---------- initialize move stepsize ----------------------------------- */
954 /* initialize move stepsize values to default */
955 for (i=0; i<MAX_NUM_ELEMENTS; i++)
956 if (!IS_CUSTOM_ELEMENT(i))
957 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
959 /* set move stepsize value for certain elements from pre-defined list */
960 for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
962 int e = move_stepsize_list[i].element;
964 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
967 /* ---------- initialize gem count --------------------------------------- */
969 /* initialize gem count values for each element */
970 for (i=0; i<MAX_NUM_ELEMENTS; i++)
971 if (!IS_CUSTOM_ELEMENT(i))
972 element_info[i].collect_count = 0;
974 /* add gem count values for all elements from pre-defined list */
975 for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
976 element_info[collect_count_list[i].element].collect_count =
977 collect_count_list[i].count;
982 =============================================================================
984 -----------------------------------------------------------------------------
985 initialize and start new game
986 =============================================================================
991 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
992 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
993 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1000 #if USE_NEW_AMOEBA_CODE
1001 printf("Using new amoeba code.\n");
1003 printf("Using old amoeba code.\n");
1008 /* don't play tapes over network */
1009 network_playing = (options.network && !tape.playing);
1011 for (i=0; i<MAX_PLAYERS; i++)
1013 struct PlayerInfo *player = &stored_player[i];
1015 player->index_nr = i;
1016 player->element_nr = EL_PLAYER_1 + i;
1018 player->present = FALSE;
1019 player->active = FALSE;
1022 player->effective_action = 0;
1023 player->programmed_action = 0;
1026 player->gems_still_needed = level.gems_needed;
1027 player->sokobanfields_still_needed = 0;
1028 player->lights_still_needed = 0;
1029 player->friends_still_needed = 0;
1032 player->key[j] = FALSE;
1034 player->dynabomb_count = 0;
1035 player->dynabomb_size = 1;
1036 player->dynabombs_left = 0;
1037 player->dynabomb_xl = FALSE;
1039 player->MovDir = MV_NO_MOVING;
1042 player->GfxDir = MV_NO_MOVING;
1043 player->GfxAction = ACTION_DEFAULT;
1045 player->StepFrame = 0;
1047 player->use_murphy_graphic = FALSE;
1049 player->actual_frame_counter = 0;
1051 player->last_move_dir = MV_NO_MOVING;
1053 player->is_waiting = FALSE;
1054 player->is_moving = FALSE;
1055 player->is_digging = FALSE;
1056 player->is_snapping = FALSE;
1057 player->is_collecting = FALSE;
1058 player->is_pushing = FALSE;
1059 player->is_switching = FALSE;
1061 player->switch_x = -1;
1062 player->switch_y = -1;
1064 player->show_envelope = 0;
1066 player->move_delay = game.initial_move_delay;
1067 player->move_delay_value = game.initial_move_delay_value;
1069 player->push_delay = 0;
1070 player->push_delay_value = game.initial_push_delay_value;
1072 player->last_jx = player->last_jy = 0;
1073 player->jx = player->jy = 0;
1075 player->shield_normal_time_left = 0;
1076 player->shield_deadly_time_left = 0;
1078 player->inventory_size = 0;
1080 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1081 SnapField(player, 0, 0);
1083 player->LevelSolved = FALSE;
1084 player->GameOver = FALSE;
1087 network_player_action_received = FALSE;
1089 #if defined(PLATFORM_UNIX)
1090 /* initial null action */
1091 if (network_playing)
1092 SendToServer_MovePlayer(MV_NO_MOVING);
1100 TimeLeft = level.time;
1102 ScreenMovDir = MV_NO_MOVING;
1106 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1108 AllPlayersGone = FALSE;
1110 game.yamyam_content_nr = 0;
1111 game.magic_wall_active = FALSE;
1112 game.magic_wall_time_left = 0;
1113 game.light_time_left = 0;
1114 game.timegate_time_left = 0;
1115 game.switchgate_pos = 0;
1116 game.balloon_dir = MV_NO_MOVING;
1117 game.gravity = level.initial_gravity;
1118 game.explosions_delayed = TRUE;
1120 game.envelope_active = FALSE;
1124 game.belt_dir[i] = MV_NO_MOVING;
1125 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1128 for (i=0; i<MAX_NUM_AMOEBA; i++)
1129 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1131 for (x=0; x<lev_fieldx; x++)
1133 for (y=0; y<lev_fieldy; y++)
1135 Feld[x][y] = level.field[x][y];
1136 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1137 ChangeDelay[x][y] = 0;
1138 ChangePage[x][y] = -1;
1139 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1141 WasJustMoving[x][y] = 0;
1142 WasJustFalling[x][y] = 0;
1144 Pushed[x][y] = FALSE;
1146 Changed[x][y] = CE_BITMASK_DEFAULT;
1147 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1149 ExplodePhase[x][y] = 0;
1150 ExplodeField[x][y] = EX_NO_EXPLOSION;
1153 GfxRandom[x][y] = INIT_GFX_RANDOM();
1154 GfxElement[x][y] = EL_UNDEFINED;
1155 GfxAction[x][y] = ACTION_DEFAULT;
1156 GfxDir[x][y] = MV_NO_MOVING;
1160 for(y=0; y<lev_fieldy; y++)
1162 for(x=0; x<lev_fieldx; x++)
1164 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1166 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1168 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1171 InitField(x, y, TRUE);
1177 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1178 emulate_sb ? EMU_SOKOBAN :
1179 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1181 /* correct non-moving belts to start moving left */
1183 if (game.belt_dir[i] == MV_NO_MOVING)
1184 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1186 /* check if any connected player was not found in playfield */
1187 for (i=0; i<MAX_PLAYERS; i++)
1189 struct PlayerInfo *player = &stored_player[i];
1191 if (player->connected && !player->present)
1193 for (j=0; j<MAX_PLAYERS; j++)
1195 struct PlayerInfo *some_player = &stored_player[j];
1196 int jx = some_player->jx, jy = some_player->jy;
1198 /* assign first free player found that is present in the playfield */
1199 if (some_player->present && !some_player->connected)
1201 player->present = TRUE;
1202 player->active = TRUE;
1203 some_player->present = FALSE;
1205 StorePlayer[jx][jy] = player->element_nr;
1206 player->jx = player->last_jx = jx;
1207 player->jy = player->last_jy = jy;
1217 /* when playing a tape, eliminate all players who do not participate */
1219 for (i=0; i<MAX_PLAYERS; i++)
1221 if (stored_player[i].active && !tape.player_participates[i])
1223 struct PlayerInfo *player = &stored_player[i];
1224 int jx = player->jx, jy = player->jy;
1226 player->active = FALSE;
1227 StorePlayer[jx][jy] = 0;
1228 Feld[jx][jy] = EL_EMPTY;
1232 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1234 /* when in single player mode, eliminate all but the first active player */
1236 for (i=0; i<MAX_PLAYERS; i++)
1238 if (stored_player[i].active)
1240 for (j=i+1; j<MAX_PLAYERS; j++)
1242 if (stored_player[j].active)
1244 struct PlayerInfo *player = &stored_player[j];
1245 int jx = player->jx, jy = player->jy;
1247 player->active = FALSE;
1248 StorePlayer[jx][jy] = 0;
1249 Feld[jx][jy] = EL_EMPTY;
1256 /* when recording the game, store which players take part in the game */
1259 for (i=0; i<MAX_PLAYERS; i++)
1260 if (stored_player[i].active)
1261 tape.player_participates[i] = TRUE;
1266 for (i=0; i<MAX_PLAYERS; i++)
1268 struct PlayerInfo *player = &stored_player[i];
1270 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1275 if (local_player == player)
1276 printf("Player %d is local player.\n", i+1);
1280 if (BorderElement == EL_EMPTY)
1283 SBX_Right = lev_fieldx - SCR_FIELDX;
1285 SBY_Lower = lev_fieldy - SCR_FIELDY;
1290 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1292 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1295 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1296 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1298 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1299 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1301 /* if local player not found, look for custom element that might create
1302 the player (make some assumptions about the right custom element) */
1303 if (!local_player->present)
1305 int start_x = 0, start_y = 0;
1306 int found_rating = 0;
1307 int found_element = EL_UNDEFINED;
1309 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1311 int element = Feld[x][y];
1316 if (!IS_CUSTOM_ELEMENT(element))
1319 if (CAN_CHANGE(element))
1321 for (i=0; i < element_info[element].num_change_pages; i++)
1323 content = element_info[element].change_page[i].target_element;
1324 is_player = ELEM_IS_PLAYER(content);
1326 if (is_player && (found_rating < 3 || element < found_element))
1332 found_element = element;
1337 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1339 content = element_info[element].content[xx][yy];
1340 is_player = ELEM_IS_PLAYER(content);
1342 if (is_player && (found_rating < 2 || element < found_element))
1344 start_x = x + xx - 1;
1345 start_y = y + yy - 1;
1348 found_element = element;
1351 if (!CAN_CHANGE(element))
1354 for (i=0; i < element_info[element].num_change_pages; i++)
1356 content = element_info[element].change_page[i].content[xx][yy];
1357 is_player = ELEM_IS_PLAYER(content);
1359 if (is_player && (found_rating < 1 || element < found_element))
1361 start_x = x + xx - 1;
1362 start_y = y + yy - 1;
1365 found_element = element;
1371 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1372 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1375 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1376 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1382 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1383 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1384 local_player->jx - MIDPOSX);
1386 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1387 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1388 local_player->jy - MIDPOSY);
1390 scroll_x = SBX_Left;
1391 scroll_y = SBY_Upper;
1392 if (local_player->jx >= SBX_Left + MIDPOSX)
1393 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1394 local_player->jx - MIDPOSX :
1396 if (local_player->jy >= SBY_Upper + MIDPOSY)
1397 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1398 local_player->jy - MIDPOSY :
1403 CloseDoor(DOOR_CLOSE_1);
1408 /* after drawing the level, correct some elements */
1409 if (game.timegate_time_left == 0)
1410 CloseAllOpenTimegates();
1412 if (setup.soft_scrolling)
1413 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1415 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1418 /* copy default game door content to main double buffer */
1419 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1420 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1423 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1426 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1427 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1428 BlitBitmap(drawto, drawto,
1429 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1430 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1431 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1432 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1435 DrawGameDoorValues();
1439 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1440 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1441 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1445 /* copy actual game door content to door double buffer for OpenDoor() */
1446 BlitBitmap(drawto, bitmap_db_door,
1447 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1449 OpenDoor(DOOR_OPEN_ALL);
1451 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1452 if (setup.sound_music)
1453 PlayMusic(level_nr);
1455 KeyboardAutoRepeatOffUnlessAutoplay();
1460 printf("Player %d %sactive.\n",
1461 i + 1, (stored_player[i].active ? "" : "not "));
1465 void InitMovDir(int x, int y)
1467 int i, element = Feld[x][y];
1468 static int xy[4][2] =
1475 static int direction[3][4] =
1477 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1478 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1479 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1488 Feld[x][y] = EL_BUG;
1489 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1492 case EL_SPACESHIP_RIGHT:
1493 case EL_SPACESHIP_UP:
1494 case EL_SPACESHIP_LEFT:
1495 case EL_SPACESHIP_DOWN:
1496 Feld[x][y] = EL_SPACESHIP;
1497 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1500 case EL_BD_BUTTERFLY_RIGHT:
1501 case EL_BD_BUTTERFLY_UP:
1502 case EL_BD_BUTTERFLY_LEFT:
1503 case EL_BD_BUTTERFLY_DOWN:
1504 Feld[x][y] = EL_BD_BUTTERFLY;
1505 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1508 case EL_BD_FIREFLY_RIGHT:
1509 case EL_BD_FIREFLY_UP:
1510 case EL_BD_FIREFLY_LEFT:
1511 case EL_BD_FIREFLY_DOWN:
1512 Feld[x][y] = EL_BD_FIREFLY;
1513 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1516 case EL_PACMAN_RIGHT:
1518 case EL_PACMAN_LEFT:
1519 case EL_PACMAN_DOWN:
1520 Feld[x][y] = EL_PACMAN;
1521 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1524 case EL_SP_SNIKSNAK:
1525 MovDir[x][y] = MV_UP;
1528 case EL_SP_ELECTRON:
1529 MovDir[x][y] = MV_LEFT;
1536 Feld[x][y] = EL_MOLE;
1537 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1541 if (IS_CUSTOM_ELEMENT(element))
1543 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1544 MovDir[x][y] = element_info[element].move_direction_initial;
1545 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1546 element_info[element].move_pattern == MV_TURNING_LEFT ||
1547 element_info[element].move_pattern == MV_TURNING_RIGHT)
1548 MovDir[x][y] = 1 << RND(4);
1549 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1550 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1551 else if (element_info[element].move_pattern == MV_VERTICAL)
1552 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1553 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1554 MovDir[x][y] = element_info[element].move_pattern;
1555 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1556 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1560 int x1 = x + xy[i][0];
1561 int y1 = y + xy[i][1];
1563 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1565 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1566 MovDir[x][y] = direction[0][i];
1568 MovDir[x][y] = direction[1][i];
1577 MovDir[x][y] = 1 << RND(4);
1579 if (element != EL_BUG &&
1580 element != EL_SPACESHIP &&
1581 element != EL_BD_BUTTERFLY &&
1582 element != EL_BD_FIREFLY)
1587 int x1 = x + xy[i][0];
1588 int y1 = y + xy[i][1];
1590 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1592 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1594 MovDir[x][y] = direction[0][i];
1597 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1598 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1600 MovDir[x][y] = direction[1][i];
1609 GfxDir[x][y] = MovDir[x][y];
1612 void InitAmoebaNr(int x, int y)
1615 int group_nr = AmoebeNachbarNr(x, y);
1619 for (i=1; i<MAX_NUM_AMOEBA; i++)
1621 if (AmoebaCnt[i] == 0)
1629 AmoebaNr[x][y] = group_nr;
1630 AmoebaCnt[group_nr]++;
1631 AmoebaCnt2[group_nr]++;
1637 boolean raise_level = FALSE;
1639 if (local_player->MovPos)
1643 if (tape.auto_play) /* tape might already be stopped here */
1644 tape.auto_play_level_solved = TRUE;
1646 if (tape.playing && tape.auto_play)
1647 tape.auto_play_level_solved = TRUE;
1650 local_player->LevelSolved = FALSE;
1652 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1656 if (!tape.playing && setup.sound_loops)
1657 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1658 SND_CTRL_PLAY_LOOP);
1660 while (TimeLeft > 0)
1662 if (!tape.playing && !setup.sound_loops)
1663 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1664 if (TimeLeft > 0 && !(TimeLeft % 10))
1665 RaiseScore(level.score[SC_TIME_BONUS]);
1666 if (TimeLeft > 100 && !(TimeLeft % 10))
1670 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1677 if (!tape.playing && setup.sound_loops)
1678 StopSound(SND_GAME_LEVELTIME_BONUS);
1680 else if (level.time == 0) /* level without time limit */
1682 if (!tape.playing && setup.sound_loops)
1683 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1684 SND_CTRL_PLAY_LOOP);
1686 while (TimePlayed < 999)
1688 if (!tape.playing && !setup.sound_loops)
1689 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1690 if (TimePlayed < 999 && !(TimePlayed % 10))
1691 RaiseScore(level.score[SC_TIME_BONUS]);
1692 if (TimePlayed < 900 && !(TimePlayed % 10))
1696 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1703 if (!tape.playing && setup.sound_loops)
1704 StopSound(SND_GAME_LEVELTIME_BONUS);
1707 /* close exit door after last player */
1708 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1709 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1711 int element = Feld[ExitX][ExitY];
1713 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1714 EL_SP_EXIT_CLOSING);
1716 PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1719 /* Hero disappears */
1720 DrawLevelField(ExitX, ExitY);
1726 CloseDoor(DOOR_CLOSE_1);
1731 SaveTape(tape.level_nr); /* Ask to save tape */
1734 if (level_nr == leveldir_current->handicap_level)
1736 leveldir_current->handicap_level++;
1737 SaveLevelSetup_SeriesInfo();
1740 if (level_editor_test_game)
1741 local_player->score = -1; /* no highscore when playing from editor */
1742 else if (level_nr < leveldir_current->last_level)
1743 raise_level = TRUE; /* advance to next level */
1745 if ((hi_pos = NewHiScore()) >= 0)
1747 game_status = GAME_MODE_SCORES;
1748 DrawHallOfFame(hi_pos);
1757 game_status = GAME_MODE_MAIN;
1774 LoadScore(level_nr);
1776 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1777 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1780 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1782 if (local_player->score > highscore[k].Score)
1784 /* player has made it to the hall of fame */
1786 if (k < MAX_SCORE_ENTRIES - 1)
1788 int m = MAX_SCORE_ENTRIES - 1;
1791 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1792 if (!strcmp(setup.player_name, highscore[l].Name))
1794 if (m == k) /* player's new highscore overwrites his old one */
1800 strcpy(highscore[l].Name, highscore[l - 1].Name);
1801 highscore[l].Score = highscore[l - 1].Score;
1808 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1809 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1810 highscore[k].Score = local_player->score;
1816 else if (!strncmp(setup.player_name, highscore[k].Name,
1817 MAX_PLAYER_NAME_LEN))
1818 break; /* player already there with a higher score */
1824 SaveScore(level_nr);
1829 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1831 if (player->GfxAction != action || player->GfxDir != dir)
1834 printf("Player frame reset! (%d => %d, %d => %d)\n",
1835 player->GfxAction, action, player->GfxDir, dir);
1838 player->GfxAction = action;
1839 player->GfxDir = dir;
1841 player->StepFrame = 0;
1845 static void ResetRandomAnimationValue(int x, int y)
1847 GfxRandom[x][y] = INIT_GFX_RANDOM();
1850 static void ResetGfxAnimation(int x, int y)
1853 GfxAction[x][y] = ACTION_DEFAULT;
1854 GfxDir[x][y] = MovDir[x][y];
1857 void InitMovingField(int x, int y, int direction)
1859 int element = Feld[x][y];
1860 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1861 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1865 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1866 ResetGfxAnimation(x, y);
1868 MovDir[newx][newy] = MovDir[x][y] = direction;
1869 GfxDir[x][y] = direction;
1871 if (Feld[newx][newy] == EL_EMPTY)
1872 Feld[newx][newy] = EL_BLOCKED;
1874 if (direction == MV_DOWN && CAN_FALL(element))
1875 GfxAction[x][y] = ACTION_FALLING;
1877 GfxAction[x][y] = ACTION_MOVING;
1879 GfxFrame[newx][newy] = GfxFrame[x][y];
1880 GfxRandom[newx][newy] = GfxRandom[x][y];
1881 GfxAction[newx][newy] = GfxAction[x][y];
1882 GfxDir[newx][newy] = GfxDir[x][y];
1885 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1887 int direction = MovDir[x][y];
1888 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1889 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1895 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1897 int oldx = x, oldy = y;
1898 int direction = MovDir[x][y];
1900 if (direction == MV_LEFT)
1902 else if (direction == MV_RIGHT)
1904 else if (direction == MV_UP)
1906 else if (direction == MV_DOWN)
1909 *comes_from_x = oldx;
1910 *comes_from_y = oldy;
1913 int MovingOrBlocked2Element(int x, int y)
1915 int element = Feld[x][y];
1917 if (element == EL_BLOCKED)
1921 Blocked2Moving(x, y, &oldx, &oldy);
1922 return Feld[oldx][oldy];
1928 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1930 /* like MovingOrBlocked2Element(), but if element is moving
1931 and (x,y) is the field the moving element is just leaving,
1932 return EL_BLOCKED instead of the element value */
1933 int element = Feld[x][y];
1935 if (IS_MOVING(x, y))
1937 if (element == EL_BLOCKED)
1941 Blocked2Moving(x, y, &oldx, &oldy);
1942 return Feld[oldx][oldy];
1951 static void RemoveField(int x, int y)
1953 Feld[x][y] = EL_EMPTY;
1960 ChangeDelay[x][y] = 0;
1961 ChangePage[x][y] = -1;
1962 Pushed[x][y] = FALSE;
1964 GfxElement[x][y] = EL_UNDEFINED;
1965 GfxAction[x][y] = ACTION_DEFAULT;
1966 GfxDir[x][y] = MV_NO_MOVING;
1969 void RemoveMovingField(int x, int y)
1971 int oldx = x, oldy = y, newx = x, newy = y;
1972 int element = Feld[x][y];
1973 int next_element = EL_UNDEFINED;
1975 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1978 if (IS_MOVING(x, y))
1980 Moving2Blocked(x, y, &newx, &newy);
1981 if (Feld[newx][newy] != EL_BLOCKED)
1984 else if (element == EL_BLOCKED)
1986 Blocked2Moving(x, y, &oldx, &oldy);
1987 if (!IS_MOVING(oldx, oldy))
1991 if (element == EL_BLOCKED &&
1992 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1993 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1994 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1995 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
1996 next_element = get_next_element(Feld[oldx][oldy]);
1998 RemoveField(oldx, oldy);
1999 RemoveField(newx, newy);
2001 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2003 if (next_element != EL_UNDEFINED)
2004 Feld[oldx][oldy] = next_element;
2006 DrawLevelField(oldx, oldy);
2007 DrawLevelField(newx, newy);
2010 void DrawDynamite(int x, int y)
2012 int sx = SCREENX(x), sy = SCREENY(y);
2013 int graphic = el2img(Feld[x][y]);
2016 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2019 if (IS_WALKABLE_INSIDE(Back[x][y]))
2023 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2024 else if (Store[x][y])
2025 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2027 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2030 if (Back[x][y] || Store[x][y])
2031 DrawGraphicThruMask(sx, sy, graphic, frame);
2033 DrawGraphic(sx, sy, graphic, frame);
2035 if (game.emulation == EMU_SUPAPLEX)
2036 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2037 else if (Store[x][y])
2038 DrawGraphicThruMask(sx, sy, graphic, frame);
2040 DrawGraphic(sx, sy, graphic, frame);
2044 void CheckDynamite(int x, int y)
2046 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2050 if (MovDelay[x][y] != 0)
2053 PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2060 StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2062 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2063 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2064 StopSound(SND_DYNAMITE_ACTIVE);
2066 StopSound(SND_DYNABOMB_ACTIVE);
2072 void RelocatePlayer(int x, int y, int element)
2074 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2077 RemoveField(x, y); /* temporarily remove newly placed player */
2078 DrawLevelField(x, y);
2081 if (player->present)
2083 while (player->MovPos)
2085 ScrollPlayer(player, SCROLL_GO_ON);
2086 ScrollScreen(NULL, SCROLL_GO_ON);
2092 Delay(GAME_FRAME_DELAY);
2095 DrawPlayer(player); /* needed here only to cleanup last field */
2096 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2098 player->is_moving = FALSE;
2101 Feld[x][y] = element;
2102 InitPlayerField(x, y, element, TRUE);
2104 if (player == local_player)
2106 int scroll_xx = -999, scroll_yy = -999;
2108 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2111 int fx = FX, fy = FY;
2113 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2114 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2115 local_player->jx - MIDPOSX);
2117 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2118 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2119 local_player->jy - MIDPOSY);
2121 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2122 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2127 fx += dx * TILEX / 2;
2128 fy += dy * TILEY / 2;
2130 ScrollLevel(dx, dy);
2133 /* scroll in two steps of half tile size to make things smoother */
2134 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2136 Delay(GAME_FRAME_DELAY);
2138 /* scroll second step to align at full tile size */
2140 Delay(GAME_FRAME_DELAY);
2145 void Explode(int ex, int ey, int phase, int mode)
2149 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2150 int last_phase = num_phase * delay;
2151 int half_phase = (num_phase / 2) * delay;
2152 int first_phase_after_start = EX_PHASE_START + 1;
2154 if (game.explosions_delayed)
2156 ExplodeField[ex][ey] = mode;
2160 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2162 int center_element = Feld[ex][ey];
2165 /* --- This is only really needed (and now handled) in "Impact()". --- */
2166 /* do not explode moving elements that left the explode field in time */
2167 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2168 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2172 if (mode == EX_NORMAL || mode == EX_CENTER)
2173 PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
2175 /* remove things displayed in background while burning dynamite */
2176 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2179 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2181 /* put moving element to center field (and let it explode there) */
2182 center_element = MovingOrBlocked2Element(ex, ey);
2183 RemoveMovingField(ex, ey);
2184 Feld[ex][ey] = center_element;
2187 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2189 int xx = x - ex + 1;
2190 int yy = y - ey + 1;
2193 if (!IN_LEV_FIELD(x, y) ||
2194 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2195 (x != ex || y != ey)))
2198 element = Feld[x][y];
2200 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2202 element = MovingOrBlocked2Element(x, y);
2204 if (!IS_EXPLOSION_PROOF(element))
2205 RemoveMovingField(x, y);
2211 if (IS_EXPLOSION_PROOF(element))
2214 /* indestructible elements can only explode in center (but not flames) */
2215 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2216 element == EL_FLAMES)
2221 if ((IS_INDESTRUCTIBLE(element) &&
2222 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2223 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2224 element == EL_FLAMES)
2228 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2230 if (IS_ACTIVE_BOMB(element))
2232 /* re-activate things under the bomb like gate or penguin */
2233 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2240 /* save walkable background elements while explosion on same tile */
2242 if (IS_INDESTRUCTIBLE(element))
2243 Back[x][y] = element;
2245 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2246 Back[x][y] = element;
2249 /* ignite explodable elements reached by other explosion */
2250 if (element == EL_EXPLOSION)
2251 element = Store2[x][y];
2254 if (AmoebaNr[x][y] &&
2255 (element == EL_AMOEBA_FULL ||
2256 element == EL_BD_AMOEBA ||
2257 element == EL_AMOEBA_GROWING))
2259 AmoebaCnt[AmoebaNr[x][y]]--;
2260 AmoebaCnt2[AmoebaNr[x][y]]--;
2266 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2268 switch(StorePlayer[ex][ey])
2271 Store[x][y] = EL_EMERALD_RED;
2274 Store[x][y] = EL_EMERALD;
2277 Store[x][y] = EL_EMERALD_PURPLE;
2281 Store[x][y] = EL_EMERALD_YELLOW;
2285 if (game.emulation == EMU_SUPAPLEX)
2286 Store[x][y] = EL_EMPTY;
2288 else if (center_element == EL_MOLE)
2289 Store[x][y] = EL_EMERALD_RED;
2290 else if (center_element == EL_PENGUIN)
2291 Store[x][y] = EL_EMERALD_PURPLE;
2292 else if (center_element == EL_BUG)
2293 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2294 else if (center_element == EL_BD_BUTTERFLY)
2295 Store[x][y] = EL_BD_DIAMOND;
2296 else if (center_element == EL_SP_ELECTRON)
2297 Store[x][y] = EL_SP_INFOTRON;
2298 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2299 Store[x][y] = level.amoeba_content;
2300 else if (center_element == EL_YAMYAM)
2301 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2302 else if (IS_CUSTOM_ELEMENT(center_element) &&
2303 element_info[center_element].content[xx][yy] != EL_EMPTY)
2304 Store[x][y] = element_info[center_element].content[xx][yy];
2305 else if (element == EL_WALL_EMERALD)
2306 Store[x][y] = EL_EMERALD;
2307 else if (element == EL_WALL_DIAMOND)
2308 Store[x][y] = EL_DIAMOND;
2309 else if (element == EL_WALL_BD_DIAMOND)
2310 Store[x][y] = EL_BD_DIAMOND;
2311 else if (element == EL_WALL_EMERALD_YELLOW)
2312 Store[x][y] = EL_EMERALD_YELLOW;
2313 else if (element == EL_WALL_EMERALD_RED)
2314 Store[x][y] = EL_EMERALD_RED;
2315 else if (element == EL_WALL_EMERALD_PURPLE)
2316 Store[x][y] = EL_EMERALD_PURPLE;
2317 else if (element == EL_WALL_PEARL)
2318 Store[x][y] = EL_PEARL;
2319 else if (element == EL_WALL_CRYSTAL)
2320 Store[x][y] = EL_CRYSTAL;
2321 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2322 Store[x][y] = element_info[element].content[1][1];
2324 Store[x][y] = EL_EMPTY;
2326 if (x != ex || y != ey ||
2327 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2328 Store2[x][y] = element;
2331 if (AmoebaNr[x][y] &&
2332 (element == EL_AMOEBA_FULL ||
2333 element == EL_BD_AMOEBA ||
2334 element == EL_AMOEBA_GROWING))
2336 AmoebaCnt[AmoebaNr[x][y]]--;
2337 AmoebaCnt2[AmoebaNr[x][y]]--;
2343 MovDir[x][y] = MovPos[x][y] = 0;
2344 GfxDir[x][y] = MovDir[x][y];
2349 Feld[x][y] = EL_EXPLOSION;
2351 GfxElement[x][y] = center_element;
2353 GfxElement[x][y] = EL_UNDEFINED;
2356 ExplodePhase[x][y] = 1;
2360 if (center_element == EL_YAMYAM)
2361 game.yamyam_content_nr =
2362 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2373 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2377 /* activate this even in non-DEBUG version until cause for crash in
2378 getGraphicAnimationFrame() (see below) is found and eliminated */
2382 if (GfxElement[x][y] == EL_UNDEFINED)
2385 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2386 printf("Explode(): This should never happen!\n");
2389 GfxElement[x][y] = EL_EMPTY;
2393 if (phase == first_phase_after_start)
2395 int element = Store2[x][y];
2397 if (element == EL_BLACK_ORB)
2399 Feld[x][y] = Store2[x][y];
2404 else if (phase == half_phase)
2406 int element = Store2[x][y];
2408 if (IS_PLAYER(x, y))
2409 KillHeroUnlessProtected(x, y);
2410 else if (CAN_EXPLODE_BY_FIRE(element))
2412 Feld[x][y] = Store2[x][y];
2416 else if (element == EL_AMOEBA_TO_DIAMOND)
2417 AmoebeUmwandeln(x, y);
2420 if (phase == last_phase)
2424 element = Feld[x][y] = Store[x][y];
2425 Store[x][y] = Store2[x][y] = 0;
2426 GfxElement[x][y] = EL_UNDEFINED;
2428 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2429 element = Feld[x][y] = Back[x][y];
2432 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2433 GfxDir[x][y] = MV_NO_MOVING;
2434 ChangeDelay[x][y] = 0;
2435 ChangePage[x][y] = -1;
2437 InitField(x, y, FALSE);
2438 if (CAN_MOVE(element))
2440 DrawLevelField(x, y);
2442 TestIfElementTouchesCustomElement(x, y);
2444 if (GFX_CRUMBLED(element))
2445 DrawLevelFieldCrumbledSandNeighbours(x, y);
2447 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2448 StorePlayer[x][y] = 0;
2450 if (ELEM_IS_PLAYER(element))
2451 RelocatePlayer(x, y, element);
2453 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2456 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2458 int stored = Store[x][y];
2459 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2460 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2463 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2466 DrawLevelFieldCrumbledSand(x, y);
2468 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2470 DrawLevelElement(x, y, Back[x][y]);
2471 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2473 else if (IS_WALKABLE_UNDER(Back[x][y]))
2475 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2476 DrawLevelElementThruMask(x, y, Back[x][y]);
2478 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2479 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2483 void DynaExplode(int ex, int ey)
2486 int dynabomb_size = 1;
2487 boolean dynabomb_xl = FALSE;
2488 struct PlayerInfo *player;
2489 static int xy[4][2] =
2497 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2499 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2500 dynabomb_size = player->dynabomb_size;
2501 dynabomb_xl = player->dynabomb_xl;
2502 player->dynabombs_left++;
2505 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2509 for (j=1; j<=dynabomb_size; j++)
2511 int x = ex + j * xy[i % 4][0];
2512 int y = ey + j * xy[i % 4][1];
2515 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2518 element = Feld[x][y];
2520 /* do not restart explosions of fields with active bombs */
2521 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2524 Explode(x, y, EX_PHASE_START, EX_BORDER);
2526 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2527 if (element != EL_EMPTY &&
2528 element != EL_SAND &&
2529 element != EL_EXPLOSION &&
2536 void Bang(int x, int y)
2539 int element = MovingOrBlocked2Element(x, y);
2541 int element = Feld[x][y];
2545 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2547 if (IS_PLAYER(x, y))
2550 struct PlayerInfo *player = PLAYERINFO(x, y);
2552 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2553 player->element_nr);
2558 PlaySoundLevelAction(x, y, ACTION_EXPLODING);
2560 if (game.emulation == EMU_SUPAPLEX)
2561 PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
2563 PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
2568 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2576 case EL_BD_BUTTERFLY:
2579 case EL_DARK_YAMYAM:
2583 RaiseScoreElement(element);
2584 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2586 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2587 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2588 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2589 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2590 case EL_DYNABOMB_INCREASE_NUMBER:
2591 case EL_DYNABOMB_INCREASE_SIZE:
2592 case EL_DYNABOMB_INCREASE_POWER:
2597 case EL_LAMP_ACTIVE:
2598 if (IS_PLAYER(x, y))
2599 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2601 Explode(x, y, EX_PHASE_START, EX_CENTER);
2604 if (CAN_EXPLODE_1X1(element))
2605 Explode(x, y, EX_PHASE_START, EX_CENTER);
2607 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2611 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2614 void SplashAcid(int x, int y)
2616 int element = Feld[x][y];
2618 if (element != EL_ACID_SPLASH_LEFT &&
2619 element != EL_ACID_SPLASH_RIGHT)
2621 PlaySoundLevel(x, y, SND_ACID_SPLASHING);
2623 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2624 (!IN_LEV_FIELD(x-1, y-1) ||
2625 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2626 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2628 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2629 (!IN_LEV_FIELD(x+1, y-1) ||
2630 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2631 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2635 static void InitBeltMovement()
2637 static int belt_base_element[4] =
2639 EL_CONVEYOR_BELT_1_LEFT,
2640 EL_CONVEYOR_BELT_2_LEFT,
2641 EL_CONVEYOR_BELT_3_LEFT,
2642 EL_CONVEYOR_BELT_4_LEFT
2644 static int belt_base_active_element[4] =
2646 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2647 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2648 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2649 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2654 /* set frame order for belt animation graphic according to belt direction */
2661 int element = belt_base_active_element[belt_nr] + j;
2662 int graphic = el2img(element);
2664 if (game.belt_dir[i] == MV_LEFT)
2665 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2667 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2671 for(y=0; y<lev_fieldy; y++)
2673 for(x=0; x<lev_fieldx; x++)
2675 int element = Feld[x][y];
2679 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2681 int e_belt_nr = getBeltNrFromBeltElement(element);
2684 if (e_belt_nr == belt_nr)
2686 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2688 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2696 static void ToggleBeltSwitch(int x, int y)
2698 static int belt_base_element[4] =
2700 EL_CONVEYOR_BELT_1_LEFT,
2701 EL_CONVEYOR_BELT_2_LEFT,
2702 EL_CONVEYOR_BELT_3_LEFT,
2703 EL_CONVEYOR_BELT_4_LEFT
2705 static int belt_base_active_element[4] =
2707 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2708 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2709 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2710 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2712 static int belt_base_switch_element[4] =
2714 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2715 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2716 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2717 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2719 static int belt_move_dir[4] =
2727 int element = Feld[x][y];
2728 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2729 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2730 int belt_dir = belt_move_dir[belt_dir_nr];
2733 if (!IS_BELT_SWITCH(element))
2736 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2737 game.belt_dir[belt_nr] = belt_dir;
2739 if (belt_dir_nr == 3)
2742 /* set frame order for belt animation graphic according to belt direction */
2745 int element = belt_base_active_element[belt_nr] + i;
2746 int graphic = el2img(element);
2748 if (belt_dir == MV_LEFT)
2749 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2751 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2754 for (yy=0; yy<lev_fieldy; yy++)
2756 for (xx=0; xx<lev_fieldx; xx++)
2758 int element = Feld[xx][yy];
2760 if (IS_BELT_SWITCH(element))
2762 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2764 if (e_belt_nr == belt_nr)
2766 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2767 DrawLevelField(xx, yy);
2770 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2772 int e_belt_nr = getBeltNrFromBeltElement(element);
2774 if (e_belt_nr == belt_nr)
2776 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2778 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2779 DrawLevelField(xx, yy);
2782 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2784 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2786 if (e_belt_nr == belt_nr)
2788 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2790 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2791 DrawLevelField(xx, yy);
2798 static void ToggleSwitchgateSwitch(int x, int y)
2802 game.switchgate_pos = !game.switchgate_pos;
2804 for (yy=0; yy<lev_fieldy; yy++)
2806 for (xx=0; xx<lev_fieldx; xx++)
2808 int element = Feld[xx][yy];
2810 if (element == EL_SWITCHGATE_SWITCH_UP ||
2811 element == EL_SWITCHGATE_SWITCH_DOWN)
2813 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2814 DrawLevelField(xx, yy);
2816 else if (element == EL_SWITCHGATE_OPEN ||
2817 element == EL_SWITCHGATE_OPENING)
2819 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2821 PlaySoundLevelAction(xx, yy, ACTION_CLOSING);
2823 PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2826 else if (element == EL_SWITCHGATE_CLOSED ||
2827 element == EL_SWITCHGATE_CLOSING)
2829 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2831 PlaySoundLevelAction(xx, yy, ACTION_OPENING);
2833 PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2840 static int getInvisibleActiveFromInvisibleElement(int element)
2842 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2843 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2844 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2848 static int getInvisibleFromInvisibleActiveElement(int element)
2850 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2851 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2852 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2856 static void RedrawAllLightSwitchesAndInvisibleElements()
2860 for (y=0; y<lev_fieldy; y++)
2862 for (x=0; x<lev_fieldx; x++)
2864 int element = Feld[x][y];
2866 if (element == EL_LIGHT_SWITCH &&
2867 game.light_time_left > 0)
2869 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2870 DrawLevelField(x, y);
2872 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2873 game.light_time_left == 0)
2875 Feld[x][y] = EL_LIGHT_SWITCH;
2876 DrawLevelField(x, y);
2878 else if (element == EL_INVISIBLE_STEELWALL ||
2879 element == EL_INVISIBLE_WALL ||
2880 element == EL_INVISIBLE_SAND)
2882 if (game.light_time_left > 0)
2883 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2885 DrawLevelField(x, y);
2887 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2888 element == EL_INVISIBLE_WALL_ACTIVE ||
2889 element == EL_INVISIBLE_SAND_ACTIVE)
2891 if (game.light_time_left == 0)
2892 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2894 DrawLevelField(x, y);
2900 static void ToggleLightSwitch(int x, int y)
2902 int element = Feld[x][y];
2904 game.light_time_left =
2905 (element == EL_LIGHT_SWITCH ?
2906 level.time_light * FRAMES_PER_SECOND : 0);
2908 RedrawAllLightSwitchesAndInvisibleElements();
2911 static void ActivateTimegateSwitch(int x, int y)
2915 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2917 for (yy=0; yy<lev_fieldy; yy++)
2919 for (xx=0; xx<lev_fieldx; xx++)
2921 int element = Feld[xx][yy];
2923 if (element == EL_TIMEGATE_CLOSED ||
2924 element == EL_TIMEGATE_CLOSING)
2926 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2927 PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2931 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2933 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2934 DrawLevelField(xx, yy);
2941 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2944 inline static int getElementMoveStepsize(int x, int y)
2946 int element = Feld[x][y];
2947 int direction = MovDir[x][y];
2948 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2949 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2950 int horiz_move = (dx != 0);
2951 int sign = (horiz_move ? dx : dy);
2952 int step = sign * element_info[element].move_stepsize;
2954 /* special values for move stepsize for spring and things on conveyor belt */
2957 if (CAN_FALL(element) &&
2958 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2959 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2960 else if (element == EL_SPRING)
2961 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2967 void Impact(int x, int y)
2969 boolean lastline = (y == lev_fieldy-1);
2970 boolean object_hit = FALSE;
2971 boolean impact = (lastline || object_hit);
2972 int element = Feld[x][y];
2973 int smashed = EL_UNDEFINED;
2975 if (!lastline) /* check if element below was hit */
2977 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2980 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2981 MovDir[x][y + 1] != MV_DOWN ||
2982 MovPos[x][y + 1] <= TILEY / 2));
2984 /* do not smash moving elements that left the smashed field in time */
2985 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2986 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2990 smashed = MovingOrBlocked2Element(x, y + 1);
2992 impact = (lastline || object_hit);
2995 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3001 /* only reset graphic animation if graphic really changes after impact */
3003 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3005 ResetGfxAnimation(x, y);
3006 DrawLevelField(x, y);
3009 if (impact && CAN_EXPLODE_IMPACT(element))
3014 else if (impact && element == EL_PEARL)
3016 Feld[x][y] = EL_PEARL_BREAKING;
3017 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3020 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3022 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3027 if (impact && element == EL_AMOEBA_DROP)
3029 if (object_hit && IS_PLAYER(x, y + 1))
3030 KillHeroUnlessProtected(x, y + 1);
3031 else if (object_hit && smashed == EL_PENGUIN)
3035 Feld[x][y] = EL_AMOEBA_GROWING;
3036 Store[x][y] = EL_AMOEBA_WET;
3038 ResetRandomAnimationValue(x, y);
3043 if (object_hit) /* check which object was hit */
3045 if (CAN_PASS_MAGIC_WALL(element) &&
3046 (smashed == EL_MAGIC_WALL ||
3047 smashed == EL_BD_MAGIC_WALL))
3050 int activated_magic_wall =
3051 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3052 EL_BD_MAGIC_WALL_ACTIVE);
3054 /* activate magic wall / mill */
3055 for (yy=0; yy<lev_fieldy; yy++)
3056 for (xx=0; xx<lev_fieldx; xx++)
3057 if (Feld[xx][yy] == smashed)
3058 Feld[xx][yy] = activated_magic_wall;
3060 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3061 game.magic_wall_active = TRUE;
3063 PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
3064 SND_MAGIC_WALL_ACTIVATING :
3065 SND_BD_MAGIC_WALL_ACTIVATING));
3068 if (IS_PLAYER(x, y + 1))
3070 if (CAN_SMASH_PLAYER(element))
3072 KillHeroUnlessProtected(x, y + 1);
3076 else if (smashed == EL_PENGUIN)
3078 if (CAN_SMASH_PLAYER(element))
3084 else if (element == EL_BD_DIAMOND)
3086 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3092 else if ((element == EL_SP_INFOTRON ||
3093 element == EL_SP_ZONK) &&
3094 (smashed == EL_SP_SNIKSNAK ||
3095 smashed == EL_SP_ELECTRON ||
3096 smashed == EL_SP_DISK_ORANGE))
3102 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3108 else if (CAN_SMASH_EVERYTHING(element))
3110 if (IS_CLASSIC_ENEMY(smashed) ||
3111 CAN_EXPLODE_SMASHED(smashed))
3116 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3118 if (smashed == EL_LAMP ||
3119 smashed == EL_LAMP_ACTIVE)
3124 else if (smashed == EL_NUT)
3126 Feld[x][y + 1] = EL_NUT_BREAKING;
3127 PlaySoundLevel(x, y, SND_NUT_BREAKING);
3128 RaiseScoreElement(EL_NUT);
3131 else if (smashed == EL_PEARL)
3133 Feld[x][y + 1] = EL_PEARL_BREAKING;
3134 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3137 else if (smashed == EL_DIAMOND)
3139 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3140 PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
3143 else if (IS_BELT_SWITCH(smashed))
3145 ToggleBeltSwitch(x, y + 1);
3147 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3148 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3150 ToggleSwitchgateSwitch(x, y + 1);
3152 else if (smashed == EL_LIGHT_SWITCH ||
3153 smashed == EL_LIGHT_SWITCH_ACTIVE)
3155 ToggleLightSwitch(x, y + 1);
3159 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3161 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3162 CE_OTHER_IS_SWITCHING);
3163 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3169 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3174 /* play sound of magic wall / mill */
3176 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3177 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3179 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3180 PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
3181 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3182 PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
3187 /* play sound of object that hits the ground */
3188 if (lastline || object_hit)
3189 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3192 inline static void TurnRoundExt(int x, int y)
3204 { 0, 0 }, { 0, 0 }, { 0, 0 },
3209 int left, right, back;
3213 { MV_DOWN, MV_UP, MV_RIGHT },
3214 { MV_UP, MV_DOWN, MV_LEFT },
3216 { MV_LEFT, MV_RIGHT, MV_DOWN },
3220 { MV_RIGHT, MV_LEFT, MV_UP }
3223 int element = Feld[x][y];
3224 int old_move_dir = MovDir[x][y];
3225 int left_dir = turn[old_move_dir].left;
3226 int right_dir = turn[old_move_dir].right;
3227 int back_dir = turn[old_move_dir].back;
3229 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3230 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3231 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3232 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3234 int left_x = x + left_dx, left_y = y + left_dy;
3235 int right_x = x + right_dx, right_y = y + right_dy;
3236 int move_x = x + move_dx, move_y = y + move_dy;
3240 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3242 TestIfBadThingTouchesOtherBadThing(x, y);
3244 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3245 MovDir[x][y] = right_dir;
3246 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3247 MovDir[x][y] = left_dir;
3249 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3251 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3254 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3255 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3257 TestIfBadThingTouchesOtherBadThing(x, y);
3259 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3260 MovDir[x][y] = left_dir;
3261 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3262 MovDir[x][y] = right_dir;
3264 if ((element == EL_SPACESHIP ||
3265 element == EL_SP_SNIKSNAK ||
3266 element == EL_SP_ELECTRON)
3267 && MovDir[x][y] != old_move_dir)
3269 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3272 else if (element == EL_YAMYAM)
3274 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3275 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3277 if (can_turn_left && can_turn_right)
3278 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3279 else if (can_turn_left)
3280 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3281 else if (can_turn_right)
3282 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3284 MovDir[x][y] = back_dir;
3286 MovDelay[x][y] = 16 + 16 * RND(3);
3288 else if (element == EL_DARK_YAMYAM)
3290 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3291 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3293 if (can_turn_left && can_turn_right)
3294 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3295 else if (can_turn_left)
3296 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3297 else if (can_turn_right)
3298 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3300 MovDir[x][y] = back_dir;
3302 MovDelay[x][y] = 16 + 16 * RND(3);
3304 else if (element == EL_PACMAN)
3306 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3307 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3309 if (can_turn_left && can_turn_right)
3310 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3311 else if (can_turn_left)
3312 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3313 else if (can_turn_right)
3314 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3316 MovDir[x][y] = back_dir;
3318 MovDelay[x][y] = 6 + RND(40);
3320 else if (element == EL_PIG)
3322 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3323 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3324 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3325 boolean should_turn_left, should_turn_right, should_move_on;
3327 int rnd = RND(rnd_value);
3329 should_turn_left = (can_turn_left &&
3331 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3332 y + back_dy + left_dy)));
3333 should_turn_right = (can_turn_right &&
3335 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3336 y + back_dy + right_dy)));
3337 should_move_on = (can_move_on &&
3340 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3341 y + move_dy + left_dy) ||
3342 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3343 y + move_dy + right_dy)));
3345 if (should_turn_left || should_turn_right || should_move_on)
3347 if (should_turn_left && should_turn_right && should_move_on)
3348 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3349 rnd < 2 * rnd_value / 3 ? right_dir :
3351 else if (should_turn_left && should_turn_right)
3352 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3353 else if (should_turn_left && should_move_on)
3354 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3355 else if (should_turn_right && should_move_on)
3356 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3357 else if (should_turn_left)
3358 MovDir[x][y] = left_dir;
3359 else if (should_turn_right)
3360 MovDir[x][y] = right_dir;
3361 else if (should_move_on)
3362 MovDir[x][y] = old_move_dir;
3364 else if (can_move_on && rnd > rnd_value / 8)
3365 MovDir[x][y] = old_move_dir;
3366 else if (can_turn_left && can_turn_right)
3367 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3368 else if (can_turn_left && rnd > rnd_value / 8)
3369 MovDir[x][y] = left_dir;
3370 else if (can_turn_right && rnd > rnd_value/8)
3371 MovDir[x][y] = right_dir;
3373 MovDir[x][y] = back_dir;
3375 xx = x + move_xy[MovDir[x][y]].x;
3376 yy = y + move_xy[MovDir[x][y]].y;
3378 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3379 MovDir[x][y] = old_move_dir;
3383 else if (element == EL_DRAGON)
3385 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3386 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3387 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3389 int rnd = RND(rnd_value);
3391 if (can_move_on && rnd > rnd_value / 8)
3392 MovDir[x][y] = old_move_dir;
3393 else if (can_turn_left && can_turn_right)
3394 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3395 else if (can_turn_left && rnd > rnd_value / 8)
3396 MovDir[x][y] = left_dir;
3397 else if (can_turn_right && rnd > rnd_value / 8)
3398 MovDir[x][y] = right_dir;
3400 MovDir[x][y] = back_dir;
3402 xx = x + move_xy[MovDir[x][y]].x;
3403 yy = y + move_xy[MovDir[x][y]].y;
3405 if (!IS_FREE(xx, yy))
3406 MovDir[x][y] = old_move_dir;
3410 else if (element == EL_MOLE)
3412 boolean can_move_on =
3413 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3414 IS_AMOEBOID(Feld[move_x][move_y]) ||
3415 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3418 boolean can_turn_left =
3419 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3420 IS_AMOEBOID(Feld[left_x][left_y])));
3422 boolean can_turn_right =
3423 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3424 IS_AMOEBOID(Feld[right_x][right_y])));
3426 if (can_turn_left && can_turn_right)
3427 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3428 else if (can_turn_left)
3429 MovDir[x][y] = left_dir;
3431 MovDir[x][y] = right_dir;
3434 if (MovDir[x][y] != old_move_dir)
3437 else if (element == EL_BALLOON)
3439 MovDir[x][y] = game.balloon_dir;
3442 else if (element == EL_SPRING)
3444 if (MovDir[x][y] & MV_HORIZONTAL &&
3445 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3446 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3447 MovDir[x][y] = MV_NO_MOVING;
3451 else if (element == EL_ROBOT ||
3452 element == EL_SATELLITE ||
3453 element == EL_PENGUIN)
3455 int attr_x = -1, attr_y = -1;
3466 for (i=0; i<MAX_PLAYERS; i++)
3468 struct PlayerInfo *player = &stored_player[i];
3469 int jx = player->jx, jy = player->jy;
3471 if (!player->active)
3475 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3483 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3489 if (element == EL_PENGUIN)
3492 static int xy[4][2] =
3502 int ex = x + xy[i % 4][0];
3503 int ey = y + xy[i % 4][1];
3505 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3514 MovDir[x][y] = MV_NO_MOVING;
3516 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3517 else if (attr_x > x)
3518 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3520 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3521 else if (attr_y > y)
3522 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3524 if (element == EL_ROBOT)
3528 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3529 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3530 Moving2Blocked(x, y, &newx, &newy);
3532 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3533 MovDelay[x][y] = 8 + 8 * !RND(3);
3535 MovDelay[x][y] = 16;
3537 else if (element == EL_PENGUIN)
3543 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3545 boolean first_horiz = RND(2);
3546 int new_move_dir = MovDir[x][y];
3549 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3550 Moving2Blocked(x, y, &newx, &newy);
3552 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3556 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3557 Moving2Blocked(x, y, &newx, &newy);
3559 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3562 MovDir[x][y] = old_move_dir;
3566 else /* (element == EL_SATELLITE) */
3572 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3574 boolean first_horiz = RND(2);
3575 int new_move_dir = MovDir[x][y];
3578 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3579 Moving2Blocked(x, y, &newx, &newy);
3581 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3585 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3586 Moving2Blocked(x, y, &newx, &newy);
3588 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3591 MovDir[x][y] = old_move_dir;
3596 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3597 element_info[element].move_pattern == MV_TURNING_LEFT ||
3598 element_info[element].move_pattern == MV_TURNING_RIGHT)
3600 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3601 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3603 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3604 MovDir[x][y] = left_dir;
3605 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3606 MovDir[x][y] = right_dir;
3607 else if (can_turn_left && can_turn_right)
3608 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3609 else if (can_turn_left)
3610 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3611 else if (can_turn_right)
3612 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3614 MovDir[x][y] = back_dir;
3616 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3618 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3619 element_info[element].move_pattern == MV_VERTICAL)
3621 if (element_info[element].move_pattern & old_move_dir)
3622 MovDir[x][y] = back_dir;
3623 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3624 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3625 else if (element_info[element].move_pattern == MV_VERTICAL)
3626 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3628 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3630 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3632 MovDir[x][y] = element_info[element].move_pattern;
3633 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3635 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3637 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3638 MovDir[x][y] = left_dir;
3639 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3640 MovDir[x][y] = right_dir;
3642 if (MovDir[x][y] != old_move_dir)
3643 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3645 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3647 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3648 MovDir[x][y] = right_dir;
3649 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3650 MovDir[x][y] = left_dir;
3652 if (MovDir[x][y] != old_move_dir)
3653 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3655 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3656 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3658 int attr_x = -1, attr_y = -1;
3661 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3672 for (i=0; i<MAX_PLAYERS; i++)
3674 struct PlayerInfo *player = &stored_player[i];
3675 int jx = player->jx, jy = player->jy;
3677 if (!player->active)
3681 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3689 MovDir[x][y] = MV_NO_MOVING;
3691 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3692 else if (attr_x > x)
3693 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3695 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3696 else if (attr_y > y)
3697 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3699 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3701 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3703 boolean first_horiz = RND(2);
3704 int new_move_dir = MovDir[x][y];
3707 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3708 Moving2Blocked(x, y, &newx, &newy);
3710 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3714 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3715 Moving2Blocked(x, y, &newx, &newy);
3717 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3720 MovDir[x][y] = old_move_dir;
3723 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3725 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3726 MovDir[x][y] = MV_NO_MOVING;
3732 static void TurnRound(int x, int y)
3734 int direction = MovDir[x][y];
3737 GfxDir[x][y] = MovDir[x][y];
3743 GfxDir[x][y] = MovDir[x][y];
3746 if (direction != MovDir[x][y])
3751 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3754 GfxAction[x][y] = ACTION_WAITING;
3758 static boolean JustBeingPushed(int x, int y)
3762 for (i=0; i<MAX_PLAYERS; i++)
3764 struct PlayerInfo *player = &stored_player[i];
3766 if (player->active && player->is_pushing && player->MovPos)
3768 int next_jx = player->jx + (player->jx - player->last_jx);
3769 int next_jy = player->jy + (player->jy - player->last_jy);
3771 if (x == next_jx && y == next_jy)
3779 void StartMoving(int x, int y)
3781 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3782 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3783 int element = Feld[x][y];
3789 if (MovDelay[x][y] == 0)
3790 GfxAction[x][y] = ACTION_DEFAULT;
3792 /* !!! this should be handled more generic (not only for mole) !!! */
3793 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3794 GfxAction[x][y] = ACTION_DEFAULT;
3797 if (CAN_FALL(element) && y < lev_fieldy - 1)
3799 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3800 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3801 if (JustBeingPushed(x, y))
3804 if (element == EL_QUICKSAND_FULL)
3806 if (IS_FREE(x, y + 1))
3808 InitMovingField(x, y, MV_DOWN);
3809 started_moving = TRUE;
3811 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3812 Store[x][y] = EL_ROCK;
3814 PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3816 PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3819 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3821 if (!MovDelay[x][y])
3822 MovDelay[x][y] = TILEY + 1;
3831 Feld[x][y] = EL_QUICKSAND_EMPTY;
3832 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3833 Store[x][y + 1] = Store[x][y];
3836 PlaySoundLevelAction(x, y, ACTION_FILLING);
3838 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3842 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3843 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3845 InitMovingField(x, y, MV_DOWN);
3846 started_moving = TRUE;
3848 Feld[x][y] = EL_QUICKSAND_FILLING;
3849 Store[x][y] = element;
3851 PlaySoundLevelAction(x, y, ACTION_FILLING);
3853 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3856 else if (element == EL_MAGIC_WALL_FULL)
3858 if (IS_FREE(x, y + 1))
3860 InitMovingField(x, y, MV_DOWN);
3861 started_moving = TRUE;
3863 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3864 Store[x][y] = EL_CHANGED(Store[x][y]);
3866 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3868 if (!MovDelay[x][y])
3869 MovDelay[x][y] = TILEY/4 + 1;
3878 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3879 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3880 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3884 else if (element == EL_BD_MAGIC_WALL_FULL)
3886 if (IS_FREE(x, y + 1))
3888 InitMovingField(x, y, MV_DOWN);
3889 started_moving = TRUE;
3891 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3892 Store[x][y] = EL_CHANGED2(Store[x][y]);
3894 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3896 if (!MovDelay[x][y])
3897 MovDelay[x][y] = TILEY/4 + 1;
3906 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3907 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3908 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3912 else if (CAN_PASS_MAGIC_WALL(element) &&
3913 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3914 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3916 InitMovingField(x, y, MV_DOWN);
3917 started_moving = TRUE;
3920 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3921 EL_BD_MAGIC_WALL_FILLING);
3922 Store[x][y] = element;
3925 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3927 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3932 InitMovingField(x, y, MV_DOWN);
3933 started_moving = TRUE;
3935 Store[x][y] = EL_ACID;
3937 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3938 GfxAction[x][y + 1] = ACTION_ACTIVE;
3942 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3943 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3944 (Feld[x][y + 1] == EL_BLOCKED)) ||
3945 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3946 CAN_SMASH(element) && WasJustFalling[x][y] &&
3947 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3951 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3952 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3953 WasJustMoving[x][y] && !Pushed[x][y + 1])
3955 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3956 WasJustMoving[x][y])
3961 /* this is needed for a special case not covered by calling "Impact()"
3962 from "ContinueMoving()": if an element moves to a tile directly below
3963 another element which was just falling on that tile (which was empty
3964 in the previous frame), the falling element above would just stop
3965 instead of smashing the element below (in previous version, the above
3966 element was just checked for "moving" instead of "falling", resulting
3967 in incorrect smashes caused by horizontal movement of the above
3968 element; also, the case of the player being the element to smash was
3969 simply not covered here... :-/ ) */
3973 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
3975 if (MovDir[x][y] == MV_NO_MOVING)
3977 InitMovingField(x, y, MV_DOWN);
3978 started_moving = TRUE;
3981 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
3983 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
3984 MovDir[x][y] = MV_DOWN;
3986 InitMovingField(x, y, MV_DOWN);
3987 started_moving = TRUE;
3989 else if (element == EL_AMOEBA_DROP)
3991 Feld[x][y] = EL_AMOEBA_GROWING;
3992 Store[x][y] = EL_AMOEBA_WET;
3994 /* Store[x][y + 1] must be zero, because:
3995 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
3998 #if OLD_GAME_BEHAVIOUR
3999 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4001 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4002 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4003 element != EL_DX_SUPABOMB)
4006 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4007 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4008 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4009 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4012 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4013 (IS_FREE(x - 1, y + 1) ||
4014 Feld[x - 1][y + 1] == EL_ACID));
4015 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4016 (IS_FREE(x + 1, y + 1) ||
4017 Feld[x + 1][y + 1] == EL_ACID));
4018 boolean can_fall_any = (can_fall_left || can_fall_right);
4019 boolean can_fall_both = (can_fall_left && can_fall_right);
4021 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4023 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4025 if (slippery_type == SLIPPERY_ONLY_LEFT)
4026 can_fall_right = FALSE;
4027 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4028 can_fall_left = FALSE;
4029 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4030 can_fall_right = FALSE;
4031 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4032 can_fall_left = FALSE;
4034 can_fall_any = (can_fall_left || can_fall_right);
4035 can_fall_both = (can_fall_left && can_fall_right);
4040 if (can_fall_both &&
4041 (game.emulation != EMU_BOULDERDASH &&
4042 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4043 can_fall_left = !(can_fall_right = RND(2));
4045 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4046 started_moving = TRUE;
4049 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4051 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4052 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4053 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4054 int belt_dir = game.belt_dir[belt_nr];
4056 if ((belt_dir == MV_LEFT && left_is_free) ||
4057 (belt_dir == MV_RIGHT && right_is_free))
4059 InitMovingField(x, y, belt_dir);
4060 started_moving = TRUE;
4062 GfxAction[x][y] = ACTION_DEFAULT;
4067 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4068 if (CAN_MOVE(element) && !started_moving)
4073 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4076 if ((element == EL_SATELLITE ||
4077 element == EL_BALLOON ||
4078 element == EL_SPRING)
4079 && JustBeingPushed(x, y))
4085 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4086 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4088 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4090 Moving2Blocked(x, y, &newx, &newy);
4091 if (Feld[newx][newy] == EL_BLOCKED)
4092 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4097 if (!MovDelay[x][y]) /* start new movement phase */
4099 /* all objects that can change their move direction after each step
4100 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4102 if (element != EL_YAMYAM &&
4103 element != EL_DARK_YAMYAM &&
4104 element != EL_PACMAN &&
4105 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4106 element_info[element].move_pattern != MV_TURNING_LEFT &&
4107 element_info[element].move_pattern != MV_TURNING_RIGHT)
4111 if (MovDelay[x][y] && (element == EL_BUG ||
4112 element == EL_SPACESHIP ||
4113 element == EL_SP_SNIKSNAK ||
4114 element == EL_SP_ELECTRON ||
4115 element == EL_MOLE))
4116 DrawLevelField(x, y);
4120 if (MovDelay[x][y]) /* wait some time before next movement */
4125 if (element == EL_YAMYAM)
4128 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4129 DrawLevelElementAnimation(x, y, element);
4133 if (MovDelay[x][y]) /* element still has to wait some time */
4136 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4137 ResetGfxAnimation(x, y);
4141 if (GfxAction[x][y] != ACTION_WAITING)
4142 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4144 GfxAction[x][y] = ACTION_WAITING;
4148 if (element == EL_ROBOT ||
4150 element == EL_PACMAN ||
4152 element == EL_YAMYAM ||
4153 element == EL_DARK_YAMYAM)
4156 DrawLevelElementAnimation(x, y, element);
4158 DrawLevelElementAnimationIfNeeded(x, y, element);
4160 PlaySoundLevelAction(x, y, ACTION_WAITING);
4162 else if (element == EL_SP_ELECTRON)
4163 DrawLevelElementAnimationIfNeeded(x, y, element);
4164 else if (element == EL_DRAGON)
4167 int dir = MovDir[x][y];
4168 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4169 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4170 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4171 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4172 dir == MV_UP ? IMG_FLAMES_1_UP :
4173 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4174 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4177 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4180 GfxAction[x][y] = ACTION_ATTACKING;
4182 if (IS_PLAYER(x, y))
4183 DrawPlayerField(x, y);
4185 DrawLevelField(x, y);
4187 PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING);
4189 for (i=1; i <= 3; i++)
4191 int xx = x + i * dx;
4192 int yy = y + i * dy;
4193 int sx = SCREENX(xx);
4194 int sy = SCREENY(yy);
4195 int flame_graphic = graphic + (i - 1);
4197 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4202 int flamed = MovingOrBlocked2Element(xx, yy);
4204 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4207 RemoveMovingField(xx, yy);
4209 Feld[xx][yy] = EL_FLAMES;
4210 if (IN_SCR_FIELD(sx, sy))
4212 DrawLevelFieldCrumbledSand(xx, yy);
4213 DrawGraphic(sx, sy, flame_graphic, frame);
4218 if (Feld[xx][yy] == EL_FLAMES)
4219 Feld[xx][yy] = EL_EMPTY;
4220 DrawLevelField(xx, yy);
4225 if (MovDelay[x][y]) /* element still has to wait some time */
4227 PlaySoundLevelAction(x, y, ACTION_WAITING);
4233 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4234 for all other elements GfxAction will be set by InitMovingField() */
4235 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4236 GfxAction[x][y] = ACTION_MOVING;
4240 /* now make next step */
4242 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4244 if (DONT_COLLIDE_WITH(element) &&
4245 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4246 !PLAYER_PROTECTED(newx, newy))
4249 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4252 /* player killed by element which is deadly when colliding with */
4254 KillHero(PLAYERINFO(newx, newy));
4259 else if ((element == EL_PENGUIN ||
4260 element == EL_ROBOT ||
4261 element == EL_SATELLITE ||
4262 element == EL_BALLOON ||
4263 IS_CUSTOM_ELEMENT(element)) &&
4264 IN_LEV_FIELD(newx, newy) &&
4265 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4268 Store[x][y] = EL_ACID;
4270 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4272 if (Feld[newx][newy] == EL_EXIT_OPEN)
4274 Feld[x][y] = EL_EMPTY;
4275 DrawLevelField(x, y);
4277 PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4278 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4279 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4281 local_player->friends_still_needed--;
4282 if (!local_player->friends_still_needed &&
4283 !local_player->GameOver && AllPlayersGone)
4284 local_player->LevelSolved = local_player->GameOver = TRUE;
4288 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4290 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4291 DrawLevelField(newx, newy);
4293 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4295 else if (!IS_FREE(newx, newy))
4297 GfxAction[x][y] = ACTION_WAITING;
4299 if (IS_PLAYER(x, y))
4300 DrawPlayerField(x, y);
4302 DrawLevelField(x, y);
4306 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4308 if (IS_FOOD_PIG(Feld[newx][newy]))
4310 if (IS_MOVING(newx, newy))
4311 RemoveMovingField(newx, newy);
4314 Feld[newx][newy] = EL_EMPTY;
4315 DrawLevelField(newx, newy);
4318 PlaySoundLevel(x, y, SND_PIG_DIGGING);
4320 else if (!IS_FREE(newx, newy))
4322 if (IS_PLAYER(x, y))
4323 DrawPlayerField(x, y);
4325 DrawLevelField(x, y);
4329 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4331 if (!IS_FREE(newx, newy))
4333 if (IS_PLAYER(x, y))
4334 DrawPlayerField(x, y);
4336 DrawLevelField(x, y);
4342 boolean wanna_flame = !RND(10);
4343 int dx = newx - x, dy = newy - y;
4344 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4345 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4346 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4347 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4348 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4349 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4352 IS_CLASSIC_ENEMY(element1) ||
4353 IS_CLASSIC_ENEMY(element2)) &&
4354 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4355 element1 != EL_FLAMES && element2 != EL_FLAMES)
4358 ResetGfxAnimation(x, y);
4359 GfxAction[x][y] = ACTION_ATTACKING;
4362 if (IS_PLAYER(x, y))
4363 DrawPlayerField(x, y);
4365 DrawLevelField(x, y);
4367 PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4369 MovDelay[x][y] = 50;
4371 Feld[newx][newy] = EL_FLAMES;
4372 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4373 Feld[newx1][newy1] = EL_FLAMES;
4374 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4375 Feld[newx2][newy2] = EL_FLAMES;
4381 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4382 Feld[newx][newy] == EL_DIAMOND)
4384 if (IS_MOVING(newx, newy))
4385 RemoveMovingField(newx, newy);
4388 Feld[newx][newy] = EL_EMPTY;
4389 DrawLevelField(newx, newy);
4392 PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4394 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4395 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4397 if (AmoebaNr[newx][newy])
4399 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4400 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4401 Feld[newx][newy] == EL_BD_AMOEBA)
4402 AmoebaCnt[AmoebaNr[newx][newy]]--;
4405 if (IS_MOVING(newx, newy))
4406 RemoveMovingField(newx, newy);
4409 Feld[newx][newy] = EL_EMPTY;
4410 DrawLevelField(newx, newy);
4413 PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4415 else if ((element == EL_PACMAN || element == EL_MOLE)
4416 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4418 if (AmoebaNr[newx][newy])
4420 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4421 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4422 Feld[newx][newy] == EL_BD_AMOEBA)
4423 AmoebaCnt[AmoebaNr[newx][newy]]--;
4426 if (element == EL_MOLE)
4428 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4429 PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4431 ResetGfxAnimation(x, y);
4432 GfxAction[x][y] = ACTION_DIGGING;
4433 DrawLevelField(x, y);
4435 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4436 return; /* wait for shrinking amoeba */
4438 else /* element == EL_PACMAN */
4440 Feld[newx][newy] = EL_EMPTY;
4441 DrawLevelField(newx, newy);
4442 PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4445 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4446 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4447 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4449 /* wait for shrinking amoeba to completely disappear */
4452 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4454 /* object was running against a wall */
4459 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4460 DrawLevelElementAnimation(x, y, element);
4462 if (element == EL_BUG ||
4463 element == EL_SPACESHIP ||
4464 element == EL_SP_SNIKSNAK)
4465 DrawLevelField(x, y);
4466 else if (element == EL_MOLE)
4467 DrawLevelField(x, y);
4468 else if (element == EL_BD_BUTTERFLY ||
4469 element == EL_BD_FIREFLY)
4470 DrawLevelElementAnimationIfNeeded(x, y, element);
4471 else if (element == EL_SATELLITE)
4472 DrawLevelElementAnimationIfNeeded(x, y, element);
4473 else if (element == EL_SP_ELECTRON)
4474 DrawLevelElementAnimationIfNeeded(x, y, element);
4477 if (DONT_TOUCH(element))
4478 TestIfBadThingTouchesHero(x, y);
4481 PlaySoundLevelAction(x, y, ACTION_WAITING);
4487 InitMovingField(x, y, MovDir[x][y]);
4489 PlaySoundLevelAction(x, y, ACTION_MOVING);
4493 ContinueMoving(x, y);
4496 void ContinueMoving(int x, int y)
4498 int element = Feld[x][y];
4499 int direction = MovDir[x][y];
4500 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4501 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4502 int newx = x + dx, newy = y + dy;
4503 int nextx = newx + dx, nexty = newy + dy;
4504 boolean pushed = Pushed[x][y];
4506 MovPos[x][y] += getElementMoveStepsize(x, y);
4508 if (pushed) /* special case: moving object pushed by player */
4509 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4511 if (ABS(MovPos[x][y]) < TILEX)
4513 DrawLevelField(x, y);
4515 return; /* element is still moving */
4518 /* element reached destination field */
4520 Feld[x][y] = EL_EMPTY;
4521 Feld[newx][newy] = element;
4522 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4524 if (element == EL_MOLE)
4526 Feld[x][y] = EL_SAND;
4528 DrawLevelFieldCrumbledSandNeighbours(x, y);
4530 else if (element == EL_QUICKSAND_FILLING)
4532 element = Feld[newx][newy] = get_next_element(element);
4533 Store[newx][newy] = Store[x][y];
4535 else if (element == EL_QUICKSAND_EMPTYING)
4537 Feld[x][y] = get_next_element(element);
4538 element = Feld[newx][newy] = Store[x][y];
4540 else if (element == EL_MAGIC_WALL_FILLING)
4542 element = Feld[newx][newy] = get_next_element(element);
4543 if (!game.magic_wall_active)
4544 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4545 Store[newx][newy] = Store[x][y];
4547 else if (element == EL_MAGIC_WALL_EMPTYING)
4549 Feld[x][y] = get_next_element(element);
4550 if (!game.magic_wall_active)
4551 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4552 element = Feld[newx][newy] = Store[x][y];
4554 else if (element == EL_BD_MAGIC_WALL_FILLING)
4556 element = Feld[newx][newy] = get_next_element(element);
4557 if (!game.magic_wall_active)
4558 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4559 Store[newx][newy] = Store[x][y];
4561 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4563 Feld[x][y] = get_next_element(element);
4564 if (!game.magic_wall_active)
4565 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4566 element = Feld[newx][newy] = Store[x][y];
4568 else if (element == EL_AMOEBA_DROPPING)
4570 Feld[x][y] = get_next_element(element);
4571 element = Feld[newx][newy] = Store[x][y];
4573 else if (element == EL_SOKOBAN_OBJECT)
4576 Feld[x][y] = Back[x][y];
4578 if (Back[newx][newy])
4579 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4581 Back[x][y] = Back[newx][newy] = 0;
4583 else if (Store[x][y] == EL_ACID)
4585 element = Feld[newx][newy] = EL_ACID;
4589 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4590 MovDelay[newx][newy] = 0;
4592 /* copy element change control values to new field */
4593 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4594 ChangePage[newx][newy] = ChangePage[x][y];
4595 Changed[newx][newy] = Changed[x][y];
4596 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4598 ChangeDelay[x][y] = 0;
4599 ChangePage[x][y] = -1;
4600 Changed[x][y] = CE_BITMASK_DEFAULT;
4601 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4603 /* copy animation control values to new field */
4604 GfxFrame[newx][newy] = GfxFrame[x][y];
4605 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4606 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4607 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4609 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4611 ResetGfxAnimation(x, y); /* reset animation values for old field */
4614 /* 2.1.1 (does not work correctly for spring) */
4615 if (!CAN_MOVE(element))
4616 MovDir[newx][newy] = 0;
4620 /* (does not work for falling objects that slide horizontally) */
4621 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4622 MovDir[newx][newy] = 0;
4625 if (!CAN_MOVE(element) ||
4626 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4627 MovDir[newx][newy] = 0;
4630 if (!CAN_MOVE(element) ||
4631 (CAN_FALL(element) && direction == MV_DOWN))
4632 GfxDir[x][y] = MovDir[newx][newy] = 0;
4637 DrawLevelField(x, y);
4638 DrawLevelField(newx, newy);
4640 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4642 /* prevent pushed element from moving on in pushed direction */
4643 if (pushed && CAN_MOVE(element) &&
4644 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4645 !(element_info[element].move_pattern & direction))
4646 TurnRound(newx, newy);
4648 if (!pushed) /* special case: moving object pushed by player */
4650 WasJustMoving[newx][newy] = 3;
4652 if (CAN_FALL(element) && direction == MV_DOWN)
4653 WasJustFalling[newx][newy] = 3;
4656 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4658 TestIfBadThingTouchesHero(newx, newy);
4659 TestIfBadThingTouchesFriend(newx, newy);
4660 TestIfBadThingTouchesOtherBadThing(newx, newy);
4662 else if (element == EL_PENGUIN)
4663 TestIfFriendTouchesBadThing(newx, newy);
4665 if (CAN_FALL(element) && direction == MV_DOWN &&
4666 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4670 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4674 if (ChangePage[newx][newy] != -1) /* delayed change */
4675 ChangeElement(newx, newy, ChangePage[newx][newy]);
4678 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4679 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4682 TestIfPlayerTouchesCustomElement(newx, newy);
4683 TestIfElementTouchesCustomElement(newx, newy);
4686 int AmoebeNachbarNr(int ax, int ay)
4689 int element = Feld[ax][ay];
4691 static int xy[4][2] =
4701 int x = ax + xy[i][0];
4702 int y = ay + xy[i][1];
4704 if (!IN_LEV_FIELD(x, y))
4707 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4708 group_nr = AmoebaNr[x][y];
4714 void AmoebenVereinigen(int ax, int ay)
4716 int i, x, y, xx, yy;
4717 int new_group_nr = AmoebaNr[ax][ay];
4718 static int xy[4][2] =
4726 if (new_group_nr == 0)
4734 if (!IN_LEV_FIELD(x, y))
4737 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4738 Feld[x][y] == EL_BD_AMOEBA ||
4739 Feld[x][y] == EL_AMOEBA_DEAD) &&
4740 AmoebaNr[x][y] != new_group_nr)
4742 int old_group_nr = AmoebaNr[x][y];
4744 if (old_group_nr == 0)
4747 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4748 AmoebaCnt[old_group_nr] = 0;
4749 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4750 AmoebaCnt2[old_group_nr] = 0;
4752 for (yy=0; yy<lev_fieldy; yy++)
4754 for (xx=0; xx<lev_fieldx; xx++)
4756 if (AmoebaNr[xx][yy] == old_group_nr)
4757 AmoebaNr[xx][yy] = new_group_nr;
4764 void AmoebeUmwandeln(int ax, int ay)
4768 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4770 int group_nr = AmoebaNr[ax][ay];
4775 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4776 printf("AmoebeUmwandeln(): This should never happen!\n");
4781 for (y=0; y<lev_fieldy; y++)
4783 for (x=0; x<lev_fieldx; x++)
4785 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4788 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4792 PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4793 SND_AMOEBA_TURNING_TO_GEM :
4794 SND_AMOEBA_TURNING_TO_ROCK));
4799 static int xy[4][2] =
4812 if (!IN_LEV_FIELD(x, y))
4815 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4817 PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4818 SND_AMOEBA_TURNING_TO_GEM :
4819 SND_AMOEBA_TURNING_TO_ROCK));
4826 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4829 int group_nr = AmoebaNr[ax][ay];
4830 boolean done = FALSE;
4835 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4836 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4841 for (y=0; y<lev_fieldy; y++)
4843 for (x=0; x<lev_fieldx; x++)
4845 if (AmoebaNr[x][y] == group_nr &&
4846 (Feld[x][y] == EL_AMOEBA_DEAD ||
4847 Feld[x][y] == EL_BD_AMOEBA ||
4848 Feld[x][y] == EL_AMOEBA_GROWING))
4851 Feld[x][y] = new_element;
4852 InitField(x, y, FALSE);
4853 DrawLevelField(x, y);
4860 PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4861 SND_BD_AMOEBA_TURNING_TO_ROCK :
4862 SND_BD_AMOEBA_TURNING_TO_GEM));
4865 void AmoebeWaechst(int x, int y)
4867 static unsigned long sound_delay = 0;
4868 static unsigned long sound_delay_value = 0;
4870 if (!MovDelay[x][y]) /* start new growing cycle */
4874 if (DelayReached(&sound_delay, sound_delay_value))
4877 PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4879 if (Store[x][y] == EL_BD_AMOEBA)
4880 PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4882 PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4884 sound_delay_value = 30;
4888 if (MovDelay[x][y]) /* wait some time before growing bigger */
4891 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4893 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4894 6 - MovDelay[x][y]);
4896 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4899 if (!MovDelay[x][y])
4901 Feld[x][y] = Store[x][y];
4903 DrawLevelField(x, y);
4908 void AmoebaDisappearing(int x, int y)
4910 static unsigned long sound_delay = 0;
4911 static unsigned long sound_delay_value = 0;
4913 if (!MovDelay[x][y]) /* start new shrinking cycle */
4917 if (DelayReached(&sound_delay, sound_delay_value))
4918 sound_delay_value = 30;
4921 if (MovDelay[x][y]) /* wait some time before shrinking */
4924 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4926 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4927 6 - MovDelay[x][y]);
4929 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4932 if (!MovDelay[x][y])
4934 Feld[x][y] = EL_EMPTY;
4935 DrawLevelField(x, y);
4937 /* don't let mole enter this field in this cycle;
4938 (give priority to objects falling to this field from above) */
4944 void AmoebeAbleger(int ax, int ay)
4947 int element = Feld[ax][ay];
4948 int graphic = el2img(element);
4949 int newax = ax, neway = ay;
4950 static int xy[4][2] =
4958 if (!level.amoeba_speed)
4960 Feld[ax][ay] = EL_AMOEBA_DEAD;
4961 DrawLevelField(ax, ay);
4965 if (IS_ANIMATED(graphic))
4966 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4968 if (!MovDelay[ax][ay]) /* start making new amoeba field */
4969 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4971 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
4974 if (MovDelay[ax][ay])
4978 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4981 int x = ax + xy[start][0];
4982 int y = ay + xy[start][1];
4984 if (!IN_LEV_FIELD(x, y))
4987 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4988 if (IS_FREE(x, y) ||
4989 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4995 if (newax == ax && neway == ay)
4998 else /* normal or "filled" (BD style) amoeba */
5001 boolean waiting_for_player = FALSE;
5005 int j = (start + i) % 4;
5006 int x = ax + xy[j][0];
5007 int y = ay + xy[j][1];
5009 if (!IN_LEV_FIELD(x, y))
5012 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5013 if (IS_FREE(x, y) ||
5014 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5020 else if (IS_PLAYER(x, y))
5021 waiting_for_player = TRUE;
5024 if (newax == ax && neway == ay) /* amoeba cannot grow */
5026 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5028 Feld[ax][ay] = EL_AMOEBA_DEAD;
5029 DrawLevelField(ax, ay);
5030 AmoebaCnt[AmoebaNr[ax][ay]]--;
5032 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5034 if (element == EL_AMOEBA_FULL)
5035 AmoebeUmwandeln(ax, ay);
5036 else if (element == EL_BD_AMOEBA)
5037 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5042 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5044 /* amoeba gets larger by growing in some direction */
5046 int new_group_nr = AmoebaNr[ax][ay];
5049 if (new_group_nr == 0)
5051 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5052 printf("AmoebeAbleger(): This should never happen!\n");
5057 AmoebaNr[newax][neway] = new_group_nr;
5058 AmoebaCnt[new_group_nr]++;
5059 AmoebaCnt2[new_group_nr]++;
5061 /* if amoeba touches other amoeba(s) after growing, unify them */
5062 AmoebenVereinigen(newax, neway);
5064 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5066 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5072 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5073 (neway == lev_fieldy - 1 && newax != ax))
5075 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5076 Store[newax][neway] = element;
5078 else if (neway == ay)
5080 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5082 PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5084 PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5089 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5090 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5091 Store[ax][ay] = EL_AMOEBA_DROP;
5092 ContinueMoving(ax, ay);
5096 DrawLevelField(newax, neway);
5099 void Life(int ax, int ay)
5102 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5104 int element = Feld[ax][ay];
5105 int graphic = el2img(element);
5106 boolean changed = FALSE;
5108 if (IS_ANIMATED(graphic))
5109 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5114 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5115 MovDelay[ax][ay] = life_time;
5117 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5120 if (MovDelay[ax][ay])
5124 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5126 int xx = ax+x1, yy = ay+y1;
5129 if (!IN_LEV_FIELD(xx, yy))
5132 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5134 int x = xx+x2, y = yy+y2;
5136 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5139 if (((Feld[x][y] == element ||
5140 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5142 (IS_FREE(x, y) && Stop[x][y]))
5146 if (xx == ax && yy == ay) /* field in the middle */
5148 if (nachbarn < life[0] || nachbarn > life[1])
5150 Feld[xx][yy] = EL_EMPTY;
5152 DrawLevelField(xx, yy);
5153 Stop[xx][yy] = TRUE;
5157 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5158 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5159 { /* free border field */
5160 if (nachbarn >= life[2] && nachbarn <= life[3])
5162 Feld[xx][yy] = element;
5163 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5165 DrawLevelField(xx, yy);
5166 Stop[xx][yy] = TRUE;
5173 PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5174 SND_GAME_OF_LIFE_GROWING);
5177 static void InitRobotWheel(int x, int y)
5179 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5182 static void RunRobotWheel(int x, int y)
5184 PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5187 static void StopRobotWheel(int x, int y)
5189 if (ZX == x && ZY == y)
5193 static void InitTimegateWheel(int x, int y)
5195 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5198 static void RunTimegateWheel(int x, int y)
5200 PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5203 void CheckExit(int x, int y)
5205 if (local_player->gems_still_needed > 0 ||
5206 local_player->sokobanfields_still_needed > 0 ||
5207 local_player->lights_still_needed > 0)
5209 int element = Feld[x][y];
5210 int graphic = el2img(element);
5212 if (IS_ANIMATED(graphic))
5213 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5218 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5221 Feld[x][y] = EL_EXIT_OPENING;
5223 PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5226 void CheckExitSP(int x, int y)
5228 if (local_player->gems_still_needed > 0)
5230 int element = Feld[x][y];
5231 int graphic = el2img(element);
5233 if (IS_ANIMATED(graphic))
5234 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5239 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5242 Feld[x][y] = EL_SP_EXIT_OPENING;
5244 PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5247 static void CloseAllOpenTimegates()
5251 for (y=0; y<lev_fieldy; y++)
5253 for (x=0; x<lev_fieldx; x++)
5255 int element = Feld[x][y];
5257 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5259 Feld[x][y] = EL_TIMEGATE_CLOSING;
5261 PlaySoundLevelAction(x, y, ACTION_CLOSING);
5263 PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5270 void EdelsteinFunkeln(int x, int y)
5272 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5275 if (Feld[x][y] == EL_BD_DIAMOND)
5278 if (MovDelay[x][y] == 0) /* next animation frame */
5279 MovDelay[x][y] = 11 * !SimpleRND(500);
5281 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5285 if (setup.direct_draw && MovDelay[x][y])
5286 SetDrawtoField(DRAW_BUFFERED);
5288 DrawLevelElementAnimation(x, y, Feld[x][y]);
5290 if (MovDelay[x][y] != 0)
5292 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5293 10 - MovDelay[x][y]);
5295 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5297 if (setup.direct_draw)
5301 dest_x = FX + SCREENX(x) * TILEX;
5302 dest_y = FY + SCREENY(y) * TILEY;
5304 BlitBitmap(drawto_field, window,
5305 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5306 SetDrawtoField(DRAW_DIRECT);
5312 void MauerWaechst(int x, int y)
5316 if (!MovDelay[x][y]) /* next animation frame */
5317 MovDelay[x][y] = 3 * delay;
5319 if (MovDelay[x][y]) /* wait some time before next frame */
5323 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5325 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5326 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5328 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5331 if (!MovDelay[x][y])
5333 if (MovDir[x][y] == MV_LEFT)
5335 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5336 DrawLevelField(x - 1, y);
5338 else if (MovDir[x][y] == MV_RIGHT)
5340 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5341 DrawLevelField(x + 1, y);
5343 else if (MovDir[x][y] == MV_UP)
5345 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5346 DrawLevelField(x, y - 1);
5350 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5351 DrawLevelField(x, y + 1);
5354 Feld[x][y] = Store[x][y];
5356 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5357 DrawLevelField(x, y);
5362 void MauerAbleger(int ax, int ay)
5364 int element = Feld[ax][ay];
5365 int graphic = el2img(element);
5366 boolean oben_frei = FALSE, unten_frei = FALSE;
5367 boolean links_frei = FALSE, rechts_frei = FALSE;
5368 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5369 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5370 boolean new_wall = FALSE;
5372 if (IS_ANIMATED(graphic))
5373 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5375 if (!MovDelay[ax][ay]) /* start building new wall */
5376 MovDelay[ax][ay] = 6;
5378 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5381 if (MovDelay[ax][ay])
5385 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5387 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5389 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5391 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5394 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5395 element == EL_EXPANDABLE_WALL_ANY)
5399 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5400 Store[ax][ay-1] = element;
5401 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5402 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5403 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5404 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5409 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5410 Store[ax][ay+1] = element;
5411 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5412 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5413 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5414 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5419 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5420 element == EL_EXPANDABLE_WALL_ANY ||
5421 element == EL_EXPANDABLE_WALL)
5425 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5426 Store[ax-1][ay] = element;
5427 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5428 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5429 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5430 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5436 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5437 Store[ax+1][ay] = element;
5438 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5439 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5440 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5441 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5446 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5447 DrawLevelField(ax, ay);
5449 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5451 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5452 unten_massiv = TRUE;
5453 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5454 links_massiv = TRUE;
5455 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5456 rechts_massiv = TRUE;
5458 if (((oben_massiv && unten_massiv) ||
5459 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5460 element == EL_EXPANDABLE_WALL) &&
5461 ((links_massiv && rechts_massiv) ||
5462 element == EL_EXPANDABLE_WALL_VERTICAL))
5463 Feld[ax][ay] = EL_WALL;
5467 PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5469 PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5473 void CheckForDragon(int x, int y)
5476 boolean dragon_found = FALSE;
5477 static int xy[4][2] =
5489 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5491 if (IN_LEV_FIELD(xx, yy) &&
5492 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5494 if (Feld[xx][yy] == EL_DRAGON)
5495 dragon_found = TRUE;
5508 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5510 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5512 Feld[xx][yy] = EL_EMPTY;
5513 DrawLevelField(xx, yy);
5522 static void InitBuggyBase(int x, int y)
5524 int element = Feld[x][y];
5525 int activating_delay = FRAMES_PER_SECOND / 4;
5528 (element == EL_SP_BUGGY_BASE ?
5529 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5530 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5532 element == EL_SP_BUGGY_BASE_ACTIVE ?
5533 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5536 static void WarnBuggyBase(int x, int y)
5539 static int xy[4][2] =
5549 int xx = x + xy[i][0], yy = y + xy[i][1];
5551 if (IS_PLAYER(xx, yy))
5553 PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5560 static void InitTrap(int x, int y)
5562 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5565 static void ActivateTrap(int x, int y)
5567 PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5570 static void ChangeActiveTrap(int x, int y)
5572 int graphic = IMG_TRAP_ACTIVE;
5574 /* if new animation frame was drawn, correct crumbled sand border */
5575 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5576 DrawLevelFieldCrumbledSand(x, y);
5579 static void ChangeElementNowExt(int x, int y, int target_element)
5581 /* check if element under player changes from accessible to unaccessible
5582 (needed for special case of dropping element which then changes) */
5583 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5584 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5591 Feld[x][y] = target_element;
5593 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5595 ResetGfxAnimation(x, y);
5596 ResetRandomAnimationValue(x, y);
5598 InitField(x, y, FALSE);
5599 if (CAN_MOVE(Feld[x][y]))
5602 DrawLevelField(x, y);
5604 if (GFX_CRUMBLED(Feld[x][y]))
5605 DrawLevelFieldCrumbledSandNeighbours(x, y);
5607 TestIfBadThingTouchesHero(x, y);
5608 TestIfPlayerTouchesCustomElement(x, y);
5609 TestIfElementTouchesCustomElement(x, y);
5611 if (ELEM_IS_PLAYER(target_element))
5612 RelocatePlayer(x, y, target_element);
5615 static boolean ChangeElementNow(int x, int y, int element, int page)
5617 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5619 /* always use default change event to prevent running into a loop */
5620 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5621 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5623 /* do not change already changed elements with same change event */
5625 if (Changed[x][y] & ChangeEvent[x][y])
5632 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5634 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5636 if (change->explode)
5643 if (change->use_content)
5645 boolean complete_change = TRUE;
5646 boolean can_change[3][3];
5649 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5651 boolean half_destructible;
5652 int ex = x + xx - 1;
5653 int ey = y + yy - 1;
5656 can_change[xx][yy] = TRUE;
5658 if (ex == x && ey == y) /* do not check changing element itself */
5661 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5663 can_change[xx][yy] = FALSE; /* do not change empty borders */
5668 if (!IN_LEV_FIELD(ex, ey))
5670 can_change[xx][yy] = FALSE;
5671 complete_change = FALSE;
5678 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5679 e = MovingOrBlocked2Element(ex, ey);
5681 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5683 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5684 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5685 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5687 can_change[xx][yy] = FALSE;
5688 complete_change = FALSE;
5692 if (!change->only_complete || complete_change)
5694 boolean something_has_changed = FALSE;
5696 if (change->only_complete && change->use_random_change &&
5697 RND(100) < change->random)
5700 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5702 int ex = x + xx - 1;
5703 int ey = y + yy - 1;
5705 if (can_change[xx][yy] && (!change->use_random_change ||
5706 RND(100) < change->random))
5708 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5709 RemoveMovingField(ex, ey);
5711 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5713 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5715 something_has_changed = TRUE;
5717 /* for symmetry reasons, freeze newly created border elements */
5718 if (ex != x || ey != y)
5719 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5723 if (something_has_changed)
5724 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5729 ChangeElementNowExt(x, y, change->target_element);
5731 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5737 static void ChangeElement(int x, int y, int page)
5739 int element = MovingOrBlocked2Element(x, y);
5740 struct ElementInfo *ei = &element_info[element];
5741 struct ElementChangeInfo *change = &ei->change_page[page];
5745 if (!CAN_CHANGE(element))
5748 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5749 x, y, element, element_info[element].token_name);
5750 printf("ChangeElement(): This should never happen!\n");
5756 if (ChangeDelay[x][y] == 0) /* initialize element change */
5758 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5759 RND(change->delay_random * change->delay_frames)) + 1;
5761 ResetGfxAnimation(x, y);
5762 ResetRandomAnimationValue(x, y);
5764 if (change->pre_change_function)
5765 change->pre_change_function(x, y);
5768 ChangeDelay[x][y]--;
5770 if (ChangeDelay[x][y] != 0) /* continue element change */
5772 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5774 if (IS_ANIMATED(graphic))
5775 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5777 if (change->change_function)
5778 change->change_function(x, y);
5780 else /* finish element change */
5782 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5784 page = ChangePage[x][y];
5785 ChangePage[x][y] = -1;
5788 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5790 ChangeDelay[x][y] = 1; /* try change after next move step */
5791 ChangePage[x][y] = page; /* remember page to use for change */
5796 if (ChangeElementNow(x, y, element, page))
5798 if (change->post_change_function)
5799 change->post_change_function(x, y);
5804 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5805 int trigger_element,
5811 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5814 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5816 int element = EL_CUSTOM_START + i;
5818 boolean change_element = FALSE;
5821 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5824 for (j=0; j < element_info[element].num_change_pages; j++)
5826 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5828 if (change->can_change &&
5830 change->events & CH_EVENT_BIT(trigger_event) &&
5832 change->sides & trigger_side &&
5833 change->trigger_element == trigger_element)
5836 if (!(change->events & CH_EVENT_BIT(trigger_event)))
5837 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5838 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5841 change_element = TRUE;
5848 if (!change_element)
5851 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5854 if (x == lx && y == ly) /* do not change trigger element itself */
5858 if (Feld[x][y] == element)
5860 ChangeDelay[x][y] = 1;
5861 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5862 ChangeElement(x, y, page);
5870 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5873 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5877 static boolean CheckElementSideChange(int x, int y, int element, int side,
5878 int trigger_event, int page)
5880 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5883 if (Feld[x][y] == EL_BLOCKED)
5885 Blocked2Moving(x, y, &x, &y);
5886 element = Feld[x][y];
5890 page = element_info[element].event_page_nr[trigger_event];
5892 if (!(element_info[element].change_page[page].sides & side))
5895 ChangeDelay[x][y] = 1;
5896 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5897 ChangeElement(x, y, page);
5902 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5904 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5907 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5909 static byte stored_player_action[MAX_PLAYERS];
5910 static int num_stored_actions = 0;
5911 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5912 int left = player_action & JOY_LEFT;
5913 int right = player_action & JOY_RIGHT;
5914 int up = player_action & JOY_UP;
5915 int down = player_action & JOY_DOWN;
5916 int button1 = player_action & JOY_BUTTON_1;
5917 int button2 = player_action & JOY_BUTTON_2;
5918 int dx = (left ? -1 : right ? 1 : 0);
5919 int dy = (up ? -1 : down ? 1 : 0);
5921 stored_player_action[player->index_nr] = 0;
5922 num_stored_actions++;
5924 if (!player->active || tape.pausing)
5930 snapped = SnapField(player, dx, dy);
5934 dropped = DropElement(player);
5936 moved = MovePlayer(player, dx, dy);
5939 if (tape.single_step && tape.recording && !tape.pausing)
5941 if (button1 || (dropped && !moved))
5943 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5944 SnapField(player, 0, 0); /* stop snapping */
5948 stored_player_action[player->index_nr] = player_action;
5952 /* no actions for this player (no input at player's configured device) */
5954 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5955 SnapField(player, 0, 0);
5956 CheckGravityMovement(player);
5958 if (player->MovPos == 0)
5959 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5961 if (player->MovPos == 0) /* needed for tape.playing */
5962 player->is_moving = FALSE;
5965 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5967 TapeRecordAction(stored_player_action);
5968 num_stored_actions = 0;
5974 static unsigned long action_delay = 0;
5975 unsigned long action_delay_value;
5976 int magic_wall_x = 0, magic_wall_y = 0;
5977 int i, x, y, element, graphic;
5978 byte *recorded_player_action;
5979 byte summarized_player_action = 0;
5981 if (game_status != GAME_MODE_PLAYING)
5984 action_delay_value =
5985 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5987 if (tape.playing && tape.index_search && !tape.pausing)
5988 action_delay_value = 0;
5990 /* ---------- main game synchronization point ---------- */
5992 WaitUntilDelayReached(&action_delay, action_delay_value);
5994 if (network_playing && !network_player_action_received)
5998 printf("DEBUG: try to get network player actions in time\n");
6002 #if defined(PLATFORM_UNIX)
6003 /* last chance to get network player actions without main loop delay */
6007 if (game_status != GAME_MODE_PLAYING)
6010 if (!network_player_action_received)
6014 printf("DEBUG: failed to get network player actions in time\n");
6024 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6026 for (i=0; i<MAX_PLAYERS; i++)
6028 summarized_player_action |= stored_player[i].action;
6030 if (!network_playing)
6031 stored_player[i].effective_action = stored_player[i].action;
6034 #if defined(PLATFORM_UNIX)
6035 if (network_playing)
6036 SendToServer_MovePlayer(summarized_player_action);
6039 if (!options.network && !setup.team_mode)
6040 local_player->effective_action = summarized_player_action;
6042 for (i=0; i<MAX_PLAYERS; i++)
6044 int actual_player_action = stored_player[i].effective_action;
6046 if (stored_player[i].programmed_action)
6047 actual_player_action = stored_player[i].programmed_action;
6049 if (recorded_player_action)
6050 actual_player_action = recorded_player_action[i];
6052 PlayerActions(&stored_player[i], actual_player_action);
6053 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6056 network_player_action_received = FALSE;
6058 ScrollScreen(NULL, SCROLL_GO_ON);
6064 for (i=0; i<MAX_PLAYERS; i++)
6065 stored_player[i].Frame++;
6069 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6071 for (i=0; i<MAX_PLAYERS; i++)
6073 struct PlayerInfo *player = &stored_player[i];
6077 if (player->active && player->is_pushing && player->is_moving &&
6080 ContinueMoving(x, y);
6082 /* continue moving after pushing (this is actually a bug) */
6083 if (!IS_MOVING(x, y))
6092 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6094 Changed[x][y] = CE_BITMASK_DEFAULT;
6095 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6098 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6100 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6101 printf("GameActions(): This should never happen!\n");
6103 ChangePage[x][y] = -1;
6108 if (WasJustMoving[x][y] > 0)
6109 WasJustMoving[x][y]--;
6110 if (WasJustFalling[x][y] > 0)
6111 WasJustFalling[x][y]--;
6116 /* reset finished pushing action (not done in ContinueMoving() to allow
6117 continous pushing animation for elements with zero push delay) */
6118 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6120 ResetGfxAnimation(x, y);
6121 DrawLevelField(x, y);
6126 if (IS_BLOCKED(x, y))
6130 Blocked2Moving(x, y, &oldx, &oldy);
6131 if (!IS_MOVING(oldx, oldy))
6133 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6134 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6135 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6136 printf("GameActions(): This should never happen!\n");
6142 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6144 element = Feld[x][y];
6146 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6148 graphic = el2img(element);
6154 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6156 element = graphic = 0;
6160 if (graphic_info[graphic].anim_global_sync)
6161 GfxFrame[x][y] = FrameCounter;
6163 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6164 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6165 ResetRandomAnimationValue(x, y);
6167 SetRandomAnimationValue(x, y);
6170 PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6173 if (IS_INACTIVE(element))
6175 if (IS_ANIMATED(graphic))
6176 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6182 /* this may take place after moving, so 'element' may have changed */
6184 if (IS_CHANGING(x, y))
6186 if (IS_CHANGING(x, y) &&
6187 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6191 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6192 element_info[element].event_page_nr[CE_DELAY]);
6194 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6197 element = Feld[x][y];
6198 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6202 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6207 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6209 if (element == EL_MOLE)
6210 printf("::: %d, %d, %d [%d]\n",
6211 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6215 if (element == EL_YAMYAM)
6216 printf("::: %d, %d, %d\n",
6217 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6221 if (IS_ANIMATED(graphic) &&
6225 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6228 if (element == EL_MOLE)
6229 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6233 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6234 EdelsteinFunkeln(x, y);
6236 else if ((element == EL_ACID ||
6237 element == EL_EXIT_OPEN ||
6238 element == EL_SP_EXIT_OPEN ||
6239 element == EL_SP_TERMINAL ||
6240 element == EL_SP_TERMINAL_ACTIVE ||
6241 element == EL_EXTRA_TIME ||
6242 element == EL_SHIELD_NORMAL ||
6243 element == EL_SHIELD_DEADLY) &&
6244 IS_ANIMATED(graphic))
6245 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6246 else if (IS_MOVING(x, y))
6247 ContinueMoving(x, y);
6248 else if (IS_ACTIVE_BOMB(element))
6249 CheckDynamite(x, y);
6251 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6252 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6254 else if (element == EL_AMOEBA_GROWING)
6255 AmoebeWaechst(x, y);
6256 else if (element == EL_AMOEBA_SHRINKING)
6257 AmoebaDisappearing(x, y);
6259 #if !USE_NEW_AMOEBA_CODE
6260 else if (IS_AMOEBALIVE(element))
6261 AmoebeAbleger(x, y);
6264 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6266 else if (element == EL_EXIT_CLOSED)
6268 else if (element == EL_SP_EXIT_CLOSED)
6270 else if (element == EL_EXPANDABLE_WALL_GROWING)
6272 else if (element == EL_EXPANDABLE_WALL ||
6273 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6274 element == EL_EXPANDABLE_WALL_VERTICAL ||
6275 element == EL_EXPANDABLE_WALL_ANY)
6277 else if (element == EL_FLAMES)
6278 CheckForDragon(x, y);
6280 else if (IS_AUTO_CHANGING(element))
6281 ChangeElement(x, y);
6283 else if (element == EL_EXPLOSION)
6284 ; /* drawing of correct explosion animation is handled separately */
6285 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6286 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6289 /* this may take place after moving, so 'element' may have changed */
6290 if (IS_AUTO_CHANGING(Feld[x][y]))
6291 ChangeElement(x, y);
6294 if (IS_BELT_ACTIVE(element))
6295 PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6297 if (game.magic_wall_active)
6299 int jx = local_player->jx, jy = local_player->jy;
6301 /* play the element sound at the position nearest to the player */
6302 if ((element == EL_MAGIC_WALL_FULL ||
6303 element == EL_MAGIC_WALL_ACTIVE ||
6304 element == EL_MAGIC_WALL_EMPTYING ||
6305 element == EL_BD_MAGIC_WALL_FULL ||
6306 element == EL_BD_MAGIC_WALL_ACTIVE ||
6307 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6308 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6316 #if USE_NEW_AMOEBA_CODE
6317 /* new experimental amoeba growth stuff */
6319 if (!(FrameCounter % 8))
6322 static unsigned long random = 1684108901;
6324 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6327 x = (random >> 10) % lev_fieldx;
6328 y = (random >> 20) % lev_fieldy;
6330 x = RND(lev_fieldx);
6331 y = RND(lev_fieldy);
6333 element = Feld[x][y];
6335 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6336 if (!IS_PLAYER(x,y) &&
6337 (element == EL_EMPTY ||
6338 element == EL_SAND ||
6339 element == EL_QUICKSAND_EMPTY ||
6340 element == EL_ACID_SPLASH_LEFT ||
6341 element == EL_ACID_SPLASH_RIGHT))
6343 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6344 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6345 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6346 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6347 Feld[x][y] = EL_AMOEBA_DROP;
6350 random = random * 129 + 1;
6356 if (game.explosions_delayed)
6359 game.explosions_delayed = FALSE;
6361 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6363 element = Feld[x][y];
6365 if (ExplodeField[x][y])
6366 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6367 else if (element == EL_EXPLOSION)
6368 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6370 ExplodeField[x][y] = EX_NO_EXPLOSION;
6373 game.explosions_delayed = TRUE;
6376 if (game.magic_wall_active)
6378 if (!(game.magic_wall_time_left % 4))
6380 int element = Feld[magic_wall_x][magic_wall_y];
6382 if (element == EL_BD_MAGIC_WALL_FULL ||
6383 element == EL_BD_MAGIC_WALL_ACTIVE ||
6384 element == EL_BD_MAGIC_WALL_EMPTYING)
6385 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6387 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6390 if (game.magic_wall_time_left > 0)
6392 game.magic_wall_time_left--;
6393 if (!game.magic_wall_time_left)
6395 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6397 element = Feld[x][y];
6399 if (element == EL_MAGIC_WALL_ACTIVE ||
6400 element == EL_MAGIC_WALL_FULL)
6402 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6403 DrawLevelField(x, y);
6405 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6406 element == EL_BD_MAGIC_WALL_FULL)
6408 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6409 DrawLevelField(x, y);
6413 game.magic_wall_active = FALSE;
6418 if (game.light_time_left > 0)
6420 game.light_time_left--;
6422 if (game.light_time_left == 0)
6423 RedrawAllLightSwitchesAndInvisibleElements();
6426 if (game.timegate_time_left > 0)
6428 game.timegate_time_left--;
6430 if (game.timegate_time_left == 0)
6431 CloseAllOpenTimegates();
6434 for (i=0; i<MAX_PLAYERS; i++)
6436 struct PlayerInfo *player = &stored_player[i];
6438 if (SHIELD_ON(player))
6440 if (player->shield_deadly_time_left)
6441 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6442 else if (player->shield_normal_time_left)
6443 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6447 if (TimeFrames >= FRAMES_PER_SECOND)
6452 for (i=0; i<MAX_PLAYERS; i++)
6454 struct PlayerInfo *player = &stored_player[i];
6456 if (SHIELD_ON(player))
6458 player->shield_normal_time_left--;
6460 if (player->shield_deadly_time_left > 0)
6461 player->shield_deadly_time_left--;
6465 if (tape.recording || tape.playing)
6466 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6472 if (TimeLeft <= 10 && setup.time_limit)
6473 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6475 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6477 if (!TimeLeft && setup.time_limit)
6478 for (i=0; i<MAX_PLAYERS; i++)
6479 KillHero(&stored_player[i]);
6481 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6482 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6487 if (options.debug) /* calculate frames per second */
6489 static unsigned long fps_counter = 0;
6490 static int fps_frames = 0;
6491 unsigned long fps_delay_ms = Counter() - fps_counter;
6495 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6497 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6500 fps_counter = Counter();
6503 redraw_mask |= REDRAW_FPS;
6507 if (stored_player[0].jx != stored_player[0].last_jx ||
6508 stored_player[0].jy != stored_player[0].last_jy)
6509 printf("::: %d, %d, %d, %d, %d\n",
6510 stored_player[0].MovDir,
6511 stored_player[0].MovPos,
6512 stored_player[0].GfxPos,
6513 stored_player[0].Frame,
6514 stored_player[0].StepFrame);
6521 for (i=0; i<MAX_PLAYERS; i++)
6524 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6526 stored_player[i].Frame += move_frames;
6528 if (stored_player[i].MovPos != 0)
6529 stored_player[i].StepFrame += move_frames;
6534 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6536 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6538 local_player->show_envelope = 0;
6543 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6545 int min_x = x, min_y = y, max_x = x, max_y = y;
6548 for (i=0; i<MAX_PLAYERS; i++)
6550 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6552 if (!stored_player[i].active || &stored_player[i] == player)
6555 min_x = MIN(min_x, jx);
6556 min_y = MIN(min_y, jy);
6557 max_x = MAX(max_x, jx);
6558 max_y = MAX(max_y, jy);
6561 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6564 static boolean AllPlayersInVisibleScreen()
6568 for (i=0; i<MAX_PLAYERS; i++)
6570 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6572 if (!stored_player[i].active)
6575 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6582 void ScrollLevel(int dx, int dy)
6584 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6587 BlitBitmap(drawto_field, drawto_field,
6588 FX + TILEX * (dx == -1) - softscroll_offset,
6589 FY + TILEY * (dy == -1) - softscroll_offset,
6590 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6591 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6592 FX + TILEX * (dx == 1) - softscroll_offset,
6593 FY + TILEY * (dy == 1) - softscroll_offset);
6597 x = (dx == 1 ? BX1 : BX2);
6598 for (y=BY1; y <= BY2; y++)
6599 DrawScreenField(x, y);
6604 y = (dy == 1 ? BY1 : BY2);
6605 for (x=BX1; x <= BX2; x++)
6606 DrawScreenField(x, y);
6609 redraw_mask |= REDRAW_FIELD;
6612 static void CheckGravityMovement(struct PlayerInfo *player)
6614 if (game.gravity && !player->programmed_action)
6616 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6617 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6619 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6620 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6621 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6622 int jx = player->jx, jy = player->jy;
6623 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6624 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6625 int new_jx = jx + dx, new_jy = jy + dy;
6626 boolean field_under_player_is_free =
6627 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6628 boolean player_is_moving_to_valid_field =
6629 (IN_LEV_FIELD(new_jx, new_jy) &&
6630 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6631 Feld[new_jx][new_jy] == EL_SAND));
6632 /* !!! extend EL_SAND to anything diggable !!! */
6634 if (field_under_player_is_free &&
6635 !player_is_moving_to_valid_field &&
6636 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6637 player->programmed_action = MV_DOWN;
6643 -----------------------------------------------------------------------------
6644 dx, dy: direction (non-diagonal) to try to move the player to
6645 real_dx, real_dy: direction as read from input device (can be diagonal)
6648 boolean MovePlayerOneStep(struct PlayerInfo *player,
6649 int dx, int dy, int real_dx, int real_dy)
6652 static int change_sides[4][2] =
6654 /* enter side leave side */
6655 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6656 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6657 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6658 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6660 int move_direction = (dx == -1 ? MV_LEFT :
6661 dx == +1 ? MV_RIGHT :
6663 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6664 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6665 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6667 int jx = player->jx, jy = player->jy;
6668 int new_jx = jx + dx, new_jy = jy + dy;
6672 if (!player->active || (!dx && !dy))
6673 return MF_NO_ACTION;
6675 player->MovDir = (dx < 0 ? MV_LEFT :
6678 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6680 if (!IN_LEV_FIELD(new_jx, new_jy))
6681 return MF_NO_ACTION;
6683 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6684 return MF_NO_ACTION;
6687 element = MovingOrBlocked2Element(new_jx, new_jy);
6689 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6692 if (DONT_RUN_INTO(element))
6694 if (element == EL_ACID && dx == 0 && dy == 1)
6697 Feld[jx][jy] = EL_PLAYER_1;
6698 InitMovingField(jx, jy, MV_DOWN);
6699 Store[jx][jy] = EL_ACID;
6700 ContinueMoving(jx, jy);
6704 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6709 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6710 if (can_move != MF_MOVING)
6713 /* check if DigField() has caused relocation of the player */
6714 if (player->jx != jx || player->jy != jy)
6715 return MF_NO_ACTION;
6717 StorePlayer[jx][jy] = 0;
6718 player->last_jx = jx;
6719 player->last_jy = jy;
6720 player->jx = new_jx;
6721 player->jy = new_jy;
6722 StorePlayer[new_jx][new_jy] = player->element_nr;
6725 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6727 ScrollPlayer(player, SCROLL_INIT);
6730 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6732 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6733 CE_OTHER_GETS_LEFT);
6734 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6735 CE_LEFT_BY_PLAYER, -1);
6738 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6740 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6741 enter_side, CE_OTHER_GETS_ENTERED);
6742 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6743 CE_ENTERED_BY_PLAYER, -1);
6750 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6752 int jx = player->jx, jy = player->jy;
6753 int old_jx = jx, old_jy = jy;
6754 int moved = MF_NO_ACTION;
6756 if (!player->active || (!dx && !dy))
6760 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6764 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6765 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6769 /* remove the last programmed player action */
6770 player->programmed_action = 0;
6774 /* should only happen if pre-1.2 tape recordings are played */
6775 /* this is only for backward compatibility */
6777 int original_move_delay_value = player->move_delay_value;
6780 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6784 /* scroll remaining steps with finest movement resolution */
6785 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6787 while (player->MovPos)
6789 ScrollPlayer(player, SCROLL_GO_ON);
6790 ScrollScreen(NULL, SCROLL_GO_ON);
6796 player->move_delay_value = original_move_delay_value;
6799 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6801 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6802 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6806 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6807 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6813 if (moved & MF_MOVING && !ScreenMovPos &&
6814 (player == local_player || !options.network))
6816 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6817 int offset = (setup.scroll_delay ? 3 : 0);
6819 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6821 /* actual player has left the screen -- scroll in that direction */
6822 if (jx != old_jx) /* player has moved horizontally */
6823 scroll_x += (jx - old_jx);
6824 else /* player has moved vertically */
6825 scroll_y += (jy - old_jy);
6829 if (jx != old_jx) /* player has moved horizontally */
6831 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6832 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6833 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6835 /* don't scroll over playfield boundaries */
6836 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6837 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6839 /* don't scroll more than one field at a time */
6840 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6842 /* don't scroll against the player's moving direction */
6843 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6844 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6845 scroll_x = old_scroll_x;
6847 else /* player has moved vertically */
6849 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6850 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6851 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6853 /* don't scroll over playfield boundaries */
6854 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6855 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6857 /* don't scroll more than one field at a time */
6858 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6860 /* don't scroll against the player's moving direction */
6861 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6862 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6863 scroll_y = old_scroll_y;
6867 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6869 if (!options.network && !AllPlayersInVisibleScreen())
6871 scroll_x = old_scroll_x;
6872 scroll_y = old_scroll_y;
6876 ScrollScreen(player, SCROLL_INIT);
6877 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6884 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6886 if (!(moved & MF_MOVING) && !player->is_pushing)
6891 player->StepFrame = 0;
6893 if (moved & MF_MOVING)
6895 if (old_jx != jx && old_jy == jy)
6896 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6897 else if (old_jx == jx && old_jy != jy)
6898 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6900 DrawLevelField(jx, jy); /* for "crumbled sand" */
6902 player->last_move_dir = player->MovDir;
6903 player->is_moving = TRUE;
6905 player->is_snapping = FALSE;
6909 player->is_switching = FALSE;
6915 static int change_sides[4][2] =
6917 /* enter side leave side */
6918 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6919 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6920 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6921 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6923 int move_direction = player->MovDir;
6924 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6925 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6928 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
6930 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6931 leave_side, CE_OTHER_GETS_LEFT);
6932 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6933 leave_side, CE_LEFT_BY_PLAYER, -1);
6936 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6938 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
6939 enter_side, CE_OTHER_GETS_ENTERED);
6940 CheckElementSideChange(jx, jy, Feld[jx][jy],
6941 enter_side, CE_ENTERED_BY_PLAYER, -1);
6952 CheckGravityMovement(player);
6955 player->last_move_dir = MV_NO_MOVING;
6957 player->is_moving = FALSE;
6960 if (game.engine_version < VERSION_IDENT(3,0,7,0))
6962 TestIfHeroTouchesBadThing(jx, jy);
6963 TestIfPlayerTouchesCustomElement(jx, jy);
6966 if (!player->active)
6972 void ScrollPlayer(struct PlayerInfo *player, int mode)
6974 int jx = player->jx, jy = player->jy;
6975 int last_jx = player->last_jx, last_jy = player->last_jy;
6976 int move_stepsize = TILEX / player->move_delay_value;
6978 if (!player->active || !player->MovPos)
6981 if (mode == SCROLL_INIT)
6983 player->actual_frame_counter = FrameCounter;
6984 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6986 if (Feld[last_jx][last_jy] == EL_EMPTY)
6987 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6994 else if (!FrameReached(&player->actual_frame_counter, 1))
6997 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6998 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7000 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7001 Feld[last_jx][last_jy] = EL_EMPTY;
7003 /* before DrawPlayer() to draw correct player graphic for this case */
7004 if (player->MovPos == 0)
7005 CheckGravityMovement(player);
7008 DrawPlayer(player); /* needed here only to cleanup last field */
7011 if (player->MovPos == 0) /* player reached destination field */
7013 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7015 /* continue with normal speed after quickly moving through gate */
7016 HALVE_PLAYER_SPEED(player);
7018 /* be able to make the next move without delay */
7019 player->move_delay = 0;
7022 player->last_jx = jx;
7023 player->last_jy = jy;
7025 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7026 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7027 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7029 DrawPlayer(player); /* needed here only to cleanup last field */
7032 if (local_player->friends_still_needed == 0 ||
7033 IS_SP_ELEMENT(Feld[jx][jy]))
7034 player->LevelSolved = player->GameOver = TRUE;
7037 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7039 TestIfHeroTouchesBadThing(jx, jy);
7040 TestIfPlayerTouchesCustomElement(jx, jy);
7042 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7045 if (!player->active)
7049 if (tape.single_step && tape.recording && !tape.pausing &&
7050 !player->programmed_action)
7051 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7055 void ScrollScreen(struct PlayerInfo *player, int mode)
7057 static unsigned long screen_frame_counter = 0;
7059 if (mode == SCROLL_INIT)
7061 /* set scrolling step size according to actual player's moving speed */
7062 ScrollStepSize = TILEX / player->move_delay_value;
7064 screen_frame_counter = FrameCounter;
7065 ScreenMovDir = player->MovDir;
7066 ScreenMovPos = player->MovPos;
7067 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7070 else if (!FrameReached(&screen_frame_counter, 1))
7075 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7076 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7077 redraw_mask |= REDRAW_FIELD;
7080 ScreenMovDir = MV_NO_MOVING;
7083 void TestIfPlayerTouchesCustomElement(int x, int y)
7085 static int xy[4][2] =
7092 static int change_sides[4][2] =
7094 /* center side border side */
7095 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7096 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7097 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7098 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7100 static int touch_dir[4] =
7107 int center_element = Feld[x][y]; /* should always be non-moving! */
7112 int xx = x + xy[i][0];
7113 int yy = y + xy[i][1];
7114 int center_side = change_sides[i][0];
7115 int border_side = change_sides[i][1];
7118 if (!IN_LEV_FIELD(xx, yy))
7121 if (IS_PLAYER(x, y))
7123 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7124 border_element = Feld[xx][yy]; /* may be moving! */
7125 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7126 border_element = Feld[xx][yy];
7127 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7128 border_element = MovingOrBlocked2Element(xx, yy);
7130 continue; /* center and border element do not touch */
7132 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7133 CE_OTHER_GETS_TOUCHED);
7134 CheckElementSideChange(xx, yy, border_element, border_side,
7135 CE_TOUCHED_BY_PLAYER, -1);
7137 else if (IS_PLAYER(xx, yy))
7139 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7141 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7143 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7144 continue; /* center and border element do not touch */
7147 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7148 CE_OTHER_GETS_TOUCHED);
7149 CheckElementSideChange(x, y, center_element, center_side,
7150 CE_TOUCHED_BY_PLAYER, -1);
7157 void TestIfElementTouchesCustomElement(int x, int y)
7159 static int xy[4][2] =
7166 static int change_sides[4][2] =
7168 /* center side border side */
7169 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7170 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7171 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7172 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7174 static int touch_dir[4] =
7181 boolean change_center_element = FALSE;
7182 int center_element_change_page = 0;
7183 int center_element = Feld[x][y]; /* should always be non-moving! */
7188 int xx = x + xy[i][0];
7189 int yy = y + xy[i][1];
7190 int center_side = change_sides[i][0];
7191 int border_side = change_sides[i][1];
7194 if (!IN_LEV_FIELD(xx, yy))
7197 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7198 border_element = Feld[xx][yy]; /* may be moving! */
7199 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7200 border_element = Feld[xx][yy];
7201 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7202 border_element = MovingOrBlocked2Element(xx, yy);
7204 continue; /* center and border element do not touch */
7206 /* check for change of center element (but change it only once) */
7207 if (IS_CUSTOM_ELEMENT(center_element) &&
7208 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7209 !change_center_element)
7211 for (j=0; j < element_info[center_element].num_change_pages; j++)
7213 struct ElementChangeInfo *change =
7214 &element_info[center_element].change_page[j];
7216 if (change->can_change &&
7217 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7218 change->sides & border_side &&
7219 change->trigger_element == border_element)
7221 change_center_element = TRUE;
7222 center_element_change_page = j;
7229 /* check for change of border element */
7230 if (IS_CUSTOM_ELEMENT(border_element) &&
7231 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7233 for (j=0; j < element_info[border_element].num_change_pages; j++)
7235 struct ElementChangeInfo *change =
7236 &element_info[border_element].change_page[j];
7238 if (change->can_change &&
7239 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7240 change->sides & center_side &&
7241 change->trigger_element == center_element)
7243 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7244 CE_OTHER_IS_TOUCHING, j);
7251 if (change_center_element)
7252 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7253 CE_OTHER_IS_TOUCHING, center_element_change_page);
7256 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7258 int i, kill_x = -1, kill_y = -1;
7259 static int test_xy[4][2] =
7266 static int test_dir[4] =
7276 int test_x, test_y, test_move_dir, test_element;
7278 test_x = good_x + test_xy[i][0];
7279 test_y = good_y + test_xy[i][1];
7280 if (!IN_LEV_FIELD(test_x, test_y))
7284 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7287 test_element = Feld[test_x][test_y];
7289 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7292 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7293 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7295 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7296 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7304 if (kill_x != -1 || kill_y != -1)
7306 if (IS_PLAYER(good_x, good_y))
7308 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7310 if (player->shield_deadly_time_left > 0)
7311 Bang(kill_x, kill_y);
7312 else if (!PLAYER_PROTECTED(good_x, good_y))
7316 Bang(good_x, good_y);
7320 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7322 int i, kill_x = -1, kill_y = -1;
7323 int bad_element = Feld[bad_x][bad_y];
7324 static int test_xy[4][2] =
7331 static int touch_dir[4] =
7338 static int test_dir[4] =
7346 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7351 int test_x, test_y, test_move_dir, test_element;
7353 test_x = bad_x + test_xy[i][0];
7354 test_y = bad_y + test_xy[i][1];
7355 if (!IN_LEV_FIELD(test_x, test_y))
7359 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7361 test_element = Feld[test_x][test_y];
7363 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7364 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7366 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7367 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7369 /* good thing is player or penguin that does not move away */
7370 if (IS_PLAYER(test_x, test_y))
7372 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7374 if (bad_element == EL_ROBOT && player->is_moving)
7375 continue; /* robot does not kill player if he is moving */
7377 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7379 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7380 continue; /* center and border element do not touch */
7387 else if (test_element == EL_PENGUIN)
7396 if (kill_x != -1 || kill_y != -1)
7398 if (IS_PLAYER(kill_x, kill_y))
7400 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7402 if (player->shield_deadly_time_left > 0)
7404 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7408 Bang(kill_x, kill_y);
7412 void TestIfHeroTouchesBadThing(int x, int y)
7414 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7417 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7419 TestIfGoodThingHitsBadThing(x, y, move_dir);
7422 void TestIfBadThingTouchesHero(int x, int y)
7424 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7427 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7429 TestIfBadThingHitsGoodThing(x, y, move_dir);
7432 void TestIfFriendTouchesBadThing(int x, int y)
7434 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7437 void TestIfBadThingTouchesFriend(int x, int y)
7439 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7442 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7444 int i, kill_x = bad_x, kill_y = bad_y;
7445 static int xy[4][2] =
7457 x = bad_x + xy[i][0];
7458 y = bad_y + xy[i][1];
7459 if (!IN_LEV_FIELD(x, y))
7462 element = Feld[x][y];
7463 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7464 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7472 if (kill_x != bad_x || kill_y != bad_y)
7476 void KillHero(struct PlayerInfo *player)
7478 int jx = player->jx, jy = player->jy;
7480 if (!player->active)
7483 /* remove accessible field at the player's position */
7484 Feld[jx][jy] = EL_EMPTY;
7486 /* deactivate shield (else Bang()/Explode() would not work right) */
7487 player->shield_normal_time_left = 0;
7488 player->shield_deadly_time_left = 0;
7494 static void KillHeroUnlessProtected(int x, int y)
7496 if (!PLAYER_PROTECTED(x, y))
7497 KillHero(PLAYERINFO(x, y));
7500 void BuryHero(struct PlayerInfo *player)
7502 int jx = player->jx, jy = player->jy;
7504 if (!player->active)
7508 PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7510 PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7512 PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7514 player->GameOver = TRUE;
7518 void RemoveHero(struct PlayerInfo *player)
7520 int jx = player->jx, jy = player->jy;
7521 int i, found = FALSE;
7523 player->present = FALSE;
7524 player->active = FALSE;
7526 if (!ExplodeField[jx][jy])
7527 StorePlayer[jx][jy] = 0;
7529 for (i=0; i<MAX_PLAYERS; i++)
7530 if (stored_player[i].active)
7534 AllPlayersGone = TRUE;
7541 =============================================================================
7542 checkDiagonalPushing()
7543 -----------------------------------------------------------------------------
7544 check if diagonal input device direction results in pushing of object
7545 (by checking if the alternative direction is walkable, diggable, ...)
7546 =============================================================================
7549 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7550 int x, int y, int real_dx, int real_dy)
7552 int jx, jy, dx, dy, xx, yy;
7554 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7557 /* diagonal direction: check alternative direction */
7562 xx = jx + (dx == 0 ? real_dx : 0);
7563 yy = jy + (dy == 0 ? real_dy : 0);
7565 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7569 =============================================================================
7571 -----------------------------------------------------------------------------
7572 x, y: field next to player (non-diagonal) to try to dig to
7573 real_dx, real_dy: direction as read from input device (can be diagonal)
7574 =============================================================================
7577 int DigField(struct PlayerInfo *player,
7578 int x, int y, int real_dx, int real_dy, int mode)
7580 static int change_sides[4] =
7582 CH_SIDE_RIGHT, /* moving left */
7583 CH_SIDE_LEFT, /* moving right */
7584 CH_SIDE_BOTTOM, /* moving up */
7585 CH_SIDE_TOP, /* moving down */
7587 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7588 int jx = player->jx, jy = player->jy;
7589 int dx = x - jx, dy = y - jy;
7590 int nextx = x + dx, nexty = y + dy;
7591 int move_direction = (dx == -1 ? MV_LEFT :
7592 dx == +1 ? MV_RIGHT :
7594 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7595 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7598 if (player->MovPos == 0)
7600 player->is_digging = FALSE;
7601 player->is_collecting = FALSE;
7604 if (player->MovPos == 0) /* last pushing move finished */
7605 player->is_pushing = FALSE;
7607 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7609 player->is_switching = FALSE;
7610 player->push_delay = 0;
7612 return MF_NO_ACTION;
7615 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7616 return MF_NO_ACTION;
7619 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7621 if (IS_TUBE(Feld[jx][jy]) ||
7622 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7626 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7627 int tube_leave_directions[][2] =
7629 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7630 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7631 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7632 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7633 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7634 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7635 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7636 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7637 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7638 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7639 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7640 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7643 while (tube_leave_directions[i][0] != tube_element)
7646 if (tube_leave_directions[i][0] == -1) /* should not happen */
7650 if (!(tube_leave_directions[i][1] & move_direction))
7651 return MF_NO_ACTION; /* tube has no opening in this direction */
7654 element = Feld[x][y];
7656 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7657 game.engine_version >= VERSION_IDENT(2,2,0,0))
7658 return MF_NO_ACTION;
7662 case EL_SP_PORT_LEFT:
7663 case EL_SP_PORT_RIGHT:
7665 case EL_SP_PORT_DOWN:
7666 case EL_SP_PORT_HORIZONTAL:
7667 case EL_SP_PORT_VERTICAL:
7668 case EL_SP_PORT_ANY:
7669 case EL_SP_GRAVITY_PORT_LEFT:
7670 case EL_SP_GRAVITY_PORT_RIGHT:
7671 case EL_SP_GRAVITY_PORT_UP:
7672 case EL_SP_GRAVITY_PORT_DOWN:
7674 element != EL_SP_PORT_LEFT &&
7675 element != EL_SP_GRAVITY_PORT_LEFT &&
7676 element != EL_SP_PORT_HORIZONTAL &&
7677 element != EL_SP_PORT_ANY) ||
7679 element != EL_SP_PORT_RIGHT &&
7680 element != EL_SP_GRAVITY_PORT_RIGHT &&
7681 element != EL_SP_PORT_HORIZONTAL &&
7682 element != EL_SP_PORT_ANY) ||
7684 element != EL_SP_PORT_UP &&
7685 element != EL_SP_GRAVITY_PORT_UP &&
7686 element != EL_SP_PORT_VERTICAL &&
7687 element != EL_SP_PORT_ANY) ||
7689 element != EL_SP_PORT_DOWN &&
7690 element != EL_SP_GRAVITY_PORT_DOWN &&
7691 element != EL_SP_PORT_VERTICAL &&
7692 element != EL_SP_PORT_ANY) ||
7693 !IN_LEV_FIELD(nextx, nexty) ||
7694 !IS_FREE(nextx, nexty))
7695 return MF_NO_ACTION;
7697 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7698 element == EL_SP_GRAVITY_PORT_RIGHT ||
7699 element == EL_SP_GRAVITY_PORT_UP ||
7700 element == EL_SP_GRAVITY_PORT_DOWN)
7701 game.gravity = !game.gravity;
7703 /* automatically move to the next field with double speed */
7704 player->programmed_action = move_direction;
7705 DOUBLE_PLAYER_SPEED(player);
7707 PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7711 case EL_TUBE_VERTICAL:
7712 case EL_TUBE_HORIZONTAL:
7713 case EL_TUBE_VERTICAL_LEFT:
7714 case EL_TUBE_VERTICAL_RIGHT:
7715 case EL_TUBE_HORIZONTAL_UP:
7716 case EL_TUBE_HORIZONTAL_DOWN:
7717 case EL_TUBE_LEFT_UP:
7718 case EL_TUBE_LEFT_DOWN:
7719 case EL_TUBE_RIGHT_UP:
7720 case EL_TUBE_RIGHT_DOWN:
7723 int tube_enter_directions[][2] =
7725 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7726 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7727 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7728 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7729 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7730 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7731 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7732 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7733 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7734 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7735 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7736 { -1, MV_NO_MOVING }
7739 while (tube_enter_directions[i][0] != element)
7742 if (tube_enter_directions[i][0] == -1) /* should not happen */
7746 if (!(tube_enter_directions[i][1] & move_direction))
7747 return MF_NO_ACTION; /* tube has no opening in this direction */
7749 PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7755 if (IS_WALKABLE(element))
7757 int sound_action = ACTION_WALKING;
7759 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7761 if (!player->key[element - EL_GATE_1])
7762 return MF_NO_ACTION;
7764 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7766 if (!player->key[element - EL_GATE_1_GRAY])
7767 return MF_NO_ACTION;
7769 else if (element == EL_EXIT_OPEN ||
7770 element == EL_SP_EXIT_OPEN ||
7771 element == EL_SP_EXIT_OPENING)
7773 sound_action = ACTION_PASSING; /* player is passing exit */
7775 else if (element == EL_EMPTY)
7777 sound_action = ACTION_MOVING; /* nothing to walk on */
7780 /* play sound from background or player, whatever is available */
7781 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7782 PlaySoundLevelElementAction(x, y, element, sound_action);
7784 PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7788 else if (IS_PASSABLE(element))
7790 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7791 return MF_NO_ACTION;
7794 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7795 return MF_NO_ACTION;
7798 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7800 if (!player->key[element - EL_EM_GATE_1])
7801 return MF_NO_ACTION;
7803 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7805 if (!player->key[element - EL_EM_GATE_1_GRAY])
7806 return MF_NO_ACTION;
7809 /* automatically move to the next field with double speed */
7810 player->programmed_action = move_direction;
7811 DOUBLE_PLAYER_SPEED(player);
7813 PlaySoundLevelAction(x, y, ACTION_PASSING);
7817 else if (IS_DIGGABLE(element))
7821 if (mode != DF_SNAP)
7824 GfxElement[x][y] = GFX_ELEMENT(element);
7827 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7829 player->is_digging = TRUE;
7832 PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7834 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7837 if (mode == DF_SNAP)
7838 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7843 else if (IS_COLLECTIBLE(element))
7847 if (mode != DF_SNAP)
7849 GfxElement[x][y] = element;
7850 player->is_collecting = TRUE;
7853 if (element == EL_SPEED_PILL)
7854 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7855 else if (element == EL_EXTRA_TIME && level.time > 0)
7858 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7860 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7862 player->shield_normal_time_left += 10;
7863 if (element == EL_SHIELD_DEADLY)
7864 player->shield_deadly_time_left += 10;
7866 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7868 if (player->inventory_size < MAX_INVENTORY_SIZE)
7869 player->inventory_element[player->inventory_size++] = element;
7871 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7872 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7874 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7876 player->dynabomb_count++;
7877 player->dynabombs_left++;
7879 else if (element == EL_DYNABOMB_INCREASE_SIZE)
7881 player->dynabomb_size++;
7883 else if (element == EL_DYNABOMB_INCREASE_POWER)
7885 player->dynabomb_xl = TRUE;
7887 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7888 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7890 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7891 element - EL_KEY_1 : element - EL_EM_KEY_1);
7893 player->key[key_nr] = TRUE;
7895 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7896 el2edimg(EL_KEY_1 + key_nr));
7897 redraw_mask |= REDRAW_DOOR_1;
7899 else if (IS_ENVELOPE(element))
7902 player->show_envelope = element;
7904 ShowEnvelope(element - EL_ENVELOPE_1);
7907 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7911 for (i=0; i < element_info[element].collect_count; i++)
7912 if (player->inventory_size < MAX_INVENTORY_SIZE)
7913 player->inventory_element[player->inventory_size++] = element;
7915 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7916 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7918 else if (element_info[element].collect_count > 0)
7920 local_player->gems_still_needed -=
7921 element_info[element].collect_count;
7922 if (local_player->gems_still_needed < 0)
7923 local_player->gems_still_needed = 0;
7925 DrawText(DX_EMERALDS, DY_EMERALDS,
7926 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7929 RaiseScoreElement(element);
7930 PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7932 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7935 if (mode == DF_SNAP)
7936 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7941 else if (IS_PUSHABLE(element))
7943 if (mode == DF_SNAP && element != EL_BD_ROCK)
7944 return MF_NO_ACTION;
7946 if (CAN_FALL(element) && dy)
7947 return MF_NO_ACTION;
7949 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7950 !(element == EL_SPRING && use_spring_bug))
7951 return MF_NO_ACTION;
7954 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
7955 ((move_direction & MV_VERTICAL &&
7956 ((element_info[element].move_pattern & MV_LEFT &&
7957 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
7958 (element_info[element].move_pattern & MV_RIGHT &&
7959 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
7960 (move_direction & MV_HORIZONTAL &&
7961 ((element_info[element].move_pattern & MV_UP &&
7962 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
7963 (element_info[element].move_pattern & MV_DOWN &&
7964 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
7965 return MF_NO_ACTION;
7969 /* do not push elements already moving away faster than player */
7970 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
7971 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
7972 return MF_NO_ACTION;
7974 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7975 return MF_NO_ACTION;
7979 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
7981 if (player->push_delay_value == -1)
7982 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7984 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
7986 if (!player->is_pushing)
7987 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7991 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
7992 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
7993 !player_is_pushing))
7994 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7997 if (!player->is_pushing &&
7998 game.engine_version >= VERSION_IDENT(2,2,0,7))
7999 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8003 printf("::: push delay: %ld [%d, %d] [%d]\n",
8004 player->push_delay_value, FrameCounter, game.engine_version,
8005 player->is_pushing);
8008 player->is_pushing = TRUE;
8010 if (!(IN_LEV_FIELD(nextx, nexty) &&
8011 (IS_FREE(nextx, nexty) ||
8012 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8013 IS_SB_ELEMENT(element)))))
8014 return MF_NO_ACTION;
8016 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8017 return MF_NO_ACTION;
8019 if (player->push_delay == 0) /* new pushing; restart delay */
8020 player->push_delay = FrameCounter;
8022 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8023 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8024 element != EL_SPRING && element != EL_BALLOON)
8026 /* make sure that there is no move delay before next try to push */
8027 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8028 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8030 return MF_NO_ACTION;
8034 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8037 if (IS_SB_ELEMENT(element))
8039 if (element == EL_SOKOBAN_FIELD_FULL)
8041 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8042 local_player->sokobanfields_still_needed++;
8045 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8047 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8048 local_player->sokobanfields_still_needed--;
8051 Feld[x][y] = EL_SOKOBAN_OBJECT;
8053 if (Back[x][y] == Back[nextx][nexty])
8054 PlaySoundLevelAction(x, y, ACTION_PUSHING);
8055 else if (Back[x][y] != 0)
8056 PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8059 PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8062 if (local_player->sokobanfields_still_needed == 0 &&
8063 game.emulation == EMU_SOKOBAN)
8065 player->LevelSolved = player->GameOver = TRUE;
8066 PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
8070 PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
8072 InitMovingField(x, y, move_direction);
8073 GfxAction[x][y] = ACTION_PUSHING;
8075 if (mode == DF_SNAP)
8076 ContinueMoving(x, y);
8078 MovPos[x][y] = (dx != 0 ? dx : dy);
8080 Pushed[x][y] = TRUE;
8081 Pushed[nextx][nexty] = TRUE;
8083 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8084 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8086 player->push_delay_value = -1; /* get new value later */
8088 CheckTriggeredElementSideChange(x, y, element, dig_side,
8089 CE_OTHER_GETS_PUSHED);
8090 CheckElementSideChange(x, y, element, dig_side,
8091 CE_PUSHED_BY_PLAYER, -1);
8095 else if (IS_SWITCHABLE(element))
8097 if (PLAYER_SWITCHING(player, x, y))
8100 player->is_switching = TRUE;
8101 player->switch_x = x;
8102 player->switch_y = y;
8104 PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
8106 if (element == EL_ROBOT_WHEEL)
8108 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8112 DrawLevelField(x, y);
8114 else if (element == EL_SP_TERMINAL)
8118 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8120 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8122 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8123 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8126 else if (IS_BELT_SWITCH(element))
8128 ToggleBeltSwitch(x, y);
8130 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8131 element == EL_SWITCHGATE_SWITCH_DOWN)
8133 ToggleSwitchgateSwitch(x, y);
8135 else if (element == EL_LIGHT_SWITCH ||
8136 element == EL_LIGHT_SWITCH_ACTIVE)
8138 ToggleLightSwitch(x, y);
8141 PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8142 SND_LIGHT_SWITCH_ACTIVATING :
8143 SND_LIGHT_SWITCH_DEACTIVATING);
8146 else if (element == EL_TIMEGATE_SWITCH)
8148 ActivateTimegateSwitch(x, y);
8150 else if (element == EL_BALLOON_SWITCH_LEFT ||
8151 element == EL_BALLOON_SWITCH_RIGHT ||
8152 element == EL_BALLOON_SWITCH_UP ||
8153 element == EL_BALLOON_SWITCH_DOWN ||
8154 element == EL_BALLOON_SWITCH_ANY)
8156 if (element == EL_BALLOON_SWITCH_ANY)
8157 game.balloon_dir = move_direction;
8159 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8160 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8161 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8162 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8165 else if (element == EL_LAMP)
8167 Feld[x][y] = EL_LAMP_ACTIVE;
8168 local_player->lights_still_needed--;
8170 DrawLevelField(x, y);
8172 else if (element == EL_TIME_ORB_FULL)
8174 Feld[x][y] = EL_TIME_ORB_EMPTY;
8176 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8178 DrawLevelField(x, y);
8181 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8189 if (!PLAYER_SWITCHING(player, x, y))
8191 player->is_switching = TRUE;
8192 player->switch_x = x;
8193 player->switch_y = y;
8195 CheckTriggeredElementSideChange(x, y, element, dig_side,
8196 CE_OTHER_IS_SWITCHING);
8197 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8200 CheckTriggeredElementSideChange(x, y, element, dig_side,
8201 CE_OTHER_GETS_PRESSED);
8202 CheckElementSideChange(x, y, element, dig_side,
8203 CE_PRESSED_BY_PLAYER, -1);
8206 return MF_NO_ACTION;
8209 player->push_delay = 0;
8211 if (Feld[x][y] != element) /* really digged/collected something */
8212 player->is_collecting = !player->is_digging;
8217 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8219 int jx = player->jx, jy = player->jy;
8220 int x = jx + dx, y = jy + dy;
8221 int snap_direction = (dx == -1 ? MV_LEFT :
8222 dx == +1 ? MV_RIGHT :
8224 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8226 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8229 if (!player->active || !IN_LEV_FIELD(x, y))
8237 if (player->MovPos == 0)
8238 player->is_pushing = FALSE;
8240 player->is_snapping = FALSE;
8242 if (player->MovPos == 0)
8244 player->is_moving = FALSE;
8245 player->is_digging = FALSE;
8246 player->is_collecting = FALSE;
8252 if (player->is_snapping)
8255 player->MovDir = snap_direction;
8257 player->is_moving = FALSE;
8258 player->is_digging = FALSE;
8259 player->is_collecting = FALSE;
8261 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8264 player->is_snapping = TRUE;
8266 player->is_moving = FALSE;
8267 player->is_digging = FALSE;
8268 player->is_collecting = FALSE;
8270 DrawLevelField(x, y);
8276 boolean DropElement(struct PlayerInfo *player)
8278 int jx = player->jx, jy = player->jy;
8281 if (!player->active || player->MovPos)
8284 old_element = Feld[jx][jy];
8286 /* check if player has anything that can be dropped */
8287 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8290 /* check if anything can be dropped at the current position */
8291 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8294 /* collected custom elements can only be dropped on empty fields */
8295 if (player->inventory_size > 0 &&
8296 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8297 && old_element != EL_EMPTY)
8300 if (old_element != EL_EMPTY)
8301 Back[jx][jy] = old_element; /* store old element on this field */
8303 MovDelay[jx][jy] = 96;
8305 ResetGfxAnimation(jx, jy);
8306 ResetRandomAnimationValue(jx, jy);
8308 if (player->inventory_size > 0)
8310 int new_element = player->inventory_element[--player->inventory_size];
8312 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8313 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8316 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8317 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8319 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8320 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8322 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8324 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8325 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8327 TestIfElementTouchesCustomElement(jx, jy);
8329 else /* player is dropping a dyna bomb */
8331 player->dynabombs_left--;
8334 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8336 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8337 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8339 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8345 /* ------------------------------------------------------------------------- */
8346 /* game sound playing functions */
8347 /* ------------------------------------------------------------------------- */
8349 static int *loop_sound_frame = NULL;
8350 static int *loop_sound_volume = NULL;
8352 void InitPlaySoundLevel()
8354 int num_sounds = getSoundListSize();
8356 if (loop_sound_frame != NULL)
8357 free(loop_sound_frame);
8359 if (loop_sound_volume != NULL)
8360 free(loop_sound_volume);
8362 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8363 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8366 static void PlaySoundLevel(int x, int y, int nr)
8368 int sx = SCREENX(x), sy = SCREENY(y);
8369 int volume, stereo_position;
8370 int max_distance = 8;
8371 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8373 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8374 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8377 if (!IN_LEV_FIELD(x, y) ||
8378 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8379 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8382 volume = SOUND_MAX_VOLUME;
8384 if (!IN_SCR_FIELD(sx, sy))
8386 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8387 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8389 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8392 stereo_position = (SOUND_MAX_LEFT +
8393 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8394 (SCR_FIELDX + 2 * max_distance));
8396 if (IS_LOOP_SOUND(nr))
8398 /* This assures that quieter loop sounds do not overwrite louder ones,
8399 while restarting sound volume comparison with each new game frame. */
8401 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8404 loop_sound_volume[nr] = volume;
8405 loop_sound_frame[nr] = FrameCounter;
8408 PlaySoundExt(nr, volume, stereo_position, type);
8411 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8413 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8414 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8415 y < LEVELY(BY1) ? LEVELY(BY1) :
8416 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8420 static void PlaySoundLevelAction(int x, int y, int action)
8422 PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8425 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8427 int sound_effect = element_info[element].sound[action];
8429 if (sound_effect != SND_UNDEFINED)
8430 PlaySoundLevel(x, y, sound_effect);
8433 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8435 int sound_effect = element_info[Feld[x][y]].sound[action];
8437 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8438 PlaySoundLevel(x, y, sound_effect);
8441 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8443 int sound_effect = element_info[Feld[x][y]].sound[action];
8445 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8446 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8449 void RaiseScore(int value)
8451 local_player->score += value;
8452 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8455 void RaiseScoreElement(int element)
8461 case EL_EMERALD_YELLOW:
8462 case EL_EMERALD_RED:
8463 case EL_EMERALD_PURPLE:
8464 case EL_SP_INFOTRON:
8465 RaiseScore(level.score[SC_EMERALD]);
8468 RaiseScore(level.score[SC_DIAMOND]);
8471 RaiseScore(level.score[SC_CRYSTAL]);
8474 RaiseScore(level.score[SC_PEARL]);
8477 case EL_BD_BUTTERFLY:
8478 case EL_SP_ELECTRON:
8479 RaiseScore(level.score[SC_BUG]);
8483 case EL_SP_SNIKSNAK:
8484 RaiseScore(level.score[SC_SPACESHIP]);
8487 case EL_DARK_YAMYAM:
8488 RaiseScore(level.score[SC_YAMYAM]);
8491 RaiseScore(level.score[SC_ROBOT]);
8494 RaiseScore(level.score[SC_PACMAN]);
8497 RaiseScore(level.score[SC_NUT]);
8500 case EL_SP_DISK_RED:
8501 case EL_DYNABOMB_INCREASE_NUMBER:
8502 case EL_DYNABOMB_INCREASE_SIZE:
8503 case EL_DYNABOMB_INCREASE_POWER:
8504 RaiseScore(level.score[SC_DYNAMITE]);
8506 case EL_SHIELD_NORMAL:
8507 case EL_SHIELD_DEADLY:
8508 RaiseScore(level.score[SC_SHIELD]);
8511 RaiseScore(level.score[SC_TIME_BONUS]);
8517 RaiseScore(level.score[SC_KEY]);
8520 RaiseScore(element_info[element].collect_score);
8525 void RequestQuitGame(boolean ask_if_really_quit)
8527 if (AllPlayersGone ||
8528 !ask_if_really_quit ||
8529 level_editor_test_game ||
8530 Request("Do you really want to quit the game ?",
8531 REQ_ASK | REQ_STAY_CLOSED))
8533 #if defined(PLATFORM_UNIX)
8534 if (options.network)
8535 SendToServer_StopPlaying();
8539 game_status = GAME_MODE_MAIN;
8545 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8550 /* ---------- new game button stuff ---------------------------------------- */
8552 /* graphic position values for game buttons */
8553 #define GAME_BUTTON_XSIZE 30
8554 #define GAME_BUTTON_YSIZE 30
8555 #define GAME_BUTTON_XPOS 5
8556 #define GAME_BUTTON_YPOS 215
8557 #define SOUND_BUTTON_XPOS 5
8558 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8560 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8561 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8562 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8563 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8564 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8565 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8572 } gamebutton_info[NUM_GAME_BUTTONS] =
8575 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8580 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8585 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8590 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8591 SOUND_CTRL_ID_MUSIC,
8592 "background music on/off"
8595 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8596 SOUND_CTRL_ID_LOOPS,
8597 "sound loops on/off"
8600 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8601 SOUND_CTRL_ID_SIMPLE,
8602 "normal sounds on/off"
8606 void CreateGameButtons()
8610 for (i=0; i<NUM_GAME_BUTTONS; i++)
8612 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8613 struct GadgetInfo *gi;
8616 unsigned long event_mask;
8617 int gd_xoffset, gd_yoffset;
8618 int gd_x1, gd_x2, gd_y1, gd_y2;
8621 gd_xoffset = gamebutton_info[i].x;
8622 gd_yoffset = gamebutton_info[i].y;
8623 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8624 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8626 if (id == GAME_CTRL_ID_STOP ||
8627 id == GAME_CTRL_ID_PAUSE ||
8628 id == GAME_CTRL_ID_PLAY)
8630 button_type = GD_TYPE_NORMAL_BUTTON;
8632 event_mask = GD_EVENT_RELEASED;
8633 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8634 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8638 button_type = GD_TYPE_CHECK_BUTTON;
8640 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8641 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8642 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8643 event_mask = GD_EVENT_PRESSED;
8644 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8645 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8648 gi = CreateGadget(GDI_CUSTOM_ID, id,
8649 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8650 GDI_X, DX + gd_xoffset,
8651 GDI_Y, DY + gd_yoffset,
8652 GDI_WIDTH, GAME_BUTTON_XSIZE,
8653 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8654 GDI_TYPE, button_type,
8655 GDI_STATE, GD_BUTTON_UNPRESSED,
8656 GDI_CHECKED, checked,
8657 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8658 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8659 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8660 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8661 GDI_EVENT_MASK, event_mask,
8662 GDI_CALLBACK_ACTION, HandleGameButtons,
8666 Error(ERR_EXIT, "cannot create gadget");
8668 game_gadget[id] = gi;
8672 void FreeGameButtons()
8676 for (i=0; i<NUM_GAME_BUTTONS; i++)
8677 FreeGadget(game_gadget[i]);
8680 static void MapGameButtons()
8684 for (i=0; i<NUM_GAME_BUTTONS; i++)
8685 MapGadget(game_gadget[i]);
8688 void UnmapGameButtons()
8692 for (i=0; i<NUM_GAME_BUTTONS; i++)
8693 UnmapGadget(game_gadget[i]);
8696 static void HandleGameButtons(struct GadgetInfo *gi)
8698 int id = gi->custom_id;
8700 if (game_status != GAME_MODE_PLAYING)
8705 case GAME_CTRL_ID_STOP:
8706 RequestQuitGame(TRUE);
8709 case GAME_CTRL_ID_PAUSE:
8710 if (options.network)
8712 #if defined(PLATFORM_UNIX)
8714 SendToServer_ContinuePlaying();
8716 SendToServer_PausePlaying();
8720 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8723 case GAME_CTRL_ID_PLAY:
8726 #if defined(PLATFORM_UNIX)
8727 if (options.network)
8728 SendToServer_ContinuePlaying();
8732 tape.pausing = FALSE;
8733 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8738 case SOUND_CTRL_ID_MUSIC:
8739 if (setup.sound_music)
8741 setup.sound_music = FALSE;
8744 else if (audio.music_available)
8746 setup.sound = setup.sound_music = TRUE;
8748 SetAudioMode(setup.sound);
8749 PlayMusic(level_nr);
8753 case SOUND_CTRL_ID_LOOPS:
8754 if (setup.sound_loops)
8755 setup.sound_loops = FALSE;
8756 else if (audio.loops_available)
8758 setup.sound = setup.sound_loops = TRUE;
8759 SetAudioMode(setup.sound);
8763 case SOUND_CTRL_ID_SIMPLE:
8764 if (setup.sound_simple)
8765 setup.sound_simple = FALSE;
8766 else if (audio.sound_available)
8768 setup.sound = setup.sound_simple = TRUE;
8769 SetAudioMode(setup.sound);