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);
5908 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
5911 static byte stored_player_action[MAX_PLAYERS];
5912 static int num_stored_actions = 0;
5914 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5915 int left = player_action & JOY_LEFT;
5916 int right = player_action & JOY_RIGHT;
5917 int up = player_action & JOY_UP;
5918 int down = player_action & JOY_DOWN;
5919 int button1 = player_action & JOY_BUTTON_1;
5920 int button2 = player_action & JOY_BUTTON_2;
5921 int dx = (left ? -1 : right ? 1 : 0);
5922 int dy = (up ? -1 : down ? 1 : 0);
5925 stored_player_action[player->index_nr] = 0;
5926 num_stored_actions++;
5930 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
5933 if (!player->active || tape.pausing)
5939 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
5943 snapped = SnapField(player, dx, dy);
5947 dropped = DropElement(player);
5949 moved = MovePlayer(player, dx, dy);
5952 if (tape.single_step && tape.recording && !tape.pausing)
5954 if (button1 || (dropped && !moved))
5956 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5957 SnapField(player, 0, 0); /* stop snapping */
5962 return player_action;
5964 stored_player_action[player->index_nr] = player_action;
5970 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
5973 /* no actions for this player (no input at player's configured device) */
5975 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5976 SnapField(player, 0, 0);
5977 CheckGravityMovement(player);
5979 if (player->MovPos == 0)
5980 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5982 if (player->MovPos == 0) /* needed for tape.playing */
5983 player->is_moving = FALSE;
5989 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5991 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
5993 TapeRecordAction(stored_player_action);
5994 num_stored_actions = 0;
6001 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6003 static byte stored_player_action[MAX_PLAYERS];
6004 static int num_stored_actions = 0;
6005 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6006 int left = player_action & JOY_LEFT;
6007 int right = player_action & JOY_RIGHT;
6008 int up = player_action & JOY_UP;
6009 int down = player_action & JOY_DOWN;
6010 int button1 = player_action & JOY_BUTTON_1;
6011 int button2 = player_action & JOY_BUTTON_2;
6012 int dx = (left ? -1 : right ? 1 : 0);
6013 int dy = (up ? -1 : down ? 1 : 0);
6015 stored_player_action[player->index_nr] = 0;
6016 num_stored_actions++;
6018 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6020 if (!player->active || tape.pausing)
6025 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6028 snapped = SnapField(player, dx, dy);
6032 dropped = DropElement(player);
6034 moved = MovePlayer(player, dx, dy);
6037 if (tape.single_step && tape.recording && !tape.pausing)
6039 if (button1 || (dropped && !moved))
6041 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6042 SnapField(player, 0, 0); /* stop snapping */
6046 stored_player_action[player->index_nr] = player_action;
6050 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6052 /* no actions for this player (no input at player's configured device) */
6054 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6055 SnapField(player, 0, 0);
6056 CheckGravityMovement(player);
6058 if (player->MovPos == 0)
6059 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6061 if (player->MovPos == 0) /* needed for tape.playing */
6062 player->is_moving = FALSE;
6065 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6067 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6069 TapeRecordAction(stored_player_action);
6070 num_stored_actions = 0;
6077 static unsigned long action_delay = 0;
6078 unsigned long action_delay_value;
6079 int magic_wall_x = 0, magic_wall_y = 0;
6080 int i, x, y, element, graphic;
6081 byte *recorded_player_action;
6082 byte summarized_player_action = 0;
6084 byte tape_action[MAX_PLAYERS];
6087 if (game_status != GAME_MODE_PLAYING)
6090 action_delay_value =
6091 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6093 if (tape.playing && tape.index_search && !tape.pausing)
6094 action_delay_value = 0;
6096 /* ---------- main game synchronization point ---------- */
6098 WaitUntilDelayReached(&action_delay, action_delay_value);
6100 if (network_playing && !network_player_action_received)
6104 printf("DEBUG: try to get network player actions in time\n");
6108 #if defined(PLATFORM_UNIX)
6109 /* last chance to get network player actions without main loop delay */
6113 if (game_status != GAME_MODE_PLAYING)
6116 if (!network_player_action_received)
6120 printf("DEBUG: failed to get network player actions in time\n");
6130 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6132 for (i=0; i<MAX_PLAYERS; i++)
6134 summarized_player_action |= stored_player[i].action;
6136 if (!network_playing)
6137 stored_player[i].effective_action = stored_player[i].action;
6140 #if defined(PLATFORM_UNIX)
6141 if (network_playing)
6142 SendToServer_MovePlayer(summarized_player_action);
6145 if (!options.network && !setup.team_mode)
6146 local_player->effective_action = summarized_player_action;
6148 for (i=0; i < MAX_PLAYERS; i++)
6150 int actual_player_action = stored_player[i].effective_action;
6152 if (stored_player[i].programmed_action)
6153 actual_player_action = stored_player[i].programmed_action;
6155 if (recorded_player_action)
6156 actual_player_action = recorded_player_action[i];
6158 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6160 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6165 TapeRecordAction(tape_action);
6168 network_player_action_received = FALSE;
6170 ScrollScreen(NULL, SCROLL_GO_ON);
6176 for (i=0; i<MAX_PLAYERS; i++)
6177 stored_player[i].Frame++;
6181 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6183 for (i=0; i<MAX_PLAYERS; i++)
6185 struct PlayerInfo *player = &stored_player[i];
6189 if (player->active && player->is_pushing && player->is_moving &&
6192 ContinueMoving(x, y);
6194 /* continue moving after pushing (this is actually a bug) */
6195 if (!IS_MOVING(x, y))
6204 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6206 Changed[x][y] = CE_BITMASK_DEFAULT;
6207 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6210 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6212 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6213 printf("GameActions(): This should never happen!\n");
6215 ChangePage[x][y] = -1;
6220 if (WasJustMoving[x][y] > 0)
6221 WasJustMoving[x][y]--;
6222 if (WasJustFalling[x][y] > 0)
6223 WasJustFalling[x][y]--;
6228 /* reset finished pushing action (not done in ContinueMoving() to allow
6229 continous pushing animation for elements with zero push delay) */
6230 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6232 ResetGfxAnimation(x, y);
6233 DrawLevelField(x, y);
6238 if (IS_BLOCKED(x, y))
6242 Blocked2Moving(x, y, &oldx, &oldy);
6243 if (!IS_MOVING(oldx, oldy))
6245 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6246 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6247 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6248 printf("GameActions(): This should never happen!\n");
6254 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6256 element = Feld[x][y];
6258 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6260 graphic = el2img(element);
6266 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6268 element = graphic = 0;
6272 if (graphic_info[graphic].anim_global_sync)
6273 GfxFrame[x][y] = FrameCounter;
6275 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6276 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6277 ResetRandomAnimationValue(x, y);
6279 SetRandomAnimationValue(x, y);
6282 PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6285 if (IS_INACTIVE(element))
6287 if (IS_ANIMATED(graphic))
6288 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6294 /* this may take place after moving, so 'element' may have changed */
6296 if (IS_CHANGING(x, y))
6298 if (IS_CHANGING(x, y) &&
6299 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6303 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6304 element_info[element].event_page_nr[CE_DELAY]);
6306 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6309 element = Feld[x][y];
6310 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6314 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6319 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6321 if (element == EL_MOLE)
6322 printf("::: %d, %d, %d [%d]\n",
6323 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6327 if (element == EL_YAMYAM)
6328 printf("::: %d, %d, %d\n",
6329 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6333 if (IS_ANIMATED(graphic) &&
6337 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6340 if (element == EL_BUG)
6341 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6345 if (element == EL_MOLE)
6346 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6350 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6351 EdelsteinFunkeln(x, y);
6353 else if ((element == EL_ACID ||
6354 element == EL_EXIT_OPEN ||
6355 element == EL_SP_EXIT_OPEN ||
6356 element == EL_SP_TERMINAL ||
6357 element == EL_SP_TERMINAL_ACTIVE ||
6358 element == EL_EXTRA_TIME ||
6359 element == EL_SHIELD_NORMAL ||
6360 element == EL_SHIELD_DEADLY) &&
6361 IS_ANIMATED(graphic))
6362 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6363 else if (IS_MOVING(x, y))
6364 ContinueMoving(x, y);
6365 else if (IS_ACTIVE_BOMB(element))
6366 CheckDynamite(x, y);
6368 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6369 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6371 else if (element == EL_AMOEBA_GROWING)
6372 AmoebeWaechst(x, y);
6373 else if (element == EL_AMOEBA_SHRINKING)
6374 AmoebaDisappearing(x, y);
6376 #if !USE_NEW_AMOEBA_CODE
6377 else if (IS_AMOEBALIVE(element))
6378 AmoebeAbleger(x, y);
6381 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6383 else if (element == EL_EXIT_CLOSED)
6385 else if (element == EL_SP_EXIT_CLOSED)
6387 else if (element == EL_EXPANDABLE_WALL_GROWING)
6389 else if (element == EL_EXPANDABLE_WALL ||
6390 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6391 element == EL_EXPANDABLE_WALL_VERTICAL ||
6392 element == EL_EXPANDABLE_WALL_ANY)
6394 else if (element == EL_FLAMES)
6395 CheckForDragon(x, y);
6397 else if (IS_AUTO_CHANGING(element))
6398 ChangeElement(x, y);
6400 else if (element == EL_EXPLOSION)
6401 ; /* drawing of correct explosion animation is handled separately */
6402 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6403 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6406 /* this may take place after moving, so 'element' may have changed */
6407 if (IS_AUTO_CHANGING(Feld[x][y]))
6408 ChangeElement(x, y);
6411 if (IS_BELT_ACTIVE(element))
6412 PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6414 if (game.magic_wall_active)
6416 int jx = local_player->jx, jy = local_player->jy;
6418 /* play the element sound at the position nearest to the player */
6419 if ((element == EL_MAGIC_WALL_FULL ||
6420 element == EL_MAGIC_WALL_ACTIVE ||
6421 element == EL_MAGIC_WALL_EMPTYING ||
6422 element == EL_BD_MAGIC_WALL_FULL ||
6423 element == EL_BD_MAGIC_WALL_ACTIVE ||
6424 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6425 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6433 #if USE_NEW_AMOEBA_CODE
6434 /* new experimental amoeba growth stuff */
6436 if (!(FrameCounter % 8))
6439 static unsigned long random = 1684108901;
6441 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6444 x = (random >> 10) % lev_fieldx;
6445 y = (random >> 20) % lev_fieldy;
6447 x = RND(lev_fieldx);
6448 y = RND(lev_fieldy);
6450 element = Feld[x][y];
6452 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6453 if (!IS_PLAYER(x,y) &&
6454 (element == EL_EMPTY ||
6455 element == EL_SAND ||
6456 element == EL_QUICKSAND_EMPTY ||
6457 element == EL_ACID_SPLASH_LEFT ||
6458 element == EL_ACID_SPLASH_RIGHT))
6460 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6461 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6462 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6463 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6464 Feld[x][y] = EL_AMOEBA_DROP;
6467 random = random * 129 + 1;
6473 if (game.explosions_delayed)
6476 game.explosions_delayed = FALSE;
6478 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6480 element = Feld[x][y];
6482 if (ExplodeField[x][y])
6483 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6484 else if (element == EL_EXPLOSION)
6485 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6487 ExplodeField[x][y] = EX_NO_EXPLOSION;
6490 game.explosions_delayed = TRUE;
6493 if (game.magic_wall_active)
6495 if (!(game.magic_wall_time_left % 4))
6497 int element = Feld[magic_wall_x][magic_wall_y];
6499 if (element == EL_BD_MAGIC_WALL_FULL ||
6500 element == EL_BD_MAGIC_WALL_ACTIVE ||
6501 element == EL_BD_MAGIC_WALL_EMPTYING)
6502 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6504 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6507 if (game.magic_wall_time_left > 0)
6509 game.magic_wall_time_left--;
6510 if (!game.magic_wall_time_left)
6512 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6514 element = Feld[x][y];
6516 if (element == EL_MAGIC_WALL_ACTIVE ||
6517 element == EL_MAGIC_WALL_FULL)
6519 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6520 DrawLevelField(x, y);
6522 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6523 element == EL_BD_MAGIC_WALL_FULL)
6525 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6526 DrawLevelField(x, y);
6530 game.magic_wall_active = FALSE;
6535 if (game.light_time_left > 0)
6537 game.light_time_left--;
6539 if (game.light_time_left == 0)
6540 RedrawAllLightSwitchesAndInvisibleElements();
6543 if (game.timegate_time_left > 0)
6545 game.timegate_time_left--;
6547 if (game.timegate_time_left == 0)
6548 CloseAllOpenTimegates();
6551 for (i=0; i<MAX_PLAYERS; i++)
6553 struct PlayerInfo *player = &stored_player[i];
6555 if (SHIELD_ON(player))
6557 if (player->shield_deadly_time_left)
6558 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6559 else if (player->shield_normal_time_left)
6560 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6564 if (TimeFrames >= FRAMES_PER_SECOND)
6569 for (i=0; i<MAX_PLAYERS; i++)
6571 struct PlayerInfo *player = &stored_player[i];
6573 if (SHIELD_ON(player))
6575 player->shield_normal_time_left--;
6577 if (player->shield_deadly_time_left > 0)
6578 player->shield_deadly_time_left--;
6582 if (tape.recording || tape.playing)
6583 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6589 if (TimeLeft <= 10 && setup.time_limit)
6590 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6592 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6594 if (!TimeLeft && setup.time_limit)
6595 for (i=0; i<MAX_PLAYERS; i++)
6596 KillHero(&stored_player[i]);
6598 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6599 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6604 if (options.debug) /* calculate frames per second */
6606 static unsigned long fps_counter = 0;
6607 static int fps_frames = 0;
6608 unsigned long fps_delay_ms = Counter() - fps_counter;
6612 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6614 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6617 fps_counter = Counter();
6620 redraw_mask |= REDRAW_FPS;
6624 if (stored_player[0].jx != stored_player[0].last_jx ||
6625 stored_player[0].jy != stored_player[0].last_jy)
6626 printf("::: %d, %d, %d, %d, %d\n",
6627 stored_player[0].MovDir,
6628 stored_player[0].MovPos,
6629 stored_player[0].GfxPos,
6630 stored_player[0].Frame,
6631 stored_player[0].StepFrame);
6638 for (i=0; i<MAX_PLAYERS; i++)
6641 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6643 stored_player[i].Frame += move_frames;
6645 if (stored_player[i].MovPos != 0)
6646 stored_player[i].StepFrame += move_frames;
6651 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6653 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6655 local_player->show_envelope = 0;
6660 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6662 int min_x = x, min_y = y, max_x = x, max_y = y;
6665 for (i=0; i<MAX_PLAYERS; i++)
6667 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6669 if (!stored_player[i].active || &stored_player[i] == player)
6672 min_x = MIN(min_x, jx);
6673 min_y = MIN(min_y, jy);
6674 max_x = MAX(max_x, jx);
6675 max_y = MAX(max_y, jy);
6678 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6681 static boolean AllPlayersInVisibleScreen()
6685 for (i=0; i<MAX_PLAYERS; i++)
6687 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6689 if (!stored_player[i].active)
6692 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6699 void ScrollLevel(int dx, int dy)
6701 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6704 BlitBitmap(drawto_field, drawto_field,
6705 FX + TILEX * (dx == -1) - softscroll_offset,
6706 FY + TILEY * (dy == -1) - softscroll_offset,
6707 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6708 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6709 FX + TILEX * (dx == 1) - softscroll_offset,
6710 FY + TILEY * (dy == 1) - softscroll_offset);
6714 x = (dx == 1 ? BX1 : BX2);
6715 for (y=BY1; y <= BY2; y++)
6716 DrawScreenField(x, y);
6721 y = (dy == 1 ? BY1 : BY2);
6722 for (x=BX1; x <= BX2; x++)
6723 DrawScreenField(x, y);
6726 redraw_mask |= REDRAW_FIELD;
6729 static void CheckGravityMovement(struct PlayerInfo *player)
6731 if (game.gravity && !player->programmed_action)
6733 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6734 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6736 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6737 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6738 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6739 int jx = player->jx, jy = player->jy;
6740 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6741 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6742 int new_jx = jx + dx, new_jy = jy + dy;
6743 boolean field_under_player_is_free =
6744 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6745 boolean player_is_moving_to_valid_field =
6746 (IN_LEV_FIELD(new_jx, new_jy) &&
6747 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6748 Feld[new_jx][new_jy] == EL_SAND));
6749 /* !!! extend EL_SAND to anything diggable !!! */
6751 if (field_under_player_is_free &&
6752 !player_is_moving_to_valid_field &&
6753 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6754 player->programmed_action = MV_DOWN;
6760 -----------------------------------------------------------------------------
6761 dx, dy: direction (non-diagonal) to try to move the player to
6762 real_dx, real_dy: direction as read from input device (can be diagonal)
6765 boolean MovePlayerOneStep(struct PlayerInfo *player,
6766 int dx, int dy, int real_dx, int real_dy)
6769 static int change_sides[4][2] =
6771 /* enter side leave side */
6772 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6773 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6774 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6775 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6777 int move_direction = (dx == -1 ? MV_LEFT :
6778 dx == +1 ? MV_RIGHT :
6780 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6781 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6782 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6784 int jx = player->jx, jy = player->jy;
6785 int new_jx = jx + dx, new_jy = jy + dy;
6789 if (!player->active || (!dx && !dy))
6790 return MF_NO_ACTION;
6792 player->MovDir = (dx < 0 ? MV_LEFT :
6795 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6797 if (!IN_LEV_FIELD(new_jx, new_jy))
6798 return MF_NO_ACTION;
6800 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6801 return MF_NO_ACTION;
6804 element = MovingOrBlocked2Element(new_jx, new_jy);
6806 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6809 if (DONT_RUN_INTO(element))
6811 if (element == EL_ACID && dx == 0 && dy == 1)
6814 Feld[jx][jy] = EL_PLAYER_1;
6815 InitMovingField(jx, jy, MV_DOWN);
6816 Store[jx][jy] = EL_ACID;
6817 ContinueMoving(jx, jy);
6821 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6826 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6827 if (can_move != MF_MOVING)
6830 /* check if DigField() has caused relocation of the player */
6831 if (player->jx != jx || player->jy != jy)
6832 return MF_NO_ACTION;
6834 StorePlayer[jx][jy] = 0;
6835 player->last_jx = jx;
6836 player->last_jy = jy;
6837 player->jx = new_jx;
6838 player->jy = new_jy;
6839 StorePlayer[new_jx][new_jy] = player->element_nr;
6842 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6844 ScrollPlayer(player, SCROLL_INIT);
6847 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6849 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6850 CE_OTHER_GETS_LEFT);
6851 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6852 CE_LEFT_BY_PLAYER, -1);
6855 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6857 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6858 enter_side, CE_OTHER_GETS_ENTERED);
6859 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6860 CE_ENTERED_BY_PLAYER, -1);
6867 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6869 int jx = player->jx, jy = player->jy;
6870 int old_jx = jx, old_jy = jy;
6871 int moved = MF_NO_ACTION;
6873 if (!player->active || (!dx && !dy))
6877 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6881 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6882 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6886 /* remove the last programmed player action */
6887 player->programmed_action = 0;
6891 /* should only happen if pre-1.2 tape recordings are played */
6892 /* this is only for backward compatibility */
6894 int original_move_delay_value = player->move_delay_value;
6897 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6901 /* scroll remaining steps with finest movement resolution */
6902 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6904 while (player->MovPos)
6906 ScrollPlayer(player, SCROLL_GO_ON);
6907 ScrollScreen(NULL, SCROLL_GO_ON);
6913 player->move_delay_value = original_move_delay_value;
6916 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6918 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6919 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6923 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6924 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6930 if (moved & MF_MOVING && !ScreenMovPos &&
6931 (player == local_player || !options.network))
6933 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6934 int offset = (setup.scroll_delay ? 3 : 0);
6936 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6938 /* actual player has left the screen -- scroll in that direction */
6939 if (jx != old_jx) /* player has moved horizontally */
6940 scroll_x += (jx - old_jx);
6941 else /* player has moved vertically */
6942 scroll_y += (jy - old_jy);
6946 if (jx != old_jx) /* player has moved horizontally */
6948 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6949 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6950 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6952 /* don't scroll over playfield boundaries */
6953 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6954 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6956 /* don't scroll more than one field at a time */
6957 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6959 /* don't scroll against the player's moving direction */
6960 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6961 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6962 scroll_x = old_scroll_x;
6964 else /* player has moved vertically */
6966 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6967 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6968 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6970 /* don't scroll over playfield boundaries */
6971 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6972 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6974 /* don't scroll more than one field at a time */
6975 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6977 /* don't scroll against the player's moving direction */
6978 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6979 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6980 scroll_y = old_scroll_y;
6984 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6986 if (!options.network && !AllPlayersInVisibleScreen())
6988 scroll_x = old_scroll_x;
6989 scroll_y = old_scroll_y;
6993 ScrollScreen(player, SCROLL_INIT);
6994 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7001 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7003 if (!(moved & MF_MOVING) && !player->is_pushing)
7008 player->StepFrame = 0;
7010 if (moved & MF_MOVING)
7012 if (old_jx != jx && old_jy == jy)
7013 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7014 else if (old_jx == jx && old_jy != jy)
7015 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7017 DrawLevelField(jx, jy); /* for "crumbled sand" */
7019 player->last_move_dir = player->MovDir;
7020 player->is_moving = TRUE;
7022 player->is_snapping = FALSE;
7026 player->is_switching = FALSE;
7032 static int change_sides[4][2] =
7034 /* enter side leave side */
7035 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7036 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7037 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7038 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7040 int move_direction = player->MovDir;
7041 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7042 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7045 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7047 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7048 leave_side, CE_OTHER_GETS_LEFT);
7049 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7050 leave_side, CE_LEFT_BY_PLAYER, -1);
7053 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7055 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7056 enter_side, CE_OTHER_GETS_ENTERED);
7057 CheckElementSideChange(jx, jy, Feld[jx][jy],
7058 enter_side, CE_ENTERED_BY_PLAYER, -1);
7069 CheckGravityMovement(player);
7072 player->last_move_dir = MV_NO_MOVING;
7074 player->is_moving = FALSE;
7077 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7079 TestIfHeroTouchesBadThing(jx, jy);
7080 TestIfPlayerTouchesCustomElement(jx, jy);
7083 if (!player->active)
7089 void ScrollPlayer(struct PlayerInfo *player, int mode)
7091 int jx = player->jx, jy = player->jy;
7092 int last_jx = player->last_jx, last_jy = player->last_jy;
7093 int move_stepsize = TILEX / player->move_delay_value;
7095 if (!player->active || !player->MovPos)
7098 if (mode == SCROLL_INIT)
7100 player->actual_frame_counter = FrameCounter;
7101 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7103 if (Feld[last_jx][last_jy] == EL_EMPTY)
7104 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7111 else if (!FrameReached(&player->actual_frame_counter, 1))
7114 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7115 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7117 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7118 Feld[last_jx][last_jy] = EL_EMPTY;
7120 /* before DrawPlayer() to draw correct player graphic for this case */
7121 if (player->MovPos == 0)
7122 CheckGravityMovement(player);
7125 DrawPlayer(player); /* needed here only to cleanup last field */
7128 if (player->MovPos == 0) /* player reached destination field */
7130 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7132 /* continue with normal speed after quickly moving through gate */
7133 HALVE_PLAYER_SPEED(player);
7135 /* be able to make the next move without delay */
7136 player->move_delay = 0;
7139 player->last_jx = jx;
7140 player->last_jy = jy;
7142 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7143 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7144 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7146 DrawPlayer(player); /* needed here only to cleanup last field */
7149 if (local_player->friends_still_needed == 0 ||
7150 IS_SP_ELEMENT(Feld[jx][jy]))
7151 player->LevelSolved = player->GameOver = TRUE;
7154 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7156 TestIfHeroTouchesBadThing(jx, jy);
7157 TestIfPlayerTouchesCustomElement(jx, jy);
7159 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7162 if (!player->active)
7166 if (tape.single_step && tape.recording && !tape.pausing &&
7167 !player->programmed_action)
7168 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7172 void ScrollScreen(struct PlayerInfo *player, int mode)
7174 static unsigned long screen_frame_counter = 0;
7176 if (mode == SCROLL_INIT)
7178 /* set scrolling step size according to actual player's moving speed */
7179 ScrollStepSize = TILEX / player->move_delay_value;
7181 screen_frame_counter = FrameCounter;
7182 ScreenMovDir = player->MovDir;
7183 ScreenMovPos = player->MovPos;
7184 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7187 else if (!FrameReached(&screen_frame_counter, 1))
7192 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7193 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7194 redraw_mask |= REDRAW_FIELD;
7197 ScreenMovDir = MV_NO_MOVING;
7200 void TestIfPlayerTouchesCustomElement(int x, int y)
7202 static int xy[4][2] =
7209 static int change_sides[4][2] =
7211 /* center side border side */
7212 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7213 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7214 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7215 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7217 static int touch_dir[4] =
7224 int center_element = Feld[x][y]; /* should always be non-moving! */
7229 int xx = x + xy[i][0];
7230 int yy = y + xy[i][1];
7231 int center_side = change_sides[i][0];
7232 int border_side = change_sides[i][1];
7235 if (!IN_LEV_FIELD(xx, yy))
7238 if (IS_PLAYER(x, y))
7240 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7241 border_element = Feld[xx][yy]; /* may be moving! */
7242 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7243 border_element = Feld[xx][yy];
7244 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7245 border_element = MovingOrBlocked2Element(xx, yy);
7247 continue; /* center and border element do not touch */
7249 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7250 CE_OTHER_GETS_TOUCHED);
7251 CheckElementSideChange(xx, yy, border_element, border_side,
7252 CE_TOUCHED_BY_PLAYER, -1);
7254 else if (IS_PLAYER(xx, yy))
7256 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7258 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7260 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7261 continue; /* center and border element do not touch */
7264 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7265 CE_OTHER_GETS_TOUCHED);
7266 CheckElementSideChange(x, y, center_element, center_side,
7267 CE_TOUCHED_BY_PLAYER, -1);
7274 void TestIfElementTouchesCustomElement(int x, int y)
7276 static int xy[4][2] =
7283 static int change_sides[4][2] =
7285 /* center side border side */
7286 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7287 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7288 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7289 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7291 static int touch_dir[4] =
7298 boolean change_center_element = FALSE;
7299 int center_element_change_page = 0;
7300 int center_element = Feld[x][y]; /* should always be non-moving! */
7305 int xx = x + xy[i][0];
7306 int yy = y + xy[i][1];
7307 int center_side = change_sides[i][0];
7308 int border_side = change_sides[i][1];
7311 if (!IN_LEV_FIELD(xx, yy))
7314 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7315 border_element = Feld[xx][yy]; /* may be moving! */
7316 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7317 border_element = Feld[xx][yy];
7318 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7319 border_element = MovingOrBlocked2Element(xx, yy);
7321 continue; /* center and border element do not touch */
7323 /* check for change of center element (but change it only once) */
7324 if (IS_CUSTOM_ELEMENT(center_element) &&
7325 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7326 !change_center_element)
7328 for (j=0; j < element_info[center_element].num_change_pages; j++)
7330 struct ElementChangeInfo *change =
7331 &element_info[center_element].change_page[j];
7333 if (change->can_change &&
7334 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7335 change->sides & border_side &&
7336 change->trigger_element == border_element)
7338 change_center_element = TRUE;
7339 center_element_change_page = j;
7346 /* check for change of border element */
7347 if (IS_CUSTOM_ELEMENT(border_element) &&
7348 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7350 for (j=0; j < element_info[border_element].num_change_pages; j++)
7352 struct ElementChangeInfo *change =
7353 &element_info[border_element].change_page[j];
7355 if (change->can_change &&
7356 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7357 change->sides & center_side &&
7358 change->trigger_element == center_element)
7360 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7361 CE_OTHER_IS_TOUCHING, j);
7368 if (change_center_element)
7369 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7370 CE_OTHER_IS_TOUCHING, center_element_change_page);
7373 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7375 int i, kill_x = -1, kill_y = -1;
7376 static int test_xy[4][2] =
7383 static int test_dir[4] =
7393 int test_x, test_y, test_move_dir, test_element;
7395 test_x = good_x + test_xy[i][0];
7396 test_y = good_y + test_xy[i][1];
7397 if (!IN_LEV_FIELD(test_x, test_y))
7401 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7404 test_element = Feld[test_x][test_y];
7406 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7409 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7410 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7412 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7413 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7421 if (kill_x != -1 || kill_y != -1)
7423 if (IS_PLAYER(good_x, good_y))
7425 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7427 if (player->shield_deadly_time_left > 0)
7428 Bang(kill_x, kill_y);
7429 else if (!PLAYER_PROTECTED(good_x, good_y))
7433 Bang(good_x, good_y);
7437 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7439 int i, kill_x = -1, kill_y = -1;
7440 int bad_element = Feld[bad_x][bad_y];
7441 static int test_xy[4][2] =
7448 static int touch_dir[4] =
7455 static int test_dir[4] =
7463 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7468 int test_x, test_y, test_move_dir, test_element;
7470 test_x = bad_x + test_xy[i][0];
7471 test_y = bad_y + test_xy[i][1];
7472 if (!IN_LEV_FIELD(test_x, test_y))
7476 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7478 test_element = Feld[test_x][test_y];
7480 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7481 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7483 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7484 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7486 /* good thing is player or penguin that does not move away */
7487 if (IS_PLAYER(test_x, test_y))
7489 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7491 if (bad_element == EL_ROBOT && player->is_moving)
7492 continue; /* robot does not kill player if he is moving */
7494 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7496 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7497 continue; /* center and border element do not touch */
7504 else if (test_element == EL_PENGUIN)
7513 if (kill_x != -1 || kill_y != -1)
7515 if (IS_PLAYER(kill_x, kill_y))
7517 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7519 if (player->shield_deadly_time_left > 0)
7521 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7525 Bang(kill_x, kill_y);
7529 void TestIfHeroTouchesBadThing(int x, int y)
7531 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7534 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7536 TestIfGoodThingHitsBadThing(x, y, move_dir);
7539 void TestIfBadThingTouchesHero(int x, int y)
7541 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7544 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7546 TestIfBadThingHitsGoodThing(x, y, move_dir);
7549 void TestIfFriendTouchesBadThing(int x, int y)
7551 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7554 void TestIfBadThingTouchesFriend(int x, int y)
7556 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7559 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7561 int i, kill_x = bad_x, kill_y = bad_y;
7562 static int xy[4][2] =
7574 x = bad_x + xy[i][0];
7575 y = bad_y + xy[i][1];
7576 if (!IN_LEV_FIELD(x, y))
7579 element = Feld[x][y];
7580 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7581 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7589 if (kill_x != bad_x || kill_y != bad_y)
7593 void KillHero(struct PlayerInfo *player)
7595 int jx = player->jx, jy = player->jy;
7597 if (!player->active)
7600 /* remove accessible field at the player's position */
7601 Feld[jx][jy] = EL_EMPTY;
7603 /* deactivate shield (else Bang()/Explode() would not work right) */
7604 player->shield_normal_time_left = 0;
7605 player->shield_deadly_time_left = 0;
7611 static void KillHeroUnlessProtected(int x, int y)
7613 if (!PLAYER_PROTECTED(x, y))
7614 KillHero(PLAYERINFO(x, y));
7617 void BuryHero(struct PlayerInfo *player)
7619 int jx = player->jx, jy = player->jy;
7621 if (!player->active)
7625 PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7627 PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7629 PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7631 player->GameOver = TRUE;
7635 void RemoveHero(struct PlayerInfo *player)
7637 int jx = player->jx, jy = player->jy;
7638 int i, found = FALSE;
7640 player->present = FALSE;
7641 player->active = FALSE;
7643 if (!ExplodeField[jx][jy])
7644 StorePlayer[jx][jy] = 0;
7646 for (i=0; i<MAX_PLAYERS; i++)
7647 if (stored_player[i].active)
7651 AllPlayersGone = TRUE;
7658 =============================================================================
7659 checkDiagonalPushing()
7660 -----------------------------------------------------------------------------
7661 check if diagonal input device direction results in pushing of object
7662 (by checking if the alternative direction is walkable, diggable, ...)
7663 =============================================================================
7666 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7667 int x, int y, int real_dx, int real_dy)
7669 int jx, jy, dx, dy, xx, yy;
7671 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7674 /* diagonal direction: check alternative direction */
7679 xx = jx + (dx == 0 ? real_dx : 0);
7680 yy = jy + (dy == 0 ? real_dy : 0);
7682 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7686 =============================================================================
7688 -----------------------------------------------------------------------------
7689 x, y: field next to player (non-diagonal) to try to dig to
7690 real_dx, real_dy: direction as read from input device (can be diagonal)
7691 =============================================================================
7694 int DigField(struct PlayerInfo *player,
7695 int x, int y, int real_dx, int real_dy, int mode)
7697 static int change_sides[4] =
7699 CH_SIDE_RIGHT, /* moving left */
7700 CH_SIDE_LEFT, /* moving right */
7701 CH_SIDE_BOTTOM, /* moving up */
7702 CH_SIDE_TOP, /* moving down */
7704 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7705 int jx = player->jx, jy = player->jy;
7706 int dx = x - jx, dy = y - jy;
7707 int nextx = x + dx, nexty = y + dy;
7708 int move_direction = (dx == -1 ? MV_LEFT :
7709 dx == +1 ? MV_RIGHT :
7711 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7712 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7715 if (player->MovPos == 0)
7717 player->is_digging = FALSE;
7718 player->is_collecting = FALSE;
7721 if (player->MovPos == 0) /* last pushing move finished */
7722 player->is_pushing = FALSE;
7724 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7726 player->is_switching = FALSE;
7727 player->push_delay = 0;
7729 return MF_NO_ACTION;
7732 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7733 return MF_NO_ACTION;
7736 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7738 if (IS_TUBE(Feld[jx][jy]) ||
7739 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7743 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7744 int tube_leave_directions[][2] =
7746 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7747 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7748 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7749 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7750 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7751 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7752 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7753 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7754 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7755 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7756 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7757 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7760 while (tube_leave_directions[i][0] != tube_element)
7763 if (tube_leave_directions[i][0] == -1) /* should not happen */
7767 if (!(tube_leave_directions[i][1] & move_direction))
7768 return MF_NO_ACTION; /* tube has no opening in this direction */
7771 element = Feld[x][y];
7773 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7774 game.engine_version >= VERSION_IDENT(2,2,0,0))
7775 return MF_NO_ACTION;
7779 case EL_SP_PORT_LEFT:
7780 case EL_SP_PORT_RIGHT:
7782 case EL_SP_PORT_DOWN:
7783 case EL_SP_PORT_HORIZONTAL:
7784 case EL_SP_PORT_VERTICAL:
7785 case EL_SP_PORT_ANY:
7786 case EL_SP_GRAVITY_PORT_LEFT:
7787 case EL_SP_GRAVITY_PORT_RIGHT:
7788 case EL_SP_GRAVITY_PORT_UP:
7789 case EL_SP_GRAVITY_PORT_DOWN:
7791 element != EL_SP_PORT_LEFT &&
7792 element != EL_SP_GRAVITY_PORT_LEFT &&
7793 element != EL_SP_PORT_HORIZONTAL &&
7794 element != EL_SP_PORT_ANY) ||
7796 element != EL_SP_PORT_RIGHT &&
7797 element != EL_SP_GRAVITY_PORT_RIGHT &&
7798 element != EL_SP_PORT_HORIZONTAL &&
7799 element != EL_SP_PORT_ANY) ||
7801 element != EL_SP_PORT_UP &&
7802 element != EL_SP_GRAVITY_PORT_UP &&
7803 element != EL_SP_PORT_VERTICAL &&
7804 element != EL_SP_PORT_ANY) ||
7806 element != EL_SP_PORT_DOWN &&
7807 element != EL_SP_GRAVITY_PORT_DOWN &&
7808 element != EL_SP_PORT_VERTICAL &&
7809 element != EL_SP_PORT_ANY) ||
7810 !IN_LEV_FIELD(nextx, nexty) ||
7811 !IS_FREE(nextx, nexty))
7812 return MF_NO_ACTION;
7814 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7815 element == EL_SP_GRAVITY_PORT_RIGHT ||
7816 element == EL_SP_GRAVITY_PORT_UP ||
7817 element == EL_SP_GRAVITY_PORT_DOWN)
7818 game.gravity = !game.gravity;
7820 /* automatically move to the next field with double speed */
7821 player->programmed_action = move_direction;
7822 DOUBLE_PLAYER_SPEED(player);
7824 PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7828 case EL_TUBE_VERTICAL:
7829 case EL_TUBE_HORIZONTAL:
7830 case EL_TUBE_VERTICAL_LEFT:
7831 case EL_TUBE_VERTICAL_RIGHT:
7832 case EL_TUBE_HORIZONTAL_UP:
7833 case EL_TUBE_HORIZONTAL_DOWN:
7834 case EL_TUBE_LEFT_UP:
7835 case EL_TUBE_LEFT_DOWN:
7836 case EL_TUBE_RIGHT_UP:
7837 case EL_TUBE_RIGHT_DOWN:
7840 int tube_enter_directions[][2] =
7842 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7843 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7844 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7845 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7846 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7847 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7848 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7849 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7850 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7851 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7852 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7853 { -1, MV_NO_MOVING }
7856 while (tube_enter_directions[i][0] != element)
7859 if (tube_enter_directions[i][0] == -1) /* should not happen */
7863 if (!(tube_enter_directions[i][1] & move_direction))
7864 return MF_NO_ACTION; /* tube has no opening in this direction */
7866 PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7872 if (IS_WALKABLE(element))
7874 int sound_action = ACTION_WALKING;
7876 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7878 if (!player->key[element - EL_GATE_1])
7879 return MF_NO_ACTION;
7881 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7883 if (!player->key[element - EL_GATE_1_GRAY])
7884 return MF_NO_ACTION;
7886 else if (element == EL_EXIT_OPEN ||
7887 element == EL_SP_EXIT_OPEN ||
7888 element == EL_SP_EXIT_OPENING)
7890 sound_action = ACTION_PASSING; /* player is passing exit */
7892 else if (element == EL_EMPTY)
7894 sound_action = ACTION_MOVING; /* nothing to walk on */
7897 /* play sound from background or player, whatever is available */
7898 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7899 PlaySoundLevelElementAction(x, y, element, sound_action);
7901 PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7905 else if (IS_PASSABLE(element))
7907 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7908 return MF_NO_ACTION;
7911 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7912 return MF_NO_ACTION;
7915 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7917 if (!player->key[element - EL_EM_GATE_1])
7918 return MF_NO_ACTION;
7920 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7922 if (!player->key[element - EL_EM_GATE_1_GRAY])
7923 return MF_NO_ACTION;
7926 /* automatically move to the next field with double speed */
7927 player->programmed_action = move_direction;
7928 DOUBLE_PLAYER_SPEED(player);
7930 PlaySoundLevelAction(x, y, ACTION_PASSING);
7934 else if (IS_DIGGABLE(element))
7938 if (mode != DF_SNAP)
7941 GfxElement[x][y] = GFX_ELEMENT(element);
7944 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7946 player->is_digging = TRUE;
7949 PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7951 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7954 if (mode == DF_SNAP)
7955 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7960 else if (IS_COLLECTIBLE(element))
7964 if (mode != DF_SNAP)
7966 GfxElement[x][y] = element;
7967 player->is_collecting = TRUE;
7970 if (element == EL_SPEED_PILL)
7971 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7972 else if (element == EL_EXTRA_TIME && level.time > 0)
7975 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7977 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7979 player->shield_normal_time_left += 10;
7980 if (element == EL_SHIELD_DEADLY)
7981 player->shield_deadly_time_left += 10;
7983 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7985 if (player->inventory_size < MAX_INVENTORY_SIZE)
7986 player->inventory_element[player->inventory_size++] = element;
7988 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7989 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7991 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7993 player->dynabomb_count++;
7994 player->dynabombs_left++;
7996 else if (element == EL_DYNABOMB_INCREASE_SIZE)
7998 player->dynabomb_size++;
8000 else if (element == EL_DYNABOMB_INCREASE_POWER)
8002 player->dynabomb_xl = TRUE;
8004 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8005 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8007 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8008 element - EL_KEY_1 : element - EL_EM_KEY_1);
8010 player->key[key_nr] = TRUE;
8012 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8013 el2edimg(EL_KEY_1 + key_nr));
8014 redraw_mask |= REDRAW_DOOR_1;
8016 else if (IS_ENVELOPE(element))
8019 player->show_envelope = element;
8021 ShowEnvelope(element - EL_ENVELOPE_1);
8024 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8028 for (i=0; i < element_info[element].collect_count; i++)
8029 if (player->inventory_size < MAX_INVENTORY_SIZE)
8030 player->inventory_element[player->inventory_size++] = element;
8032 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8033 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8035 else if (element_info[element].collect_count > 0)
8037 local_player->gems_still_needed -=
8038 element_info[element].collect_count;
8039 if (local_player->gems_still_needed < 0)
8040 local_player->gems_still_needed = 0;
8042 DrawText(DX_EMERALDS, DY_EMERALDS,
8043 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8046 RaiseScoreElement(element);
8047 PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
8049 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8052 if (mode == DF_SNAP)
8053 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8058 else if (IS_PUSHABLE(element))
8060 if (mode == DF_SNAP && element != EL_BD_ROCK)
8061 return MF_NO_ACTION;
8063 if (CAN_FALL(element) && dy)
8064 return MF_NO_ACTION;
8066 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8067 !(element == EL_SPRING && use_spring_bug))
8068 return MF_NO_ACTION;
8071 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8072 ((move_direction & MV_VERTICAL &&
8073 ((element_info[element].move_pattern & MV_LEFT &&
8074 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8075 (element_info[element].move_pattern & MV_RIGHT &&
8076 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8077 (move_direction & MV_HORIZONTAL &&
8078 ((element_info[element].move_pattern & MV_UP &&
8079 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8080 (element_info[element].move_pattern & MV_DOWN &&
8081 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8082 return MF_NO_ACTION;
8086 /* do not push elements already moving away faster than player */
8087 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8088 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8089 return MF_NO_ACTION;
8091 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8092 return MF_NO_ACTION;
8096 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8098 if (player->push_delay_value == -1)
8099 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8101 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8103 if (!player->is_pushing)
8104 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8108 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8109 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8110 !player_is_pushing))
8111 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8114 if (!player->is_pushing &&
8115 game.engine_version >= VERSION_IDENT(2,2,0,7))
8116 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8120 printf("::: push delay: %ld [%d, %d] [%d]\n",
8121 player->push_delay_value, FrameCounter, game.engine_version,
8122 player->is_pushing);
8125 player->is_pushing = TRUE;
8127 if (!(IN_LEV_FIELD(nextx, nexty) &&
8128 (IS_FREE(nextx, nexty) ||
8129 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8130 IS_SB_ELEMENT(element)))))
8131 return MF_NO_ACTION;
8133 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8134 return MF_NO_ACTION;
8136 if (player->push_delay == 0) /* new pushing; restart delay */
8137 player->push_delay = FrameCounter;
8139 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8140 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8141 element != EL_SPRING && element != EL_BALLOON)
8143 /* make sure that there is no move delay before next try to push */
8144 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8145 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8147 return MF_NO_ACTION;
8151 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8154 if (IS_SB_ELEMENT(element))
8156 if (element == EL_SOKOBAN_FIELD_FULL)
8158 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8159 local_player->sokobanfields_still_needed++;
8162 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8164 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8165 local_player->sokobanfields_still_needed--;
8168 Feld[x][y] = EL_SOKOBAN_OBJECT;
8170 if (Back[x][y] == Back[nextx][nexty])
8171 PlaySoundLevelAction(x, y, ACTION_PUSHING);
8172 else if (Back[x][y] != 0)
8173 PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8176 PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8179 if (local_player->sokobanfields_still_needed == 0 &&
8180 game.emulation == EMU_SOKOBAN)
8182 player->LevelSolved = player->GameOver = TRUE;
8183 PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
8187 PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
8189 InitMovingField(x, y, move_direction);
8190 GfxAction[x][y] = ACTION_PUSHING;
8192 if (mode == DF_SNAP)
8193 ContinueMoving(x, y);
8195 MovPos[x][y] = (dx != 0 ? dx : dy);
8197 Pushed[x][y] = TRUE;
8198 Pushed[nextx][nexty] = TRUE;
8200 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8201 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8203 player->push_delay_value = -1; /* get new value later */
8205 CheckTriggeredElementSideChange(x, y, element, dig_side,
8206 CE_OTHER_GETS_PUSHED);
8207 CheckElementSideChange(x, y, element, dig_side,
8208 CE_PUSHED_BY_PLAYER, -1);
8212 else if (IS_SWITCHABLE(element))
8214 if (PLAYER_SWITCHING(player, x, y))
8217 player->is_switching = TRUE;
8218 player->switch_x = x;
8219 player->switch_y = y;
8221 PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
8223 if (element == EL_ROBOT_WHEEL)
8225 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8229 DrawLevelField(x, y);
8231 else if (element == EL_SP_TERMINAL)
8235 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8237 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8239 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8240 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8243 else if (IS_BELT_SWITCH(element))
8245 ToggleBeltSwitch(x, y);
8247 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8248 element == EL_SWITCHGATE_SWITCH_DOWN)
8250 ToggleSwitchgateSwitch(x, y);
8252 else if (element == EL_LIGHT_SWITCH ||
8253 element == EL_LIGHT_SWITCH_ACTIVE)
8255 ToggleLightSwitch(x, y);
8258 PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8259 SND_LIGHT_SWITCH_ACTIVATING :
8260 SND_LIGHT_SWITCH_DEACTIVATING);
8263 else if (element == EL_TIMEGATE_SWITCH)
8265 ActivateTimegateSwitch(x, y);
8267 else if (element == EL_BALLOON_SWITCH_LEFT ||
8268 element == EL_BALLOON_SWITCH_RIGHT ||
8269 element == EL_BALLOON_SWITCH_UP ||
8270 element == EL_BALLOON_SWITCH_DOWN ||
8271 element == EL_BALLOON_SWITCH_ANY)
8273 if (element == EL_BALLOON_SWITCH_ANY)
8274 game.balloon_dir = move_direction;
8276 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8277 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8278 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8279 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8282 else if (element == EL_LAMP)
8284 Feld[x][y] = EL_LAMP_ACTIVE;
8285 local_player->lights_still_needed--;
8287 DrawLevelField(x, y);
8289 else if (element == EL_TIME_ORB_FULL)
8291 Feld[x][y] = EL_TIME_ORB_EMPTY;
8293 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8295 DrawLevelField(x, y);
8298 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8306 if (!PLAYER_SWITCHING(player, x, y))
8308 player->is_switching = TRUE;
8309 player->switch_x = x;
8310 player->switch_y = y;
8312 CheckTriggeredElementSideChange(x, y, element, dig_side,
8313 CE_OTHER_IS_SWITCHING);
8314 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8317 CheckTriggeredElementSideChange(x, y, element, dig_side,
8318 CE_OTHER_GETS_PRESSED);
8319 CheckElementSideChange(x, y, element, dig_side,
8320 CE_PRESSED_BY_PLAYER, -1);
8323 return MF_NO_ACTION;
8326 player->push_delay = 0;
8328 if (Feld[x][y] != element) /* really digged/collected something */
8329 player->is_collecting = !player->is_digging;
8334 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8336 int jx = player->jx, jy = player->jy;
8337 int x = jx + dx, y = jy + dy;
8338 int snap_direction = (dx == -1 ? MV_LEFT :
8339 dx == +1 ? MV_RIGHT :
8341 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8343 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8346 if (!player->active || !IN_LEV_FIELD(x, y))
8354 if (player->MovPos == 0)
8355 player->is_pushing = FALSE;
8357 player->is_snapping = FALSE;
8359 if (player->MovPos == 0)
8361 player->is_moving = FALSE;
8362 player->is_digging = FALSE;
8363 player->is_collecting = FALSE;
8369 if (player->is_snapping)
8372 player->MovDir = snap_direction;
8374 player->is_moving = FALSE;
8375 player->is_digging = FALSE;
8376 player->is_collecting = FALSE;
8378 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8381 player->is_snapping = TRUE;
8383 player->is_moving = FALSE;
8384 player->is_digging = FALSE;
8385 player->is_collecting = FALSE;
8387 DrawLevelField(x, y);
8393 boolean DropElement(struct PlayerInfo *player)
8395 int jx = player->jx, jy = player->jy;
8398 if (!player->active || player->MovPos)
8401 old_element = Feld[jx][jy];
8403 /* check if player has anything that can be dropped */
8404 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8407 /* check if anything can be dropped at the current position */
8408 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8411 /* collected custom elements can only be dropped on empty fields */
8412 if (player->inventory_size > 0 &&
8413 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8414 && old_element != EL_EMPTY)
8417 if (old_element != EL_EMPTY)
8418 Back[jx][jy] = old_element; /* store old element on this field */
8420 MovDelay[jx][jy] = 96;
8422 ResetGfxAnimation(jx, jy);
8423 ResetRandomAnimationValue(jx, jy);
8425 if (player->inventory_size > 0)
8427 int new_element = player->inventory_element[--player->inventory_size];
8429 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8430 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8433 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8434 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8436 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8437 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8439 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8441 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8442 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8444 TestIfElementTouchesCustomElement(jx, jy);
8446 else /* player is dropping a dyna bomb */
8448 player->dynabombs_left--;
8451 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8453 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8454 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8456 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8462 /* ------------------------------------------------------------------------- */
8463 /* game sound playing functions */
8464 /* ------------------------------------------------------------------------- */
8466 static int *loop_sound_frame = NULL;
8467 static int *loop_sound_volume = NULL;
8469 void InitPlaySoundLevel()
8471 int num_sounds = getSoundListSize();
8473 if (loop_sound_frame != NULL)
8474 free(loop_sound_frame);
8476 if (loop_sound_volume != NULL)
8477 free(loop_sound_volume);
8479 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8480 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8483 static void PlaySoundLevel(int x, int y, int nr)
8485 int sx = SCREENX(x), sy = SCREENY(y);
8486 int volume, stereo_position;
8487 int max_distance = 8;
8488 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8490 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8491 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8494 if (!IN_LEV_FIELD(x, y) ||
8495 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8496 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8499 volume = SOUND_MAX_VOLUME;
8501 if (!IN_SCR_FIELD(sx, sy))
8503 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8504 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8506 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8509 stereo_position = (SOUND_MAX_LEFT +
8510 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8511 (SCR_FIELDX + 2 * max_distance));
8513 if (IS_LOOP_SOUND(nr))
8515 /* This assures that quieter loop sounds do not overwrite louder ones,
8516 while restarting sound volume comparison with each new game frame. */
8518 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8521 loop_sound_volume[nr] = volume;
8522 loop_sound_frame[nr] = FrameCounter;
8525 PlaySoundExt(nr, volume, stereo_position, type);
8528 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8530 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8531 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8532 y < LEVELY(BY1) ? LEVELY(BY1) :
8533 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8537 static void PlaySoundLevelAction(int x, int y, int action)
8539 PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8542 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8544 int sound_effect = element_info[element].sound[action];
8546 if (sound_effect != SND_UNDEFINED)
8547 PlaySoundLevel(x, y, sound_effect);
8550 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8552 int sound_effect = element_info[Feld[x][y]].sound[action];
8554 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8555 PlaySoundLevel(x, y, sound_effect);
8558 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8560 int sound_effect = element_info[Feld[x][y]].sound[action];
8562 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8563 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8566 void RaiseScore(int value)
8568 local_player->score += value;
8569 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8572 void RaiseScoreElement(int element)
8578 case EL_EMERALD_YELLOW:
8579 case EL_EMERALD_RED:
8580 case EL_EMERALD_PURPLE:
8581 case EL_SP_INFOTRON:
8582 RaiseScore(level.score[SC_EMERALD]);
8585 RaiseScore(level.score[SC_DIAMOND]);
8588 RaiseScore(level.score[SC_CRYSTAL]);
8591 RaiseScore(level.score[SC_PEARL]);
8594 case EL_BD_BUTTERFLY:
8595 case EL_SP_ELECTRON:
8596 RaiseScore(level.score[SC_BUG]);
8600 case EL_SP_SNIKSNAK:
8601 RaiseScore(level.score[SC_SPACESHIP]);
8604 case EL_DARK_YAMYAM:
8605 RaiseScore(level.score[SC_YAMYAM]);
8608 RaiseScore(level.score[SC_ROBOT]);
8611 RaiseScore(level.score[SC_PACMAN]);
8614 RaiseScore(level.score[SC_NUT]);
8617 case EL_SP_DISK_RED:
8618 case EL_DYNABOMB_INCREASE_NUMBER:
8619 case EL_DYNABOMB_INCREASE_SIZE:
8620 case EL_DYNABOMB_INCREASE_POWER:
8621 RaiseScore(level.score[SC_DYNAMITE]);
8623 case EL_SHIELD_NORMAL:
8624 case EL_SHIELD_DEADLY:
8625 RaiseScore(level.score[SC_SHIELD]);
8628 RaiseScore(level.score[SC_TIME_BONUS]);
8634 RaiseScore(level.score[SC_KEY]);
8637 RaiseScore(element_info[element].collect_score);
8642 void RequestQuitGame(boolean ask_if_really_quit)
8644 if (AllPlayersGone ||
8645 !ask_if_really_quit ||
8646 level_editor_test_game ||
8647 Request("Do you really want to quit the game ?",
8648 REQ_ASK | REQ_STAY_CLOSED))
8650 #if defined(PLATFORM_UNIX)
8651 if (options.network)
8652 SendToServer_StopPlaying();
8656 game_status = GAME_MODE_MAIN;
8662 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8667 /* ---------- new game button stuff ---------------------------------------- */
8669 /* graphic position values for game buttons */
8670 #define GAME_BUTTON_XSIZE 30
8671 #define GAME_BUTTON_YSIZE 30
8672 #define GAME_BUTTON_XPOS 5
8673 #define GAME_BUTTON_YPOS 215
8674 #define SOUND_BUTTON_XPOS 5
8675 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8677 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8678 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8679 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8680 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8681 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8682 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8689 } gamebutton_info[NUM_GAME_BUTTONS] =
8692 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8697 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8702 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8707 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8708 SOUND_CTRL_ID_MUSIC,
8709 "background music on/off"
8712 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8713 SOUND_CTRL_ID_LOOPS,
8714 "sound loops on/off"
8717 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8718 SOUND_CTRL_ID_SIMPLE,
8719 "normal sounds on/off"
8723 void CreateGameButtons()
8727 for (i=0; i<NUM_GAME_BUTTONS; i++)
8729 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8730 struct GadgetInfo *gi;
8733 unsigned long event_mask;
8734 int gd_xoffset, gd_yoffset;
8735 int gd_x1, gd_x2, gd_y1, gd_y2;
8738 gd_xoffset = gamebutton_info[i].x;
8739 gd_yoffset = gamebutton_info[i].y;
8740 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8741 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8743 if (id == GAME_CTRL_ID_STOP ||
8744 id == GAME_CTRL_ID_PAUSE ||
8745 id == GAME_CTRL_ID_PLAY)
8747 button_type = GD_TYPE_NORMAL_BUTTON;
8749 event_mask = GD_EVENT_RELEASED;
8750 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8751 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8755 button_type = GD_TYPE_CHECK_BUTTON;
8757 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8758 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8759 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8760 event_mask = GD_EVENT_PRESSED;
8761 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8762 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8765 gi = CreateGadget(GDI_CUSTOM_ID, id,
8766 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8767 GDI_X, DX + gd_xoffset,
8768 GDI_Y, DY + gd_yoffset,
8769 GDI_WIDTH, GAME_BUTTON_XSIZE,
8770 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8771 GDI_TYPE, button_type,
8772 GDI_STATE, GD_BUTTON_UNPRESSED,
8773 GDI_CHECKED, checked,
8774 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8775 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8776 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8777 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8778 GDI_EVENT_MASK, event_mask,
8779 GDI_CALLBACK_ACTION, HandleGameButtons,
8783 Error(ERR_EXIT, "cannot create gadget");
8785 game_gadget[id] = gi;
8789 void FreeGameButtons()
8793 for (i=0; i<NUM_GAME_BUTTONS; i++)
8794 FreeGadget(game_gadget[i]);
8797 static void MapGameButtons()
8801 for (i=0; i<NUM_GAME_BUTTONS; i++)
8802 MapGadget(game_gadget[i]);
8805 void UnmapGameButtons()
8809 for (i=0; i<NUM_GAME_BUTTONS; i++)
8810 UnmapGadget(game_gadget[i]);
8813 static void HandleGameButtons(struct GadgetInfo *gi)
8815 int id = gi->custom_id;
8817 if (game_status != GAME_MODE_PLAYING)
8822 case GAME_CTRL_ID_STOP:
8823 RequestQuitGame(TRUE);
8826 case GAME_CTRL_ID_PAUSE:
8827 if (options.network)
8829 #if defined(PLATFORM_UNIX)
8831 SendToServer_ContinuePlaying();
8833 SendToServer_PausePlaying();
8837 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8840 case GAME_CTRL_ID_PLAY:
8843 #if defined(PLATFORM_UNIX)
8844 if (options.network)
8845 SendToServer_ContinuePlaying();
8849 tape.pausing = FALSE;
8850 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8855 case SOUND_CTRL_ID_MUSIC:
8856 if (setup.sound_music)
8858 setup.sound_music = FALSE;
8861 else if (audio.music_available)
8863 setup.sound = setup.sound_music = TRUE;
8865 SetAudioMode(setup.sound);
8866 PlayMusic(level_nr);
8870 case SOUND_CTRL_ID_LOOPS:
8871 if (setup.sound_loops)
8872 setup.sound_loops = FALSE;
8873 else if (audio.loops_available)
8875 setup.sound = setup.sound_loops = TRUE;
8876 SetAudioMode(setup.sound);
8880 case SOUND_CTRL_ID_SIMPLE:
8881 if (setup.sound_simple)
8882 setup.sound_simple = FALSE;
8883 else if (audio.sound_available)
8885 setup.sound = setup.sound_simple = TRUE;
8886 SetAudioMode(setup.sound);