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 printf("::: starting game [%d]\n", FrameCounter);
1469 void InitMovDir(int x, int y)
1471 int i, element = Feld[x][y];
1472 static int xy[4][2] =
1479 static int direction[3][4] =
1481 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1482 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1483 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1492 Feld[x][y] = EL_BUG;
1493 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1496 case EL_SPACESHIP_RIGHT:
1497 case EL_SPACESHIP_UP:
1498 case EL_SPACESHIP_LEFT:
1499 case EL_SPACESHIP_DOWN:
1500 Feld[x][y] = EL_SPACESHIP;
1501 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1504 case EL_BD_BUTTERFLY_RIGHT:
1505 case EL_BD_BUTTERFLY_UP:
1506 case EL_BD_BUTTERFLY_LEFT:
1507 case EL_BD_BUTTERFLY_DOWN:
1508 Feld[x][y] = EL_BD_BUTTERFLY;
1509 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1512 case EL_BD_FIREFLY_RIGHT:
1513 case EL_BD_FIREFLY_UP:
1514 case EL_BD_FIREFLY_LEFT:
1515 case EL_BD_FIREFLY_DOWN:
1516 Feld[x][y] = EL_BD_FIREFLY;
1517 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1520 case EL_PACMAN_RIGHT:
1522 case EL_PACMAN_LEFT:
1523 case EL_PACMAN_DOWN:
1524 Feld[x][y] = EL_PACMAN;
1525 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1528 case EL_SP_SNIKSNAK:
1529 MovDir[x][y] = MV_UP;
1532 case EL_SP_ELECTRON:
1533 MovDir[x][y] = MV_LEFT;
1540 Feld[x][y] = EL_MOLE;
1541 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1545 if (IS_CUSTOM_ELEMENT(element))
1547 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1548 MovDir[x][y] = element_info[element].move_direction_initial;
1549 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1550 element_info[element].move_pattern == MV_TURNING_LEFT ||
1551 element_info[element].move_pattern == MV_TURNING_RIGHT)
1552 MovDir[x][y] = 1 << RND(4);
1553 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1554 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1555 else if (element_info[element].move_pattern == MV_VERTICAL)
1556 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1557 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1558 MovDir[x][y] = element_info[element].move_pattern;
1559 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1560 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1564 int x1 = x + xy[i][0];
1565 int y1 = y + xy[i][1];
1567 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1569 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1570 MovDir[x][y] = direction[0][i];
1572 MovDir[x][y] = direction[1][i];
1581 MovDir[x][y] = 1 << RND(4);
1583 if (element != EL_BUG &&
1584 element != EL_SPACESHIP &&
1585 element != EL_BD_BUTTERFLY &&
1586 element != EL_BD_FIREFLY)
1591 int x1 = x + xy[i][0];
1592 int y1 = y + xy[i][1];
1594 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1596 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1598 MovDir[x][y] = direction[0][i];
1601 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1602 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1604 MovDir[x][y] = direction[1][i];
1613 GfxDir[x][y] = MovDir[x][y];
1616 void InitAmoebaNr(int x, int y)
1619 int group_nr = AmoebeNachbarNr(x, y);
1623 for (i=1; i<MAX_NUM_AMOEBA; i++)
1625 if (AmoebaCnt[i] == 0)
1633 AmoebaNr[x][y] = group_nr;
1634 AmoebaCnt[group_nr]++;
1635 AmoebaCnt2[group_nr]++;
1641 boolean raise_level = FALSE;
1643 if (local_player->MovPos)
1647 if (tape.auto_play) /* tape might already be stopped here */
1648 tape.auto_play_level_solved = TRUE;
1650 if (tape.playing && tape.auto_play)
1651 tape.auto_play_level_solved = TRUE;
1654 local_player->LevelSolved = FALSE;
1656 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1660 if (!tape.playing && setup.sound_loops)
1661 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1662 SND_CTRL_PLAY_LOOP);
1664 while (TimeLeft > 0)
1666 if (!tape.playing && !setup.sound_loops)
1667 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1668 if (TimeLeft > 0 && !(TimeLeft % 10))
1669 RaiseScore(level.score[SC_TIME_BONUS]);
1670 if (TimeLeft > 100 && !(TimeLeft % 10))
1674 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1681 if (!tape.playing && setup.sound_loops)
1682 StopSound(SND_GAME_LEVELTIME_BONUS);
1684 else if (level.time == 0) /* level without time limit */
1686 if (!tape.playing && setup.sound_loops)
1687 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1688 SND_CTRL_PLAY_LOOP);
1690 while (TimePlayed < 999)
1692 if (!tape.playing && !setup.sound_loops)
1693 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1694 if (TimePlayed < 999 && !(TimePlayed % 10))
1695 RaiseScore(level.score[SC_TIME_BONUS]);
1696 if (TimePlayed < 900 && !(TimePlayed % 10))
1700 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1707 if (!tape.playing && setup.sound_loops)
1708 StopSound(SND_GAME_LEVELTIME_BONUS);
1711 /* close exit door after last player */
1712 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1713 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1715 int element = Feld[ExitX][ExitY];
1717 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1718 EL_SP_EXIT_CLOSING);
1720 PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1723 /* Hero disappears */
1724 DrawLevelField(ExitX, ExitY);
1730 CloseDoor(DOOR_CLOSE_1);
1735 SaveTape(tape.level_nr); /* Ask to save tape */
1738 if (level_nr == leveldir_current->handicap_level)
1740 leveldir_current->handicap_level++;
1741 SaveLevelSetup_SeriesInfo();
1744 if (level_editor_test_game)
1745 local_player->score = -1; /* no highscore when playing from editor */
1746 else if (level_nr < leveldir_current->last_level)
1747 raise_level = TRUE; /* advance to next level */
1749 if ((hi_pos = NewHiScore()) >= 0)
1751 game_status = GAME_MODE_SCORES;
1752 DrawHallOfFame(hi_pos);
1761 game_status = GAME_MODE_MAIN;
1778 LoadScore(level_nr);
1780 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1781 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1784 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1786 if (local_player->score > highscore[k].Score)
1788 /* player has made it to the hall of fame */
1790 if (k < MAX_SCORE_ENTRIES - 1)
1792 int m = MAX_SCORE_ENTRIES - 1;
1795 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1796 if (!strcmp(setup.player_name, highscore[l].Name))
1798 if (m == k) /* player's new highscore overwrites his old one */
1804 strcpy(highscore[l].Name, highscore[l - 1].Name);
1805 highscore[l].Score = highscore[l - 1].Score;
1812 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1813 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1814 highscore[k].Score = local_player->score;
1820 else if (!strncmp(setup.player_name, highscore[k].Name,
1821 MAX_PLAYER_NAME_LEN))
1822 break; /* player already there with a higher score */
1828 SaveScore(level_nr);
1833 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1835 if (player->GfxAction != action || player->GfxDir != dir)
1838 printf("Player frame reset! (%d => %d, %d => %d)\n",
1839 player->GfxAction, action, player->GfxDir, dir);
1842 player->GfxAction = action;
1843 player->GfxDir = dir;
1845 player->StepFrame = 0;
1849 static void ResetRandomAnimationValue(int x, int y)
1851 GfxRandom[x][y] = INIT_GFX_RANDOM();
1854 static void ResetGfxAnimation(int x, int y)
1857 GfxAction[x][y] = ACTION_DEFAULT;
1858 GfxDir[x][y] = MovDir[x][y];
1861 void InitMovingField(int x, int y, int direction)
1863 int element = Feld[x][y];
1864 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1865 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1869 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1870 ResetGfxAnimation(x, y);
1872 MovDir[newx][newy] = MovDir[x][y] = direction;
1873 GfxDir[x][y] = direction;
1875 if (Feld[newx][newy] == EL_EMPTY)
1876 Feld[newx][newy] = EL_BLOCKED;
1878 if (direction == MV_DOWN && CAN_FALL(element))
1879 GfxAction[x][y] = ACTION_FALLING;
1881 GfxAction[x][y] = ACTION_MOVING;
1883 GfxFrame[newx][newy] = GfxFrame[x][y];
1884 GfxRandom[newx][newy] = GfxRandom[x][y];
1885 GfxAction[newx][newy] = GfxAction[x][y];
1886 GfxDir[newx][newy] = GfxDir[x][y];
1889 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1891 int direction = MovDir[x][y];
1892 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1893 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1899 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1901 int oldx = x, oldy = y;
1902 int direction = MovDir[x][y];
1904 if (direction == MV_LEFT)
1906 else if (direction == MV_RIGHT)
1908 else if (direction == MV_UP)
1910 else if (direction == MV_DOWN)
1913 *comes_from_x = oldx;
1914 *comes_from_y = oldy;
1917 int MovingOrBlocked2Element(int x, int y)
1919 int element = Feld[x][y];
1921 if (element == EL_BLOCKED)
1925 Blocked2Moving(x, y, &oldx, &oldy);
1926 return Feld[oldx][oldy];
1932 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1934 /* like MovingOrBlocked2Element(), but if element is moving
1935 and (x,y) is the field the moving element is just leaving,
1936 return EL_BLOCKED instead of the element value */
1937 int element = Feld[x][y];
1939 if (IS_MOVING(x, y))
1941 if (element == EL_BLOCKED)
1945 Blocked2Moving(x, y, &oldx, &oldy);
1946 return Feld[oldx][oldy];
1955 static void RemoveField(int x, int y)
1957 Feld[x][y] = EL_EMPTY;
1964 ChangeDelay[x][y] = 0;
1965 ChangePage[x][y] = -1;
1966 Pushed[x][y] = FALSE;
1968 GfxElement[x][y] = EL_UNDEFINED;
1969 GfxAction[x][y] = ACTION_DEFAULT;
1970 GfxDir[x][y] = MV_NO_MOVING;
1973 void RemoveMovingField(int x, int y)
1975 int oldx = x, oldy = y, newx = x, newy = y;
1976 int element = Feld[x][y];
1977 int next_element = EL_UNDEFINED;
1979 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1982 if (IS_MOVING(x, y))
1984 Moving2Blocked(x, y, &newx, &newy);
1985 if (Feld[newx][newy] != EL_BLOCKED)
1988 else if (element == EL_BLOCKED)
1990 Blocked2Moving(x, y, &oldx, &oldy);
1991 if (!IS_MOVING(oldx, oldy))
1995 if (element == EL_BLOCKED &&
1996 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1997 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1998 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1999 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2000 next_element = get_next_element(Feld[oldx][oldy]);
2002 RemoveField(oldx, oldy);
2003 RemoveField(newx, newy);
2005 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2007 if (next_element != EL_UNDEFINED)
2008 Feld[oldx][oldy] = next_element;
2010 DrawLevelField(oldx, oldy);
2011 DrawLevelField(newx, newy);
2014 void DrawDynamite(int x, int y)
2016 int sx = SCREENX(x), sy = SCREENY(y);
2017 int graphic = el2img(Feld[x][y]);
2020 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2023 if (IS_WALKABLE_INSIDE(Back[x][y]))
2027 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2028 else if (Store[x][y])
2029 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2031 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2034 if (Back[x][y] || Store[x][y])
2035 DrawGraphicThruMask(sx, sy, graphic, frame);
2037 DrawGraphic(sx, sy, graphic, frame);
2039 if (game.emulation == EMU_SUPAPLEX)
2040 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2041 else if (Store[x][y])
2042 DrawGraphicThruMask(sx, sy, graphic, frame);
2044 DrawGraphic(sx, sy, graphic, frame);
2048 void CheckDynamite(int x, int y)
2050 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2054 if (MovDelay[x][y] != 0)
2057 PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2064 StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2066 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2067 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2068 StopSound(SND_DYNAMITE_ACTIVE);
2070 StopSound(SND_DYNABOMB_ACTIVE);
2076 void RelocatePlayer(int x, int y, int element)
2078 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2081 RemoveField(x, y); /* temporarily remove newly placed player */
2082 DrawLevelField(x, y);
2085 if (player->present)
2087 while (player->MovPos)
2089 ScrollPlayer(player, SCROLL_GO_ON);
2090 ScrollScreen(NULL, SCROLL_GO_ON);
2096 Delay(GAME_FRAME_DELAY);
2099 DrawPlayer(player); /* needed here only to cleanup last field */
2100 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2102 player->is_moving = FALSE;
2105 Feld[x][y] = element;
2106 InitPlayerField(x, y, element, TRUE);
2108 if (player == local_player)
2110 int scroll_xx = -999, scroll_yy = -999;
2112 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2115 int fx = FX, fy = FY;
2117 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2118 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2119 local_player->jx - MIDPOSX);
2121 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2122 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2123 local_player->jy - MIDPOSY);
2125 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2126 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2131 fx += dx * TILEX / 2;
2132 fy += dy * TILEY / 2;
2134 ScrollLevel(dx, dy);
2137 /* scroll in two steps of half tile size to make things smoother */
2138 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2140 Delay(GAME_FRAME_DELAY);
2142 /* scroll second step to align at full tile size */
2144 Delay(GAME_FRAME_DELAY);
2149 void Explode(int ex, int ey, int phase, int mode)
2153 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2154 int last_phase = num_phase * delay;
2155 int half_phase = (num_phase / 2) * delay;
2156 int first_phase_after_start = EX_PHASE_START + 1;
2158 if (game.explosions_delayed)
2160 ExplodeField[ex][ey] = mode;
2164 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2166 int center_element = Feld[ex][ey];
2169 /* --- This is only really needed (and now handled) in "Impact()". --- */
2170 /* do not explode moving elements that left the explode field in time */
2171 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2172 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2176 if (mode == EX_NORMAL || mode == EX_CENTER)
2177 PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
2179 /* remove things displayed in background while burning dynamite */
2180 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2183 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2185 /* put moving element to center field (and let it explode there) */
2186 center_element = MovingOrBlocked2Element(ex, ey);
2187 RemoveMovingField(ex, ey);
2188 Feld[ex][ey] = center_element;
2191 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2193 int xx = x - ex + 1;
2194 int yy = y - ey + 1;
2197 if (!IN_LEV_FIELD(x, y) ||
2198 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2199 (x != ex || y != ey)))
2202 element = Feld[x][y];
2204 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2206 element = MovingOrBlocked2Element(x, y);
2208 if (!IS_EXPLOSION_PROOF(element))
2209 RemoveMovingField(x, y);
2215 if (IS_EXPLOSION_PROOF(element))
2218 /* indestructible elements can only explode in center (but not flames) */
2219 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2220 element == EL_FLAMES)
2225 if ((IS_INDESTRUCTIBLE(element) &&
2226 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2227 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2228 element == EL_FLAMES)
2232 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2234 if (IS_ACTIVE_BOMB(element))
2236 /* re-activate things under the bomb like gate or penguin */
2237 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2244 /* save walkable background elements while explosion on same tile */
2246 if (IS_INDESTRUCTIBLE(element))
2247 Back[x][y] = element;
2249 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2250 Back[x][y] = element;
2253 /* ignite explodable elements reached by other explosion */
2254 if (element == EL_EXPLOSION)
2255 element = Store2[x][y];
2258 if (AmoebaNr[x][y] &&
2259 (element == EL_AMOEBA_FULL ||
2260 element == EL_BD_AMOEBA ||
2261 element == EL_AMOEBA_GROWING))
2263 AmoebaCnt[AmoebaNr[x][y]]--;
2264 AmoebaCnt2[AmoebaNr[x][y]]--;
2270 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2272 switch(StorePlayer[ex][ey])
2275 Store[x][y] = EL_EMERALD_RED;
2278 Store[x][y] = EL_EMERALD;
2281 Store[x][y] = EL_EMERALD_PURPLE;
2285 Store[x][y] = EL_EMERALD_YELLOW;
2289 if (game.emulation == EMU_SUPAPLEX)
2290 Store[x][y] = EL_EMPTY;
2292 else if (center_element == EL_MOLE)
2293 Store[x][y] = EL_EMERALD_RED;
2294 else if (center_element == EL_PENGUIN)
2295 Store[x][y] = EL_EMERALD_PURPLE;
2296 else if (center_element == EL_BUG)
2297 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2298 else if (center_element == EL_BD_BUTTERFLY)
2299 Store[x][y] = EL_BD_DIAMOND;
2300 else if (center_element == EL_SP_ELECTRON)
2301 Store[x][y] = EL_SP_INFOTRON;
2302 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2303 Store[x][y] = level.amoeba_content;
2304 else if (center_element == EL_YAMYAM)
2305 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2306 else if (IS_CUSTOM_ELEMENT(center_element) &&
2307 element_info[center_element].content[xx][yy] != EL_EMPTY)
2308 Store[x][y] = element_info[center_element].content[xx][yy];
2309 else if (element == EL_WALL_EMERALD)
2310 Store[x][y] = EL_EMERALD;
2311 else if (element == EL_WALL_DIAMOND)
2312 Store[x][y] = EL_DIAMOND;
2313 else if (element == EL_WALL_BD_DIAMOND)
2314 Store[x][y] = EL_BD_DIAMOND;
2315 else if (element == EL_WALL_EMERALD_YELLOW)
2316 Store[x][y] = EL_EMERALD_YELLOW;
2317 else if (element == EL_WALL_EMERALD_RED)
2318 Store[x][y] = EL_EMERALD_RED;
2319 else if (element == EL_WALL_EMERALD_PURPLE)
2320 Store[x][y] = EL_EMERALD_PURPLE;
2321 else if (element == EL_WALL_PEARL)
2322 Store[x][y] = EL_PEARL;
2323 else if (element == EL_WALL_CRYSTAL)
2324 Store[x][y] = EL_CRYSTAL;
2325 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2326 Store[x][y] = element_info[element].content[1][1];
2328 Store[x][y] = EL_EMPTY;
2330 if (x != ex || y != ey ||
2331 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2332 Store2[x][y] = element;
2335 if (AmoebaNr[x][y] &&
2336 (element == EL_AMOEBA_FULL ||
2337 element == EL_BD_AMOEBA ||
2338 element == EL_AMOEBA_GROWING))
2340 AmoebaCnt[AmoebaNr[x][y]]--;
2341 AmoebaCnt2[AmoebaNr[x][y]]--;
2347 MovDir[x][y] = MovPos[x][y] = 0;
2348 GfxDir[x][y] = MovDir[x][y];
2353 Feld[x][y] = EL_EXPLOSION;
2355 GfxElement[x][y] = center_element;
2357 GfxElement[x][y] = EL_UNDEFINED;
2360 ExplodePhase[x][y] = 1;
2364 if (center_element == EL_YAMYAM)
2365 game.yamyam_content_nr =
2366 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2377 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2381 /* activate this even in non-DEBUG version until cause for crash in
2382 getGraphicAnimationFrame() (see below) is found and eliminated */
2386 if (GfxElement[x][y] == EL_UNDEFINED)
2389 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2390 printf("Explode(): This should never happen!\n");
2393 GfxElement[x][y] = EL_EMPTY;
2397 if (phase == first_phase_after_start)
2399 int element = Store2[x][y];
2401 if (element == EL_BLACK_ORB)
2403 Feld[x][y] = Store2[x][y];
2408 else if (phase == half_phase)
2410 int element = Store2[x][y];
2412 if (IS_PLAYER(x, y))
2413 KillHeroUnlessProtected(x, y);
2414 else if (CAN_EXPLODE_BY_FIRE(element))
2416 Feld[x][y] = Store2[x][y];
2420 else if (element == EL_AMOEBA_TO_DIAMOND)
2421 AmoebeUmwandeln(x, y);
2424 if (phase == last_phase)
2428 element = Feld[x][y] = Store[x][y];
2429 Store[x][y] = Store2[x][y] = 0;
2430 GfxElement[x][y] = EL_UNDEFINED;
2432 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2433 element = Feld[x][y] = Back[x][y];
2436 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2437 GfxDir[x][y] = MV_NO_MOVING;
2438 ChangeDelay[x][y] = 0;
2439 ChangePage[x][y] = -1;
2441 InitField(x, y, FALSE);
2442 if (CAN_MOVE(element))
2444 DrawLevelField(x, y);
2446 TestIfElementTouchesCustomElement(x, y);
2448 if (GFX_CRUMBLED(element))
2449 DrawLevelFieldCrumbledSandNeighbours(x, y);
2451 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2452 StorePlayer[x][y] = 0;
2454 if (ELEM_IS_PLAYER(element))
2455 RelocatePlayer(x, y, element);
2457 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2460 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2462 int stored = Store[x][y];
2463 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2464 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2467 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2470 DrawLevelFieldCrumbledSand(x, y);
2472 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2474 DrawLevelElement(x, y, Back[x][y]);
2475 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2477 else if (IS_WALKABLE_UNDER(Back[x][y]))
2479 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2480 DrawLevelElementThruMask(x, y, Back[x][y]);
2482 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2483 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2487 void DynaExplode(int ex, int ey)
2490 int dynabomb_size = 1;
2491 boolean dynabomb_xl = FALSE;
2492 struct PlayerInfo *player;
2493 static int xy[4][2] =
2501 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2503 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2504 dynabomb_size = player->dynabomb_size;
2505 dynabomb_xl = player->dynabomb_xl;
2506 player->dynabombs_left++;
2509 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2513 for (j=1; j<=dynabomb_size; j++)
2515 int x = ex + j * xy[i % 4][0];
2516 int y = ey + j * xy[i % 4][1];
2519 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2522 element = Feld[x][y];
2524 /* do not restart explosions of fields with active bombs */
2525 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2528 Explode(x, y, EX_PHASE_START, EX_BORDER);
2530 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2531 if (element != EL_EMPTY &&
2532 element != EL_SAND &&
2533 element != EL_EXPLOSION &&
2540 void Bang(int x, int y)
2543 int element = MovingOrBlocked2Element(x, y);
2545 int element = Feld[x][y];
2549 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2551 if (IS_PLAYER(x, y))
2554 struct PlayerInfo *player = PLAYERINFO(x, y);
2556 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2557 player->element_nr);
2562 PlaySoundLevelAction(x, y, ACTION_EXPLODING);
2564 if (game.emulation == EMU_SUPAPLEX)
2565 PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
2567 PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
2572 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2580 case EL_BD_BUTTERFLY:
2583 case EL_DARK_YAMYAM:
2587 RaiseScoreElement(element);
2588 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2590 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2591 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2592 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2593 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2594 case EL_DYNABOMB_INCREASE_NUMBER:
2595 case EL_DYNABOMB_INCREASE_SIZE:
2596 case EL_DYNABOMB_INCREASE_POWER:
2601 case EL_LAMP_ACTIVE:
2602 if (IS_PLAYER(x, y))
2603 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2605 Explode(x, y, EX_PHASE_START, EX_CENTER);
2608 if (CAN_EXPLODE_1X1(element))
2609 Explode(x, y, EX_PHASE_START, EX_CENTER);
2611 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2615 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2618 void SplashAcid(int x, int y)
2620 int element = Feld[x][y];
2622 if (element != EL_ACID_SPLASH_LEFT &&
2623 element != EL_ACID_SPLASH_RIGHT)
2625 PlaySoundLevel(x, y, SND_ACID_SPLASHING);
2627 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2628 (!IN_LEV_FIELD(x-1, y-1) ||
2629 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2630 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2632 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2633 (!IN_LEV_FIELD(x+1, y-1) ||
2634 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2635 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2639 static void InitBeltMovement()
2641 static int belt_base_element[4] =
2643 EL_CONVEYOR_BELT_1_LEFT,
2644 EL_CONVEYOR_BELT_2_LEFT,
2645 EL_CONVEYOR_BELT_3_LEFT,
2646 EL_CONVEYOR_BELT_4_LEFT
2648 static int belt_base_active_element[4] =
2650 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2651 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2652 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2653 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2658 /* set frame order for belt animation graphic according to belt direction */
2665 int element = belt_base_active_element[belt_nr] + j;
2666 int graphic = el2img(element);
2668 if (game.belt_dir[i] == MV_LEFT)
2669 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2671 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2675 for(y=0; y<lev_fieldy; y++)
2677 for(x=0; x<lev_fieldx; x++)
2679 int element = Feld[x][y];
2683 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2685 int e_belt_nr = getBeltNrFromBeltElement(element);
2688 if (e_belt_nr == belt_nr)
2690 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2692 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2700 static void ToggleBeltSwitch(int x, int y)
2702 static int belt_base_element[4] =
2704 EL_CONVEYOR_BELT_1_LEFT,
2705 EL_CONVEYOR_BELT_2_LEFT,
2706 EL_CONVEYOR_BELT_3_LEFT,
2707 EL_CONVEYOR_BELT_4_LEFT
2709 static int belt_base_active_element[4] =
2711 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2712 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2713 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2714 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2716 static int belt_base_switch_element[4] =
2718 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2719 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2720 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2721 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2723 static int belt_move_dir[4] =
2731 int element = Feld[x][y];
2732 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2733 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2734 int belt_dir = belt_move_dir[belt_dir_nr];
2737 if (!IS_BELT_SWITCH(element))
2740 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2741 game.belt_dir[belt_nr] = belt_dir;
2743 if (belt_dir_nr == 3)
2746 /* set frame order for belt animation graphic according to belt direction */
2749 int element = belt_base_active_element[belt_nr] + i;
2750 int graphic = el2img(element);
2752 if (belt_dir == MV_LEFT)
2753 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2755 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2758 for (yy=0; yy<lev_fieldy; yy++)
2760 for (xx=0; xx<lev_fieldx; xx++)
2762 int element = Feld[xx][yy];
2764 if (IS_BELT_SWITCH(element))
2766 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2768 if (e_belt_nr == belt_nr)
2770 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2771 DrawLevelField(xx, yy);
2774 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2776 int e_belt_nr = getBeltNrFromBeltElement(element);
2778 if (e_belt_nr == belt_nr)
2780 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2782 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2783 DrawLevelField(xx, yy);
2786 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2788 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2790 if (e_belt_nr == belt_nr)
2792 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2794 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2795 DrawLevelField(xx, yy);
2802 static void ToggleSwitchgateSwitch(int x, int y)
2806 game.switchgate_pos = !game.switchgate_pos;
2808 for (yy=0; yy<lev_fieldy; yy++)
2810 for (xx=0; xx<lev_fieldx; xx++)
2812 int element = Feld[xx][yy];
2814 if (element == EL_SWITCHGATE_SWITCH_UP ||
2815 element == EL_SWITCHGATE_SWITCH_DOWN)
2817 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2818 DrawLevelField(xx, yy);
2820 else if (element == EL_SWITCHGATE_OPEN ||
2821 element == EL_SWITCHGATE_OPENING)
2823 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2825 PlaySoundLevelAction(xx, yy, ACTION_CLOSING);
2827 PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2830 else if (element == EL_SWITCHGATE_CLOSED ||
2831 element == EL_SWITCHGATE_CLOSING)
2833 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2835 PlaySoundLevelAction(xx, yy, ACTION_OPENING);
2837 PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2844 static int getInvisibleActiveFromInvisibleElement(int element)
2846 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2847 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2848 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2852 static int getInvisibleFromInvisibleActiveElement(int element)
2854 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2855 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2856 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2860 static void RedrawAllLightSwitchesAndInvisibleElements()
2864 for (y=0; y<lev_fieldy; y++)
2866 for (x=0; x<lev_fieldx; x++)
2868 int element = Feld[x][y];
2870 if (element == EL_LIGHT_SWITCH &&
2871 game.light_time_left > 0)
2873 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2874 DrawLevelField(x, y);
2876 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2877 game.light_time_left == 0)
2879 Feld[x][y] = EL_LIGHT_SWITCH;
2880 DrawLevelField(x, y);
2882 else if (element == EL_INVISIBLE_STEELWALL ||
2883 element == EL_INVISIBLE_WALL ||
2884 element == EL_INVISIBLE_SAND)
2886 if (game.light_time_left > 0)
2887 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2889 DrawLevelField(x, y);
2891 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2892 element == EL_INVISIBLE_WALL_ACTIVE ||
2893 element == EL_INVISIBLE_SAND_ACTIVE)
2895 if (game.light_time_left == 0)
2896 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2898 DrawLevelField(x, y);
2904 static void ToggleLightSwitch(int x, int y)
2906 int element = Feld[x][y];
2908 game.light_time_left =
2909 (element == EL_LIGHT_SWITCH ?
2910 level.time_light * FRAMES_PER_SECOND : 0);
2912 RedrawAllLightSwitchesAndInvisibleElements();
2915 static void ActivateTimegateSwitch(int x, int y)
2919 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2921 for (yy=0; yy<lev_fieldy; yy++)
2923 for (xx=0; xx<lev_fieldx; xx++)
2925 int element = Feld[xx][yy];
2927 if (element == EL_TIMEGATE_CLOSED ||
2928 element == EL_TIMEGATE_CLOSING)
2930 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2931 PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2935 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2937 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2938 DrawLevelField(xx, yy);
2945 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2948 inline static int getElementMoveStepsize(int x, int y)
2950 int element = Feld[x][y];
2951 int direction = MovDir[x][y];
2952 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2953 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2954 int horiz_move = (dx != 0);
2955 int sign = (horiz_move ? dx : dy);
2956 int step = sign * element_info[element].move_stepsize;
2958 /* special values for move stepsize for spring and things on conveyor belt */
2961 if (CAN_FALL(element) &&
2962 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2963 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2964 else if (element == EL_SPRING)
2965 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2971 void Impact(int x, int y)
2973 boolean lastline = (y == lev_fieldy-1);
2974 boolean object_hit = FALSE;
2975 boolean impact = (lastline || object_hit);
2976 int element = Feld[x][y];
2977 int smashed = EL_UNDEFINED;
2979 if (!lastline) /* check if element below was hit */
2981 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2984 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2985 MovDir[x][y + 1] != MV_DOWN ||
2986 MovPos[x][y + 1] <= TILEY / 2));
2988 /* do not smash moving elements that left the smashed field in time */
2989 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2990 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2994 smashed = MovingOrBlocked2Element(x, y + 1);
2996 impact = (lastline || object_hit);
2999 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3005 /* only reset graphic animation if graphic really changes after impact */
3007 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3009 ResetGfxAnimation(x, y);
3010 DrawLevelField(x, y);
3013 if (impact && CAN_EXPLODE_IMPACT(element))
3018 else if (impact && element == EL_PEARL)
3020 Feld[x][y] = EL_PEARL_BREAKING;
3021 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3024 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3026 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3031 if (impact && element == EL_AMOEBA_DROP)
3033 if (object_hit && IS_PLAYER(x, y + 1))
3034 KillHeroUnlessProtected(x, y + 1);
3035 else if (object_hit && smashed == EL_PENGUIN)
3039 Feld[x][y] = EL_AMOEBA_GROWING;
3040 Store[x][y] = EL_AMOEBA_WET;
3042 ResetRandomAnimationValue(x, y);
3047 if (object_hit) /* check which object was hit */
3049 if (CAN_PASS_MAGIC_WALL(element) &&
3050 (smashed == EL_MAGIC_WALL ||
3051 smashed == EL_BD_MAGIC_WALL))
3054 int activated_magic_wall =
3055 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3056 EL_BD_MAGIC_WALL_ACTIVE);
3058 /* activate magic wall / mill */
3059 for (yy=0; yy<lev_fieldy; yy++)
3060 for (xx=0; xx<lev_fieldx; xx++)
3061 if (Feld[xx][yy] == smashed)
3062 Feld[xx][yy] = activated_magic_wall;
3064 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3065 game.magic_wall_active = TRUE;
3067 PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
3068 SND_MAGIC_WALL_ACTIVATING :
3069 SND_BD_MAGIC_WALL_ACTIVATING));
3072 if (IS_PLAYER(x, y + 1))
3074 if (CAN_SMASH_PLAYER(element))
3076 KillHeroUnlessProtected(x, y + 1);
3080 else if (smashed == EL_PENGUIN)
3082 if (CAN_SMASH_PLAYER(element))
3088 else if (element == EL_BD_DIAMOND)
3090 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3096 else if ((element == EL_SP_INFOTRON ||
3097 element == EL_SP_ZONK) &&
3098 (smashed == EL_SP_SNIKSNAK ||
3099 smashed == EL_SP_ELECTRON ||
3100 smashed == EL_SP_DISK_ORANGE))
3106 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3112 else if (CAN_SMASH_EVERYTHING(element))
3114 if (IS_CLASSIC_ENEMY(smashed) ||
3115 CAN_EXPLODE_SMASHED(smashed))
3120 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3122 if (smashed == EL_LAMP ||
3123 smashed == EL_LAMP_ACTIVE)
3128 else if (smashed == EL_NUT)
3130 Feld[x][y + 1] = EL_NUT_BREAKING;
3131 PlaySoundLevel(x, y, SND_NUT_BREAKING);
3132 RaiseScoreElement(EL_NUT);
3135 else if (smashed == EL_PEARL)
3137 Feld[x][y + 1] = EL_PEARL_BREAKING;
3138 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3141 else if (smashed == EL_DIAMOND)
3143 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3144 PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
3147 else if (IS_BELT_SWITCH(smashed))
3149 ToggleBeltSwitch(x, y + 1);
3151 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3152 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3154 ToggleSwitchgateSwitch(x, y + 1);
3156 else if (smashed == EL_LIGHT_SWITCH ||
3157 smashed == EL_LIGHT_SWITCH_ACTIVE)
3159 ToggleLightSwitch(x, y + 1);
3163 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3165 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3166 CE_OTHER_IS_SWITCHING);
3167 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3173 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3178 /* play sound of magic wall / mill */
3180 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3181 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3183 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3184 PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
3185 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3186 PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
3191 /* play sound of object that hits the ground */
3192 if (lastline || object_hit)
3193 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3196 inline static void TurnRoundExt(int x, int y)
3208 { 0, 0 }, { 0, 0 }, { 0, 0 },
3213 int left, right, back;
3217 { MV_DOWN, MV_UP, MV_RIGHT },
3218 { MV_UP, MV_DOWN, MV_LEFT },
3220 { MV_LEFT, MV_RIGHT, MV_DOWN },
3224 { MV_RIGHT, MV_LEFT, MV_UP }
3227 int element = Feld[x][y];
3228 int old_move_dir = MovDir[x][y];
3229 int left_dir = turn[old_move_dir].left;
3230 int right_dir = turn[old_move_dir].right;
3231 int back_dir = turn[old_move_dir].back;
3233 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3234 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3235 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3236 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3238 int left_x = x + left_dx, left_y = y + left_dy;
3239 int right_x = x + right_dx, right_y = y + right_dy;
3240 int move_x = x + move_dx, move_y = y + move_dy;
3244 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3246 TestIfBadThingTouchesOtherBadThing(x, y);
3248 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3249 MovDir[x][y] = right_dir;
3250 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3251 MovDir[x][y] = left_dir;
3253 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3255 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3258 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3259 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3261 TestIfBadThingTouchesOtherBadThing(x, y);
3263 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3264 MovDir[x][y] = left_dir;
3265 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3266 MovDir[x][y] = right_dir;
3268 if ((element == EL_SPACESHIP ||
3269 element == EL_SP_SNIKSNAK ||
3270 element == EL_SP_ELECTRON)
3271 && MovDir[x][y] != old_move_dir)
3273 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3276 else if (element == EL_YAMYAM)
3278 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3279 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3281 if (can_turn_left && can_turn_right)
3282 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3283 else if (can_turn_left)
3284 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3285 else if (can_turn_right)
3286 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3288 MovDir[x][y] = back_dir;
3290 MovDelay[x][y] = 16 + 16 * RND(3);
3292 else if (element == EL_DARK_YAMYAM)
3294 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3295 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3297 if (can_turn_left && can_turn_right)
3298 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3299 else if (can_turn_left)
3300 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3301 else if (can_turn_right)
3302 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3304 MovDir[x][y] = back_dir;
3306 MovDelay[x][y] = 16 + 16 * RND(3);
3308 else if (element == EL_PACMAN)
3310 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3311 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3313 if (can_turn_left && can_turn_right)
3314 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3315 else if (can_turn_left)
3316 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3317 else if (can_turn_right)
3318 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3320 MovDir[x][y] = back_dir;
3322 MovDelay[x][y] = 6 + RND(40);
3324 else if (element == EL_PIG)
3326 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3327 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3328 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3329 boolean should_turn_left, should_turn_right, should_move_on;
3331 int rnd = RND(rnd_value);
3333 should_turn_left = (can_turn_left &&
3335 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3336 y + back_dy + left_dy)));
3337 should_turn_right = (can_turn_right &&
3339 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3340 y + back_dy + right_dy)));
3341 should_move_on = (can_move_on &&
3344 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3345 y + move_dy + left_dy) ||
3346 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3347 y + move_dy + right_dy)));
3349 if (should_turn_left || should_turn_right || should_move_on)
3351 if (should_turn_left && should_turn_right && should_move_on)
3352 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3353 rnd < 2 * rnd_value / 3 ? right_dir :
3355 else if (should_turn_left && should_turn_right)
3356 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3357 else if (should_turn_left && should_move_on)
3358 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3359 else if (should_turn_right && should_move_on)
3360 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3361 else if (should_turn_left)
3362 MovDir[x][y] = left_dir;
3363 else if (should_turn_right)
3364 MovDir[x][y] = right_dir;
3365 else if (should_move_on)
3366 MovDir[x][y] = old_move_dir;
3368 else if (can_move_on && rnd > rnd_value / 8)
3369 MovDir[x][y] = old_move_dir;
3370 else if (can_turn_left && can_turn_right)
3371 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3372 else if (can_turn_left && rnd > rnd_value / 8)
3373 MovDir[x][y] = left_dir;
3374 else if (can_turn_right && rnd > rnd_value/8)
3375 MovDir[x][y] = right_dir;
3377 MovDir[x][y] = back_dir;
3379 xx = x + move_xy[MovDir[x][y]].x;
3380 yy = y + move_xy[MovDir[x][y]].y;
3382 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3383 MovDir[x][y] = old_move_dir;
3387 else if (element == EL_DRAGON)
3389 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3390 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3391 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3393 int rnd = RND(rnd_value);
3396 if (FrameCounter < 1 && x == 0 && y == 29)
3397 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3400 if (can_move_on && rnd > rnd_value / 8)
3401 MovDir[x][y] = old_move_dir;
3402 else if (can_turn_left && can_turn_right)
3403 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3404 else if (can_turn_left && rnd > rnd_value / 8)
3405 MovDir[x][y] = left_dir;
3406 else if (can_turn_right && rnd > rnd_value / 8)
3407 MovDir[x][y] = right_dir;
3409 MovDir[x][y] = back_dir;
3411 xx = x + move_xy[MovDir[x][y]].x;
3412 yy = y + move_xy[MovDir[x][y]].y;
3415 if (FrameCounter < 1 && x == 0 && y == 29)
3416 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3417 xx, yy, Feld[xx][yy],
3422 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3423 MovDir[x][y] = old_move_dir;
3425 if (!IS_FREE(xx, yy))
3426 MovDir[x][y] = old_move_dir;
3430 if (FrameCounter < 1 && x == 0 && y == 29)
3431 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3436 else if (element == EL_MOLE)
3438 boolean can_move_on =
3439 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3440 IS_AMOEBOID(Feld[move_x][move_y]) ||
3441 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3444 boolean can_turn_left =
3445 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3446 IS_AMOEBOID(Feld[left_x][left_y])));
3448 boolean can_turn_right =
3449 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3450 IS_AMOEBOID(Feld[right_x][right_y])));
3452 if (can_turn_left && can_turn_right)
3453 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3454 else if (can_turn_left)
3455 MovDir[x][y] = left_dir;
3457 MovDir[x][y] = right_dir;
3460 if (MovDir[x][y] != old_move_dir)
3463 else if (element == EL_BALLOON)
3465 MovDir[x][y] = game.balloon_dir;
3468 else if (element == EL_SPRING)
3470 if (MovDir[x][y] & MV_HORIZONTAL &&
3471 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3472 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3473 MovDir[x][y] = MV_NO_MOVING;
3477 else if (element == EL_ROBOT ||
3478 element == EL_SATELLITE ||
3479 element == EL_PENGUIN)
3481 int attr_x = -1, attr_y = -1;
3492 for (i=0; i<MAX_PLAYERS; i++)
3494 struct PlayerInfo *player = &stored_player[i];
3495 int jx = player->jx, jy = player->jy;
3497 if (!player->active)
3501 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3509 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3515 if (element == EL_PENGUIN)
3518 static int xy[4][2] =
3528 int ex = x + xy[i % 4][0];
3529 int ey = y + xy[i % 4][1];
3531 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3540 MovDir[x][y] = MV_NO_MOVING;
3542 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3543 else if (attr_x > x)
3544 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3546 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3547 else if (attr_y > y)
3548 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3550 if (element == EL_ROBOT)
3554 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3555 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3556 Moving2Blocked(x, y, &newx, &newy);
3558 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3559 MovDelay[x][y] = 8 + 8 * !RND(3);
3561 MovDelay[x][y] = 16;
3563 else if (element == EL_PENGUIN)
3569 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3571 boolean first_horiz = RND(2);
3572 int new_move_dir = MovDir[x][y];
3575 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3576 Moving2Blocked(x, y, &newx, &newy);
3578 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3582 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3583 Moving2Blocked(x, y, &newx, &newy);
3585 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3588 MovDir[x][y] = old_move_dir;
3592 else /* (element == EL_SATELLITE) */
3598 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3600 boolean first_horiz = RND(2);
3601 int new_move_dir = MovDir[x][y];
3604 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3605 Moving2Blocked(x, y, &newx, &newy);
3607 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3611 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3612 Moving2Blocked(x, y, &newx, &newy);
3614 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3617 MovDir[x][y] = old_move_dir;
3622 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3623 element_info[element].move_pattern == MV_TURNING_LEFT ||
3624 element_info[element].move_pattern == MV_TURNING_RIGHT)
3626 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3627 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3629 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3630 MovDir[x][y] = left_dir;
3631 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3632 MovDir[x][y] = right_dir;
3633 else if (can_turn_left && can_turn_right)
3634 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3635 else if (can_turn_left)
3636 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3637 else if (can_turn_right)
3638 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3640 MovDir[x][y] = back_dir;
3642 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3644 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3645 element_info[element].move_pattern == MV_VERTICAL)
3647 if (element_info[element].move_pattern & old_move_dir)
3648 MovDir[x][y] = back_dir;
3649 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3650 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3651 else if (element_info[element].move_pattern == MV_VERTICAL)
3652 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3654 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3656 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3658 MovDir[x][y] = element_info[element].move_pattern;
3659 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3661 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3663 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3664 MovDir[x][y] = left_dir;
3665 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3666 MovDir[x][y] = right_dir;
3668 if (MovDir[x][y] != old_move_dir)
3669 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3671 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3673 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3674 MovDir[x][y] = right_dir;
3675 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3676 MovDir[x][y] = left_dir;
3678 if (MovDir[x][y] != old_move_dir)
3679 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3681 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3682 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3684 int attr_x = -1, attr_y = -1;
3687 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3698 for (i=0; i<MAX_PLAYERS; i++)
3700 struct PlayerInfo *player = &stored_player[i];
3701 int jx = player->jx, jy = player->jy;
3703 if (!player->active)
3707 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3715 MovDir[x][y] = MV_NO_MOVING;
3717 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3718 else if (attr_x > x)
3719 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3721 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3722 else if (attr_y > y)
3723 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3725 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3727 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3729 boolean first_horiz = RND(2);
3730 int new_move_dir = MovDir[x][y];
3733 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3734 Moving2Blocked(x, y, &newx, &newy);
3736 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3740 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3741 Moving2Blocked(x, y, &newx, &newy);
3743 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3746 MovDir[x][y] = old_move_dir;
3749 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3751 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3752 MovDir[x][y] = MV_NO_MOVING;
3758 static void TurnRound(int x, int y)
3760 int direction = MovDir[x][y];
3763 GfxDir[x][y] = MovDir[x][y];
3769 GfxDir[x][y] = MovDir[x][y];
3772 if (direction != MovDir[x][y])
3777 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3780 GfxAction[x][y] = ACTION_WAITING;
3784 static boolean JustBeingPushed(int x, int y)
3788 for (i=0; i<MAX_PLAYERS; i++)
3790 struct PlayerInfo *player = &stored_player[i];
3792 if (player->active && player->is_pushing && player->MovPos)
3794 int next_jx = player->jx + (player->jx - player->last_jx);
3795 int next_jy = player->jy + (player->jy - player->last_jy);
3797 if (x == next_jx && y == next_jy)
3805 void StartMoving(int x, int y)
3807 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3808 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3809 int element = Feld[x][y];
3815 if (MovDelay[x][y] == 0)
3816 GfxAction[x][y] = ACTION_DEFAULT;
3818 /* !!! this should be handled more generic (not only for mole) !!! */
3819 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3820 GfxAction[x][y] = ACTION_DEFAULT;
3823 if (CAN_FALL(element) && y < lev_fieldy - 1)
3825 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3826 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3827 if (JustBeingPushed(x, y))
3830 if (element == EL_QUICKSAND_FULL)
3832 if (IS_FREE(x, y + 1))
3834 InitMovingField(x, y, MV_DOWN);
3835 started_moving = TRUE;
3837 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3838 Store[x][y] = EL_ROCK;
3840 PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3842 PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3845 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3847 if (!MovDelay[x][y])
3848 MovDelay[x][y] = TILEY + 1;
3857 Feld[x][y] = EL_QUICKSAND_EMPTY;
3858 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3859 Store[x][y + 1] = Store[x][y];
3862 PlaySoundLevelAction(x, y, ACTION_FILLING);
3864 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3868 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3869 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3871 InitMovingField(x, y, MV_DOWN);
3872 started_moving = TRUE;
3874 Feld[x][y] = EL_QUICKSAND_FILLING;
3875 Store[x][y] = element;
3877 PlaySoundLevelAction(x, y, ACTION_FILLING);
3879 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3882 else if (element == EL_MAGIC_WALL_FULL)
3884 if (IS_FREE(x, y + 1))
3886 InitMovingField(x, y, MV_DOWN);
3887 started_moving = TRUE;
3889 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3890 Store[x][y] = EL_CHANGED(Store[x][y]);
3892 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3894 if (!MovDelay[x][y])
3895 MovDelay[x][y] = TILEY/4 + 1;
3904 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3905 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3906 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3910 else if (element == EL_BD_MAGIC_WALL_FULL)
3912 if (IS_FREE(x, y + 1))
3914 InitMovingField(x, y, MV_DOWN);
3915 started_moving = TRUE;
3917 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3918 Store[x][y] = EL_CHANGED2(Store[x][y]);
3920 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3922 if (!MovDelay[x][y])
3923 MovDelay[x][y] = TILEY/4 + 1;
3932 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3933 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3934 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3938 else if (CAN_PASS_MAGIC_WALL(element) &&
3939 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3940 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3942 InitMovingField(x, y, MV_DOWN);
3943 started_moving = TRUE;
3946 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3947 EL_BD_MAGIC_WALL_FILLING);
3948 Store[x][y] = element;
3951 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3953 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3958 InitMovingField(x, y, MV_DOWN);
3959 started_moving = TRUE;
3961 Store[x][y] = EL_ACID;
3963 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3964 GfxAction[x][y + 1] = ACTION_ACTIVE;
3968 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3969 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3970 (Feld[x][y + 1] == EL_BLOCKED)) ||
3971 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3972 CAN_SMASH(element) && WasJustFalling[x][y] &&
3973 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3977 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3978 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3979 WasJustMoving[x][y] && !Pushed[x][y + 1])
3981 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3982 WasJustMoving[x][y])
3987 /* this is needed for a special case not covered by calling "Impact()"
3988 from "ContinueMoving()": if an element moves to a tile directly below
3989 another element which was just falling on that tile (which was empty
3990 in the previous frame), the falling element above would just stop
3991 instead of smashing the element below (in previous version, the above
3992 element was just checked for "moving" instead of "falling", resulting
3993 in incorrect smashes caused by horizontal movement of the above
3994 element; also, the case of the player being the element to smash was
3995 simply not covered here... :-/ ) */
3999 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4001 if (MovDir[x][y] == MV_NO_MOVING)
4003 InitMovingField(x, y, MV_DOWN);
4004 started_moving = TRUE;
4007 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4009 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4010 MovDir[x][y] = MV_DOWN;
4012 InitMovingField(x, y, MV_DOWN);
4013 started_moving = TRUE;
4015 else if (element == EL_AMOEBA_DROP)
4017 Feld[x][y] = EL_AMOEBA_GROWING;
4018 Store[x][y] = EL_AMOEBA_WET;
4020 /* Store[x][y + 1] must be zero, because:
4021 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4024 #if OLD_GAME_BEHAVIOUR
4025 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4027 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4028 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4029 element != EL_DX_SUPABOMB)
4032 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4033 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4034 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4035 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4038 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4039 (IS_FREE(x - 1, y + 1) ||
4040 Feld[x - 1][y + 1] == EL_ACID));
4041 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4042 (IS_FREE(x + 1, y + 1) ||
4043 Feld[x + 1][y + 1] == EL_ACID));
4044 boolean can_fall_any = (can_fall_left || can_fall_right);
4045 boolean can_fall_both = (can_fall_left && can_fall_right);
4047 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4049 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4051 if (slippery_type == SLIPPERY_ONLY_LEFT)
4052 can_fall_right = FALSE;
4053 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4054 can_fall_left = FALSE;
4055 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4056 can_fall_right = FALSE;
4057 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4058 can_fall_left = FALSE;
4060 can_fall_any = (can_fall_left || can_fall_right);
4061 can_fall_both = (can_fall_left && can_fall_right);
4066 if (can_fall_both &&
4067 (game.emulation != EMU_BOULDERDASH &&
4068 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4069 can_fall_left = !(can_fall_right = RND(2));
4071 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4072 started_moving = TRUE;
4075 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4077 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4078 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4079 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4080 int belt_dir = game.belt_dir[belt_nr];
4082 if ((belt_dir == MV_LEFT && left_is_free) ||
4083 (belt_dir == MV_RIGHT && right_is_free))
4085 InitMovingField(x, y, belt_dir);
4086 started_moving = TRUE;
4088 GfxAction[x][y] = ACTION_DEFAULT;
4093 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4094 if (CAN_MOVE(element) && !started_moving)
4099 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4102 if ((element == EL_SATELLITE ||
4103 element == EL_BALLOON ||
4104 element == EL_SPRING)
4105 && JustBeingPushed(x, y))
4111 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4112 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4114 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4116 Moving2Blocked(x, y, &newx, &newy);
4117 if (Feld[newx][newy] == EL_BLOCKED)
4118 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4124 if (FrameCounter < 1 && x == 0 && y == 29)
4125 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4128 if (!MovDelay[x][y]) /* start new movement phase */
4130 /* all objects that can change their move direction after each step
4131 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4133 if (element != EL_YAMYAM &&
4134 element != EL_DARK_YAMYAM &&
4135 element != EL_PACMAN &&
4136 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4137 element_info[element].move_pattern != MV_TURNING_LEFT &&
4138 element_info[element].move_pattern != MV_TURNING_RIGHT)
4143 if (FrameCounter < 1 && x == 0 && y == 29)
4144 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4147 if (MovDelay[x][y] && (element == EL_BUG ||
4148 element == EL_SPACESHIP ||
4149 element == EL_SP_SNIKSNAK ||
4150 element == EL_SP_ELECTRON ||
4151 element == EL_MOLE))
4152 DrawLevelField(x, y);
4156 if (MovDelay[x][y]) /* wait some time before next movement */
4161 if (element == EL_YAMYAM)
4164 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4165 DrawLevelElementAnimation(x, y, element);
4169 if (MovDelay[x][y]) /* element still has to wait some time */
4172 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4173 ResetGfxAnimation(x, y);
4177 if (GfxAction[x][y] != ACTION_WAITING)
4178 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4180 GfxAction[x][y] = ACTION_WAITING;
4184 if (element == EL_ROBOT ||
4186 element == EL_PACMAN ||
4188 element == EL_YAMYAM ||
4189 element == EL_DARK_YAMYAM)
4192 DrawLevelElementAnimation(x, y, element);
4194 DrawLevelElementAnimationIfNeeded(x, y, element);
4196 PlaySoundLevelAction(x, y, ACTION_WAITING);
4198 else if (element == EL_SP_ELECTRON)
4199 DrawLevelElementAnimationIfNeeded(x, y, element);
4200 else if (element == EL_DRAGON)
4203 int dir = MovDir[x][y];
4204 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4205 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4206 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4207 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4208 dir == MV_UP ? IMG_FLAMES_1_UP :
4209 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4210 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4213 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4216 GfxAction[x][y] = ACTION_ATTACKING;
4218 if (IS_PLAYER(x, y))
4219 DrawPlayerField(x, y);
4221 DrawLevelField(x, y);
4223 PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING);
4225 for (i=1; i <= 3; i++)
4227 int xx = x + i * dx;
4228 int yy = y + i * dy;
4229 int sx = SCREENX(xx);
4230 int sy = SCREENY(yy);
4231 int flame_graphic = graphic + (i - 1);
4233 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4238 int flamed = MovingOrBlocked2Element(xx, yy);
4240 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4243 RemoveMovingField(xx, yy);
4245 Feld[xx][yy] = EL_FLAMES;
4246 if (IN_SCR_FIELD(sx, sy))
4248 DrawLevelFieldCrumbledSand(xx, yy);
4249 DrawGraphic(sx, sy, flame_graphic, frame);
4254 if (Feld[xx][yy] == EL_FLAMES)
4255 Feld[xx][yy] = EL_EMPTY;
4256 DrawLevelField(xx, yy);
4261 if (MovDelay[x][y]) /* element still has to wait some time */
4263 PlaySoundLevelAction(x, y, ACTION_WAITING);
4269 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4270 for all other elements GfxAction will be set by InitMovingField() */
4271 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4272 GfxAction[x][y] = ACTION_MOVING;
4276 /* now make next step */
4278 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4280 if (DONT_COLLIDE_WITH(element) &&
4281 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4282 !PLAYER_PROTECTED(newx, newy))
4285 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4288 /* player killed by element which is deadly when colliding with */
4290 KillHero(PLAYERINFO(newx, newy));
4295 else if ((element == EL_PENGUIN ||
4296 element == EL_ROBOT ||
4297 element == EL_SATELLITE ||
4298 element == EL_BALLOON ||
4299 IS_CUSTOM_ELEMENT(element)) &&
4300 IN_LEV_FIELD(newx, newy) &&
4301 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4304 Store[x][y] = EL_ACID;
4306 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4308 if (Feld[newx][newy] == EL_EXIT_OPEN)
4310 Feld[x][y] = EL_EMPTY;
4311 DrawLevelField(x, y);
4313 PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4314 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4315 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4317 local_player->friends_still_needed--;
4318 if (!local_player->friends_still_needed &&
4319 !local_player->GameOver && AllPlayersGone)
4320 local_player->LevelSolved = local_player->GameOver = TRUE;
4324 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4326 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4327 DrawLevelField(newx, newy);
4329 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4331 else if (!IS_FREE(newx, newy))
4333 GfxAction[x][y] = ACTION_WAITING;
4335 if (IS_PLAYER(x, y))
4336 DrawPlayerField(x, y);
4338 DrawLevelField(x, y);
4342 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4344 if (IS_FOOD_PIG(Feld[newx][newy]))
4346 if (IS_MOVING(newx, newy))
4347 RemoveMovingField(newx, newy);
4350 Feld[newx][newy] = EL_EMPTY;
4351 DrawLevelField(newx, newy);
4354 PlaySoundLevel(x, y, SND_PIG_DIGGING);
4356 else if (!IS_FREE(newx, newy))
4358 if (IS_PLAYER(x, y))
4359 DrawPlayerField(x, y);
4361 DrawLevelField(x, y);
4365 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4367 if (!IS_FREE(newx, newy))
4369 if (IS_PLAYER(x, y))
4370 DrawPlayerField(x, y);
4372 DrawLevelField(x, y);
4378 boolean wanna_flame = !RND(10);
4379 int dx = newx - x, dy = newy - y;
4380 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4381 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4382 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4383 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4384 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4385 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4388 IS_CLASSIC_ENEMY(element1) ||
4389 IS_CLASSIC_ENEMY(element2)) &&
4390 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4391 element1 != EL_FLAMES && element2 != EL_FLAMES)
4394 ResetGfxAnimation(x, y);
4395 GfxAction[x][y] = ACTION_ATTACKING;
4398 if (IS_PLAYER(x, y))
4399 DrawPlayerField(x, y);
4401 DrawLevelField(x, y);
4403 PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4405 MovDelay[x][y] = 50;
4407 Feld[newx][newy] = EL_FLAMES;
4408 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4409 Feld[newx1][newy1] = EL_FLAMES;
4410 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4411 Feld[newx2][newy2] = EL_FLAMES;
4417 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4418 Feld[newx][newy] == EL_DIAMOND)
4420 if (IS_MOVING(newx, newy))
4421 RemoveMovingField(newx, newy);
4424 Feld[newx][newy] = EL_EMPTY;
4425 DrawLevelField(newx, newy);
4428 PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4430 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4431 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4433 if (AmoebaNr[newx][newy])
4435 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4436 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4437 Feld[newx][newy] == EL_BD_AMOEBA)
4438 AmoebaCnt[AmoebaNr[newx][newy]]--;
4441 if (IS_MOVING(newx, newy))
4442 RemoveMovingField(newx, newy);
4445 Feld[newx][newy] = EL_EMPTY;
4446 DrawLevelField(newx, newy);
4449 PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4451 else if ((element == EL_PACMAN || element == EL_MOLE)
4452 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4454 if (AmoebaNr[newx][newy])
4456 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4457 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4458 Feld[newx][newy] == EL_BD_AMOEBA)
4459 AmoebaCnt[AmoebaNr[newx][newy]]--;
4462 if (element == EL_MOLE)
4464 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4465 PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4467 ResetGfxAnimation(x, y);
4468 GfxAction[x][y] = ACTION_DIGGING;
4469 DrawLevelField(x, y);
4471 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4472 return; /* wait for shrinking amoeba */
4474 else /* element == EL_PACMAN */
4476 Feld[newx][newy] = EL_EMPTY;
4477 DrawLevelField(newx, newy);
4478 PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4481 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4482 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4483 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4485 /* wait for shrinking amoeba to completely disappear */
4488 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4490 /* object was running against a wall */
4495 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4496 DrawLevelElementAnimation(x, y, element);
4498 if (element == EL_BUG ||
4499 element == EL_SPACESHIP ||
4500 element == EL_SP_SNIKSNAK)
4501 DrawLevelField(x, y);
4502 else if (element == EL_MOLE)
4503 DrawLevelField(x, y);
4504 else if (element == EL_BD_BUTTERFLY ||
4505 element == EL_BD_FIREFLY)
4506 DrawLevelElementAnimationIfNeeded(x, y, element);
4507 else if (element == EL_SATELLITE)
4508 DrawLevelElementAnimationIfNeeded(x, y, element);
4509 else if (element == EL_SP_ELECTRON)
4510 DrawLevelElementAnimationIfNeeded(x, y, element);
4513 if (DONT_TOUCH(element))
4514 TestIfBadThingTouchesHero(x, y);
4517 PlaySoundLevelAction(x, y, ACTION_WAITING);
4523 InitMovingField(x, y, MovDir[x][y]);
4525 PlaySoundLevelAction(x, y, ACTION_MOVING);
4529 ContinueMoving(x, y);
4532 void ContinueMoving(int x, int y)
4534 int element = Feld[x][y];
4535 int direction = MovDir[x][y];
4536 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4537 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4538 int newx = x + dx, newy = y + dy;
4539 int nextx = newx + dx, nexty = newy + dy;
4540 boolean pushed = Pushed[x][y];
4542 MovPos[x][y] += getElementMoveStepsize(x, y);
4544 if (pushed) /* special case: moving object pushed by player */
4545 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4547 if (ABS(MovPos[x][y]) < TILEX)
4549 DrawLevelField(x, y);
4551 return; /* element is still moving */
4554 /* element reached destination field */
4556 Feld[x][y] = EL_EMPTY;
4557 Feld[newx][newy] = element;
4558 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4560 if (element == EL_MOLE)
4562 Feld[x][y] = EL_SAND;
4564 DrawLevelFieldCrumbledSandNeighbours(x, y);
4566 else if (element == EL_QUICKSAND_FILLING)
4568 element = Feld[newx][newy] = get_next_element(element);
4569 Store[newx][newy] = Store[x][y];
4571 else if (element == EL_QUICKSAND_EMPTYING)
4573 Feld[x][y] = get_next_element(element);
4574 element = Feld[newx][newy] = Store[x][y];
4576 else if (element == EL_MAGIC_WALL_FILLING)
4578 element = Feld[newx][newy] = get_next_element(element);
4579 if (!game.magic_wall_active)
4580 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4581 Store[newx][newy] = Store[x][y];
4583 else if (element == EL_MAGIC_WALL_EMPTYING)
4585 Feld[x][y] = get_next_element(element);
4586 if (!game.magic_wall_active)
4587 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4588 element = Feld[newx][newy] = Store[x][y];
4590 else if (element == EL_BD_MAGIC_WALL_FILLING)
4592 element = Feld[newx][newy] = get_next_element(element);
4593 if (!game.magic_wall_active)
4594 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4595 Store[newx][newy] = Store[x][y];
4597 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4599 Feld[x][y] = get_next_element(element);
4600 if (!game.magic_wall_active)
4601 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4602 element = Feld[newx][newy] = Store[x][y];
4604 else if (element == EL_AMOEBA_DROPPING)
4606 Feld[x][y] = get_next_element(element);
4607 element = Feld[newx][newy] = Store[x][y];
4609 else if (element == EL_SOKOBAN_OBJECT)
4612 Feld[x][y] = Back[x][y];
4614 if (Back[newx][newy])
4615 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4617 Back[x][y] = Back[newx][newy] = 0;
4619 else if (Store[x][y] == EL_ACID)
4621 element = Feld[newx][newy] = EL_ACID;
4625 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4626 MovDelay[newx][newy] = 0;
4628 /* copy element change control values to new field */
4629 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4630 ChangePage[newx][newy] = ChangePage[x][y];
4631 Changed[newx][newy] = Changed[x][y];
4632 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4634 ChangeDelay[x][y] = 0;
4635 ChangePage[x][y] = -1;
4636 Changed[x][y] = CE_BITMASK_DEFAULT;
4637 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4639 /* copy animation control values to new field */
4640 GfxFrame[newx][newy] = GfxFrame[x][y];
4641 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4642 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4643 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4645 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4647 ResetGfxAnimation(x, y); /* reset animation values for old field */
4650 /* 2.1.1 (does not work correctly for spring) */
4651 if (!CAN_MOVE(element))
4652 MovDir[newx][newy] = 0;
4656 /* (does not work for falling objects that slide horizontally) */
4657 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4658 MovDir[newx][newy] = 0;
4661 if (!CAN_MOVE(element) ||
4662 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4663 MovDir[newx][newy] = 0;
4666 if (!CAN_MOVE(element) ||
4667 (CAN_FALL(element) && direction == MV_DOWN))
4668 GfxDir[x][y] = MovDir[newx][newy] = 0;
4673 DrawLevelField(x, y);
4674 DrawLevelField(newx, newy);
4676 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4678 /* prevent pushed element from moving on in pushed direction */
4679 if (pushed && CAN_MOVE(element) &&
4680 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4681 !(element_info[element].move_pattern & direction))
4682 TurnRound(newx, newy);
4684 if (!pushed) /* special case: moving object pushed by player */
4686 WasJustMoving[newx][newy] = 3;
4688 if (CAN_FALL(element) && direction == MV_DOWN)
4689 WasJustFalling[newx][newy] = 3;
4692 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4694 TestIfBadThingTouchesHero(newx, newy);
4695 TestIfBadThingTouchesFriend(newx, newy);
4697 if (!IS_CUSTOM_ELEMENT(element))
4698 TestIfBadThingTouchesOtherBadThing(newx, newy);
4700 else if (element == EL_PENGUIN)
4701 TestIfFriendTouchesBadThing(newx, newy);
4703 if (CAN_FALL(element) && direction == MV_DOWN &&
4704 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4708 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4712 if (ChangePage[newx][newy] != -1) /* delayed change */
4713 ChangeElement(newx, newy, ChangePage[newx][newy]);
4716 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4717 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4720 TestIfPlayerTouchesCustomElement(newx, newy);
4721 TestIfElementTouchesCustomElement(newx, newy);
4724 int AmoebeNachbarNr(int ax, int ay)
4727 int element = Feld[ax][ay];
4729 static int xy[4][2] =
4739 int x = ax + xy[i][0];
4740 int y = ay + xy[i][1];
4742 if (!IN_LEV_FIELD(x, y))
4745 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4746 group_nr = AmoebaNr[x][y];
4752 void AmoebenVereinigen(int ax, int ay)
4754 int i, x, y, xx, yy;
4755 int new_group_nr = AmoebaNr[ax][ay];
4756 static int xy[4][2] =
4764 if (new_group_nr == 0)
4772 if (!IN_LEV_FIELD(x, y))
4775 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4776 Feld[x][y] == EL_BD_AMOEBA ||
4777 Feld[x][y] == EL_AMOEBA_DEAD) &&
4778 AmoebaNr[x][y] != new_group_nr)
4780 int old_group_nr = AmoebaNr[x][y];
4782 if (old_group_nr == 0)
4785 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4786 AmoebaCnt[old_group_nr] = 0;
4787 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4788 AmoebaCnt2[old_group_nr] = 0;
4790 for (yy=0; yy<lev_fieldy; yy++)
4792 for (xx=0; xx<lev_fieldx; xx++)
4794 if (AmoebaNr[xx][yy] == old_group_nr)
4795 AmoebaNr[xx][yy] = new_group_nr;
4802 void AmoebeUmwandeln(int ax, int ay)
4806 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4808 int group_nr = AmoebaNr[ax][ay];
4813 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4814 printf("AmoebeUmwandeln(): This should never happen!\n");
4819 for (y=0; y<lev_fieldy; y++)
4821 for (x=0; x<lev_fieldx; x++)
4823 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4826 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4830 PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4831 SND_AMOEBA_TURNING_TO_GEM :
4832 SND_AMOEBA_TURNING_TO_ROCK));
4837 static int xy[4][2] =
4850 if (!IN_LEV_FIELD(x, y))
4853 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4855 PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4856 SND_AMOEBA_TURNING_TO_GEM :
4857 SND_AMOEBA_TURNING_TO_ROCK));
4864 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4867 int group_nr = AmoebaNr[ax][ay];
4868 boolean done = FALSE;
4873 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4874 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4879 for (y=0; y<lev_fieldy; y++)
4881 for (x=0; x<lev_fieldx; x++)
4883 if (AmoebaNr[x][y] == group_nr &&
4884 (Feld[x][y] == EL_AMOEBA_DEAD ||
4885 Feld[x][y] == EL_BD_AMOEBA ||
4886 Feld[x][y] == EL_AMOEBA_GROWING))
4889 Feld[x][y] = new_element;
4890 InitField(x, y, FALSE);
4891 DrawLevelField(x, y);
4898 PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4899 SND_BD_AMOEBA_TURNING_TO_ROCK :
4900 SND_BD_AMOEBA_TURNING_TO_GEM));
4903 void AmoebeWaechst(int x, int y)
4905 static unsigned long sound_delay = 0;
4906 static unsigned long sound_delay_value = 0;
4908 if (!MovDelay[x][y]) /* start new growing cycle */
4912 if (DelayReached(&sound_delay, sound_delay_value))
4915 PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4917 if (Store[x][y] == EL_BD_AMOEBA)
4918 PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4920 PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4922 sound_delay_value = 30;
4926 if (MovDelay[x][y]) /* wait some time before growing bigger */
4929 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4931 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4932 6 - MovDelay[x][y]);
4934 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4937 if (!MovDelay[x][y])
4939 Feld[x][y] = Store[x][y];
4941 DrawLevelField(x, y);
4946 void AmoebaDisappearing(int x, int y)
4948 static unsigned long sound_delay = 0;
4949 static unsigned long sound_delay_value = 0;
4951 if (!MovDelay[x][y]) /* start new shrinking cycle */
4955 if (DelayReached(&sound_delay, sound_delay_value))
4956 sound_delay_value = 30;
4959 if (MovDelay[x][y]) /* wait some time before shrinking */
4962 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4964 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4965 6 - MovDelay[x][y]);
4967 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4970 if (!MovDelay[x][y])
4972 Feld[x][y] = EL_EMPTY;
4973 DrawLevelField(x, y);
4975 /* don't let mole enter this field in this cycle;
4976 (give priority to objects falling to this field from above) */
4982 void AmoebeAbleger(int ax, int ay)
4985 int element = Feld[ax][ay];
4986 int graphic = el2img(element);
4987 int newax = ax, neway = ay;
4988 static int xy[4][2] =
4996 if (!level.amoeba_speed)
4998 Feld[ax][ay] = EL_AMOEBA_DEAD;
4999 DrawLevelField(ax, ay);
5003 if (IS_ANIMATED(graphic))
5004 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5006 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5007 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5009 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5012 if (MovDelay[ax][ay])
5016 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5019 int x = ax + xy[start][0];
5020 int y = ay + xy[start][1];
5022 if (!IN_LEV_FIELD(x, y))
5025 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5026 if (IS_FREE(x, y) ||
5027 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5033 if (newax == ax && neway == ay)
5036 else /* normal or "filled" (BD style) amoeba */
5039 boolean waiting_for_player = FALSE;
5043 int j = (start + i) % 4;
5044 int x = ax + xy[j][0];
5045 int y = ay + xy[j][1];
5047 if (!IN_LEV_FIELD(x, y))
5050 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5051 if (IS_FREE(x, y) ||
5052 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5058 else if (IS_PLAYER(x, y))
5059 waiting_for_player = TRUE;
5062 if (newax == ax && neway == ay) /* amoeba cannot grow */
5064 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5066 Feld[ax][ay] = EL_AMOEBA_DEAD;
5067 DrawLevelField(ax, ay);
5068 AmoebaCnt[AmoebaNr[ax][ay]]--;
5070 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5072 if (element == EL_AMOEBA_FULL)
5073 AmoebeUmwandeln(ax, ay);
5074 else if (element == EL_BD_AMOEBA)
5075 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5080 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5082 /* amoeba gets larger by growing in some direction */
5084 int new_group_nr = AmoebaNr[ax][ay];
5087 if (new_group_nr == 0)
5089 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5090 printf("AmoebeAbleger(): This should never happen!\n");
5095 AmoebaNr[newax][neway] = new_group_nr;
5096 AmoebaCnt[new_group_nr]++;
5097 AmoebaCnt2[new_group_nr]++;
5099 /* if amoeba touches other amoeba(s) after growing, unify them */
5100 AmoebenVereinigen(newax, neway);
5102 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5104 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5110 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5111 (neway == lev_fieldy - 1 && newax != ax))
5113 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5114 Store[newax][neway] = element;
5116 else if (neway == ay)
5118 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5120 PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5122 PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5127 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5128 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5129 Store[ax][ay] = EL_AMOEBA_DROP;
5130 ContinueMoving(ax, ay);
5134 DrawLevelField(newax, neway);
5137 void Life(int ax, int ay)
5140 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5142 int element = Feld[ax][ay];
5143 int graphic = el2img(element);
5144 boolean changed = FALSE;
5146 if (IS_ANIMATED(graphic))
5147 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5152 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5153 MovDelay[ax][ay] = life_time;
5155 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5158 if (MovDelay[ax][ay])
5162 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5164 int xx = ax+x1, yy = ay+y1;
5167 if (!IN_LEV_FIELD(xx, yy))
5170 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5172 int x = xx+x2, y = yy+y2;
5174 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5177 if (((Feld[x][y] == element ||
5178 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5180 (IS_FREE(x, y) && Stop[x][y]))
5184 if (xx == ax && yy == ay) /* field in the middle */
5186 if (nachbarn < life[0] || nachbarn > life[1])
5188 Feld[xx][yy] = EL_EMPTY;
5190 DrawLevelField(xx, yy);
5191 Stop[xx][yy] = TRUE;
5195 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5196 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5197 { /* free border field */
5198 if (nachbarn >= life[2] && nachbarn <= life[3])
5200 Feld[xx][yy] = element;
5201 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5203 DrawLevelField(xx, yy);
5204 Stop[xx][yy] = TRUE;
5211 PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5212 SND_GAME_OF_LIFE_GROWING);
5215 static void InitRobotWheel(int x, int y)
5217 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5220 static void RunRobotWheel(int x, int y)
5222 PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5225 static void StopRobotWheel(int x, int y)
5227 if (ZX == x && ZY == y)
5231 static void InitTimegateWheel(int x, int y)
5233 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5236 static void RunTimegateWheel(int x, int y)
5238 PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5241 void CheckExit(int x, int y)
5243 if (local_player->gems_still_needed > 0 ||
5244 local_player->sokobanfields_still_needed > 0 ||
5245 local_player->lights_still_needed > 0)
5247 int element = Feld[x][y];
5248 int graphic = el2img(element);
5250 if (IS_ANIMATED(graphic))
5251 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5256 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5259 Feld[x][y] = EL_EXIT_OPENING;
5261 PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5264 void CheckExitSP(int x, int y)
5266 if (local_player->gems_still_needed > 0)
5268 int element = Feld[x][y];
5269 int graphic = el2img(element);
5271 if (IS_ANIMATED(graphic))
5272 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5277 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5280 Feld[x][y] = EL_SP_EXIT_OPENING;
5282 PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5285 static void CloseAllOpenTimegates()
5289 for (y=0; y<lev_fieldy; y++)
5291 for (x=0; x<lev_fieldx; x++)
5293 int element = Feld[x][y];
5295 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5297 Feld[x][y] = EL_TIMEGATE_CLOSING;
5299 PlaySoundLevelAction(x, y, ACTION_CLOSING);
5301 PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5308 void EdelsteinFunkeln(int x, int y)
5310 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5313 if (Feld[x][y] == EL_BD_DIAMOND)
5316 if (MovDelay[x][y] == 0) /* next animation frame */
5317 MovDelay[x][y] = 11 * !SimpleRND(500);
5319 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5323 if (setup.direct_draw && MovDelay[x][y])
5324 SetDrawtoField(DRAW_BUFFERED);
5326 DrawLevelElementAnimation(x, y, Feld[x][y]);
5328 if (MovDelay[x][y] != 0)
5330 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5331 10 - MovDelay[x][y]);
5333 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5335 if (setup.direct_draw)
5339 dest_x = FX + SCREENX(x) * TILEX;
5340 dest_y = FY + SCREENY(y) * TILEY;
5342 BlitBitmap(drawto_field, window,
5343 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5344 SetDrawtoField(DRAW_DIRECT);
5350 void MauerWaechst(int x, int y)
5354 if (!MovDelay[x][y]) /* next animation frame */
5355 MovDelay[x][y] = 3 * delay;
5357 if (MovDelay[x][y]) /* wait some time before next frame */
5361 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5363 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5364 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5366 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5369 if (!MovDelay[x][y])
5371 if (MovDir[x][y] == MV_LEFT)
5373 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5374 DrawLevelField(x - 1, y);
5376 else if (MovDir[x][y] == MV_RIGHT)
5378 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5379 DrawLevelField(x + 1, y);
5381 else if (MovDir[x][y] == MV_UP)
5383 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5384 DrawLevelField(x, y - 1);
5388 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5389 DrawLevelField(x, y + 1);
5392 Feld[x][y] = Store[x][y];
5394 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5395 DrawLevelField(x, y);
5400 void MauerAbleger(int ax, int ay)
5402 int element = Feld[ax][ay];
5403 int graphic = el2img(element);
5404 boolean oben_frei = FALSE, unten_frei = FALSE;
5405 boolean links_frei = FALSE, rechts_frei = FALSE;
5406 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5407 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5408 boolean new_wall = FALSE;
5410 if (IS_ANIMATED(graphic))
5411 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5413 if (!MovDelay[ax][ay]) /* start building new wall */
5414 MovDelay[ax][ay] = 6;
5416 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5419 if (MovDelay[ax][ay])
5423 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5425 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5427 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5429 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5432 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5433 element == EL_EXPANDABLE_WALL_ANY)
5437 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5438 Store[ax][ay-1] = element;
5439 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5440 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5441 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5442 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5447 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5448 Store[ax][ay+1] = element;
5449 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5450 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5451 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5452 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5457 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5458 element == EL_EXPANDABLE_WALL_ANY ||
5459 element == EL_EXPANDABLE_WALL)
5463 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5464 Store[ax-1][ay] = element;
5465 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5466 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5467 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5468 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5474 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5475 Store[ax+1][ay] = element;
5476 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5477 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5478 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5479 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5484 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5485 DrawLevelField(ax, ay);
5487 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5489 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5490 unten_massiv = TRUE;
5491 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5492 links_massiv = TRUE;
5493 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5494 rechts_massiv = TRUE;
5496 if (((oben_massiv && unten_massiv) ||
5497 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5498 element == EL_EXPANDABLE_WALL) &&
5499 ((links_massiv && rechts_massiv) ||
5500 element == EL_EXPANDABLE_WALL_VERTICAL))
5501 Feld[ax][ay] = EL_WALL;
5505 PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5507 PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5511 void CheckForDragon(int x, int y)
5514 boolean dragon_found = FALSE;
5515 static int xy[4][2] =
5527 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5529 if (IN_LEV_FIELD(xx, yy) &&
5530 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5532 if (Feld[xx][yy] == EL_DRAGON)
5533 dragon_found = TRUE;
5546 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5548 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5550 Feld[xx][yy] = EL_EMPTY;
5551 DrawLevelField(xx, yy);
5560 static void InitBuggyBase(int x, int y)
5562 int element = Feld[x][y];
5563 int activating_delay = FRAMES_PER_SECOND / 4;
5566 (element == EL_SP_BUGGY_BASE ?
5567 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5568 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5570 element == EL_SP_BUGGY_BASE_ACTIVE ?
5571 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5574 static void WarnBuggyBase(int x, int y)
5577 static int xy[4][2] =
5587 int xx = x + xy[i][0], yy = y + xy[i][1];
5589 if (IS_PLAYER(xx, yy))
5591 PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5598 static void InitTrap(int x, int y)
5600 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5603 static void ActivateTrap(int x, int y)
5605 PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5608 static void ChangeActiveTrap(int x, int y)
5610 int graphic = IMG_TRAP_ACTIVE;
5612 /* if new animation frame was drawn, correct crumbled sand border */
5613 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5614 DrawLevelFieldCrumbledSand(x, y);
5617 static void ChangeElementNowExt(int x, int y, int target_element)
5619 /* check if element under player changes from accessible to unaccessible
5620 (needed for special case of dropping element which then changes) */
5621 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5622 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5629 Feld[x][y] = target_element;
5631 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5633 ResetGfxAnimation(x, y);
5634 ResetRandomAnimationValue(x, y);
5636 InitField(x, y, FALSE);
5637 if (CAN_MOVE(Feld[x][y]))
5640 DrawLevelField(x, y);
5642 if (GFX_CRUMBLED(Feld[x][y]))
5643 DrawLevelFieldCrumbledSandNeighbours(x, y);
5645 TestIfBadThingTouchesHero(x, y);
5646 TestIfPlayerTouchesCustomElement(x, y);
5647 TestIfElementTouchesCustomElement(x, y);
5649 if (ELEM_IS_PLAYER(target_element))
5650 RelocatePlayer(x, y, target_element);
5653 static boolean ChangeElementNow(int x, int y, int element, int page)
5655 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5657 /* always use default change event to prevent running into a loop */
5658 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5659 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5661 /* do not change already changed elements with same change event */
5663 if (Changed[x][y] & ChangeEvent[x][y])
5670 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5672 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5674 if (change->explode)
5681 if (change->use_content)
5683 boolean complete_change = TRUE;
5684 boolean can_change[3][3];
5687 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5689 boolean half_destructible;
5690 int ex = x + xx - 1;
5691 int ey = y + yy - 1;
5694 can_change[xx][yy] = TRUE;
5696 if (ex == x && ey == y) /* do not check changing element itself */
5699 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5701 can_change[xx][yy] = FALSE; /* do not change empty borders */
5706 if (!IN_LEV_FIELD(ex, ey))
5708 can_change[xx][yy] = FALSE;
5709 complete_change = FALSE;
5716 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5717 e = MovingOrBlocked2Element(ex, ey);
5719 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5721 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5722 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5723 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5725 can_change[xx][yy] = FALSE;
5726 complete_change = FALSE;
5730 if (!change->only_complete || complete_change)
5732 boolean something_has_changed = FALSE;
5734 if (change->only_complete && change->use_random_change &&
5735 RND(100) < change->random)
5738 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5740 int ex = x + xx - 1;
5741 int ey = y + yy - 1;
5743 if (can_change[xx][yy] && (!change->use_random_change ||
5744 RND(100) < change->random))
5746 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5747 RemoveMovingField(ex, ey);
5749 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5751 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5753 something_has_changed = TRUE;
5755 /* for symmetry reasons, freeze newly created border elements */
5756 if (ex != x || ey != y)
5757 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5761 if (something_has_changed)
5762 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5767 ChangeElementNowExt(x, y, change->target_element);
5769 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5775 static void ChangeElement(int x, int y, int page)
5777 int element = MovingOrBlocked2Element(x, y);
5778 struct ElementInfo *ei = &element_info[element];
5779 struct ElementChangeInfo *change = &ei->change_page[page];
5783 if (!CAN_CHANGE(element))
5786 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5787 x, y, element, element_info[element].token_name);
5788 printf("ChangeElement(): This should never happen!\n");
5794 if (ChangeDelay[x][y] == 0) /* initialize element change */
5796 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5797 RND(change->delay_random * change->delay_frames)) + 1;
5799 ResetGfxAnimation(x, y);
5800 ResetRandomAnimationValue(x, y);
5802 if (change->pre_change_function)
5803 change->pre_change_function(x, y);
5806 ChangeDelay[x][y]--;
5808 if (ChangeDelay[x][y] != 0) /* continue element change */
5810 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5812 if (IS_ANIMATED(graphic))
5813 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5815 if (change->change_function)
5816 change->change_function(x, y);
5818 else /* finish element change */
5820 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5822 page = ChangePage[x][y];
5823 ChangePage[x][y] = -1;
5826 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5828 ChangeDelay[x][y] = 1; /* try change after next move step */
5829 ChangePage[x][y] = page; /* remember page to use for change */
5834 if (ChangeElementNow(x, y, element, page))
5836 if (change->post_change_function)
5837 change->post_change_function(x, y);
5842 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5843 int trigger_element,
5849 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5852 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5854 int element = EL_CUSTOM_START + i;
5856 boolean change_element = FALSE;
5859 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5862 for (j=0; j < element_info[element].num_change_pages; j++)
5864 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5866 if (change->can_change &&
5868 change->events & CH_EVENT_BIT(trigger_event) &&
5870 change->sides & trigger_side &&
5871 change->trigger_element == trigger_element)
5874 if (!(change->events & CH_EVENT_BIT(trigger_event)))
5875 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5876 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5879 change_element = TRUE;
5886 if (!change_element)
5889 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5892 if (x == lx && y == ly) /* do not change trigger element itself */
5896 if (Feld[x][y] == element)
5898 ChangeDelay[x][y] = 1;
5899 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5900 ChangeElement(x, y, page);
5908 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5911 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5915 static boolean CheckElementSideChange(int x, int y, int element, int side,
5916 int trigger_event, int page)
5918 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5921 if (Feld[x][y] == EL_BLOCKED)
5923 Blocked2Moving(x, y, &x, &y);
5924 element = Feld[x][y];
5928 page = element_info[element].event_page_nr[trigger_event];
5930 if (!(element_info[element].change_page[page].sides & side))
5933 ChangeDelay[x][y] = 1;
5934 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5935 ChangeElement(x, y, page);
5940 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5942 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5946 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
5949 static byte stored_player_action[MAX_PLAYERS];
5950 static int num_stored_actions = 0;
5952 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5953 int left = player_action & JOY_LEFT;
5954 int right = player_action & JOY_RIGHT;
5955 int up = player_action & JOY_UP;
5956 int down = player_action & JOY_DOWN;
5957 int button1 = player_action & JOY_BUTTON_1;
5958 int button2 = player_action & JOY_BUTTON_2;
5959 int dx = (left ? -1 : right ? 1 : 0);
5960 int dy = (up ? -1 : down ? 1 : 0);
5963 stored_player_action[player->index_nr] = 0;
5964 num_stored_actions++;
5968 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
5971 if (!player->active || tape.pausing)
5977 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
5981 snapped = SnapField(player, dx, dy);
5985 dropped = DropElement(player);
5987 moved = MovePlayer(player, dx, dy);
5990 if (tape.single_step && tape.recording && !tape.pausing)
5992 if (button1 || (dropped && !moved))
5994 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5995 SnapField(player, 0, 0); /* stop snapping */
6000 return player_action;
6002 stored_player_action[player->index_nr] = player_action;
6008 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6011 /* no actions for this player (no input at player's configured device) */
6013 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6014 SnapField(player, 0, 0);
6015 CheckGravityMovement(player);
6017 if (player->MovPos == 0)
6018 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6020 if (player->MovPos == 0) /* needed for tape.playing */
6021 player->is_moving = FALSE;
6027 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6029 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6031 TapeRecordAction(stored_player_action);
6032 num_stored_actions = 0;
6039 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6041 static byte stored_player_action[MAX_PLAYERS];
6042 static int num_stored_actions = 0;
6043 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6044 int left = player_action & JOY_LEFT;
6045 int right = player_action & JOY_RIGHT;
6046 int up = player_action & JOY_UP;
6047 int down = player_action & JOY_DOWN;
6048 int button1 = player_action & JOY_BUTTON_1;
6049 int button2 = player_action & JOY_BUTTON_2;
6050 int dx = (left ? -1 : right ? 1 : 0);
6051 int dy = (up ? -1 : down ? 1 : 0);
6053 stored_player_action[player->index_nr] = 0;
6054 num_stored_actions++;
6056 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6058 if (!player->active || tape.pausing)
6063 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6066 snapped = SnapField(player, dx, dy);
6070 dropped = DropElement(player);
6072 moved = MovePlayer(player, dx, dy);
6075 if (tape.single_step && tape.recording && !tape.pausing)
6077 if (button1 || (dropped && !moved))
6079 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6080 SnapField(player, 0, 0); /* stop snapping */
6084 stored_player_action[player->index_nr] = player_action;
6088 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6090 /* no actions for this player (no input at player's configured device) */
6092 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6093 SnapField(player, 0, 0);
6094 CheckGravityMovement(player);
6096 if (player->MovPos == 0)
6097 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6099 if (player->MovPos == 0) /* needed for tape.playing */
6100 player->is_moving = FALSE;
6103 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6105 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6107 TapeRecordAction(stored_player_action);
6108 num_stored_actions = 0;
6115 static unsigned long action_delay = 0;
6116 unsigned long action_delay_value;
6117 int magic_wall_x = 0, magic_wall_y = 0;
6118 int i, x, y, element, graphic;
6119 byte *recorded_player_action;
6120 byte summarized_player_action = 0;
6122 byte tape_action[MAX_PLAYERS];
6125 if (game_status != GAME_MODE_PLAYING)
6128 action_delay_value =
6129 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6131 if (tape.playing && tape.index_search && !tape.pausing)
6132 action_delay_value = 0;
6134 /* ---------- main game synchronization point ---------- */
6136 WaitUntilDelayReached(&action_delay, action_delay_value);
6138 if (network_playing && !network_player_action_received)
6142 printf("DEBUG: try to get network player actions in time\n");
6146 #if defined(PLATFORM_UNIX)
6147 /* last chance to get network player actions without main loop delay */
6151 if (game_status != GAME_MODE_PLAYING)
6154 if (!network_player_action_received)
6158 printf("DEBUG: failed to get network player actions in time\n");
6169 printf("::: getting new tape action [%d]\n", FrameCounter);
6172 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6174 for (i=0; i<MAX_PLAYERS; i++)
6176 summarized_player_action |= stored_player[i].action;
6178 if (!network_playing)
6179 stored_player[i].effective_action = stored_player[i].action;
6182 #if defined(PLATFORM_UNIX)
6183 if (network_playing)
6184 SendToServer_MovePlayer(summarized_player_action);
6187 if (!options.network && !setup.team_mode)
6188 local_player->effective_action = summarized_player_action;
6190 for (i=0; i < MAX_PLAYERS; i++)
6192 int actual_player_action = stored_player[i].effective_action;
6194 if (stored_player[i].programmed_action)
6195 actual_player_action = stored_player[i].programmed_action;
6197 if (recorded_player_action)
6198 actual_player_action = recorded_player_action[i];
6200 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6202 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6207 TapeRecordAction(tape_action);
6210 network_player_action_received = FALSE;
6212 ScrollScreen(NULL, SCROLL_GO_ON);
6218 for (i=0; i<MAX_PLAYERS; i++)
6219 stored_player[i].Frame++;
6223 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6225 for (i=0; i<MAX_PLAYERS; i++)
6227 struct PlayerInfo *player = &stored_player[i];
6231 if (player->active && player->is_pushing && player->is_moving &&
6234 ContinueMoving(x, y);
6236 /* continue moving after pushing (this is actually a bug) */
6237 if (!IS_MOVING(x, y))
6246 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6248 Changed[x][y] = CE_BITMASK_DEFAULT;
6249 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6252 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6254 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6255 printf("GameActions(): This should never happen!\n");
6257 ChangePage[x][y] = -1;
6262 if (WasJustMoving[x][y] > 0)
6263 WasJustMoving[x][y]--;
6264 if (WasJustFalling[x][y] > 0)
6265 WasJustFalling[x][y]--;
6270 /* reset finished pushing action (not done in ContinueMoving() to allow
6271 continous pushing animation for elements with zero push delay) */
6272 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6274 ResetGfxAnimation(x, y);
6275 DrawLevelField(x, y);
6280 if (IS_BLOCKED(x, y))
6284 Blocked2Moving(x, y, &oldx, &oldy);
6285 if (!IS_MOVING(oldx, oldy))
6287 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6288 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6289 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6290 printf("GameActions(): This should never happen!\n");
6296 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6298 element = Feld[x][y];
6300 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6302 graphic = el2img(element);
6308 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6310 element = graphic = 0;
6314 if (graphic_info[graphic].anim_global_sync)
6315 GfxFrame[x][y] = FrameCounter;
6317 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6318 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6319 ResetRandomAnimationValue(x, y);
6321 SetRandomAnimationValue(x, y);
6324 PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6327 if (IS_INACTIVE(element))
6329 if (IS_ANIMATED(graphic))
6330 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6336 /* this may take place after moving, so 'element' may have changed */
6338 if (IS_CHANGING(x, y))
6340 if (IS_CHANGING(x, y) &&
6341 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6345 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6346 element_info[element].event_page_nr[CE_DELAY]);
6348 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6351 element = Feld[x][y];
6352 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6356 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6361 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6363 if (element == EL_MOLE)
6364 printf("::: %d, %d, %d [%d]\n",
6365 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6369 if (element == EL_YAMYAM)
6370 printf("::: %d, %d, %d\n",
6371 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6375 if (IS_ANIMATED(graphic) &&
6379 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6382 if (element == EL_BUG)
6383 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6387 if (element == EL_MOLE)
6388 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6392 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6393 EdelsteinFunkeln(x, y);
6395 else if ((element == EL_ACID ||
6396 element == EL_EXIT_OPEN ||
6397 element == EL_SP_EXIT_OPEN ||
6398 element == EL_SP_TERMINAL ||
6399 element == EL_SP_TERMINAL_ACTIVE ||
6400 element == EL_EXTRA_TIME ||
6401 element == EL_SHIELD_NORMAL ||
6402 element == EL_SHIELD_DEADLY) &&
6403 IS_ANIMATED(graphic))
6404 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6405 else if (IS_MOVING(x, y))
6406 ContinueMoving(x, y);
6407 else if (IS_ACTIVE_BOMB(element))
6408 CheckDynamite(x, y);
6410 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6411 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6413 else if (element == EL_AMOEBA_GROWING)
6414 AmoebeWaechst(x, y);
6415 else if (element == EL_AMOEBA_SHRINKING)
6416 AmoebaDisappearing(x, y);
6418 #if !USE_NEW_AMOEBA_CODE
6419 else if (IS_AMOEBALIVE(element))
6420 AmoebeAbleger(x, y);
6423 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6425 else if (element == EL_EXIT_CLOSED)
6427 else if (element == EL_SP_EXIT_CLOSED)
6429 else if (element == EL_EXPANDABLE_WALL_GROWING)
6431 else if (element == EL_EXPANDABLE_WALL ||
6432 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6433 element == EL_EXPANDABLE_WALL_VERTICAL ||
6434 element == EL_EXPANDABLE_WALL_ANY)
6436 else if (element == EL_FLAMES)
6437 CheckForDragon(x, y);
6439 else if (IS_AUTO_CHANGING(element))
6440 ChangeElement(x, y);
6442 else if (element == EL_EXPLOSION)
6443 ; /* drawing of correct explosion animation is handled separately */
6444 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6445 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6448 /* this may take place after moving, so 'element' may have changed */
6449 if (IS_AUTO_CHANGING(Feld[x][y]))
6450 ChangeElement(x, y);
6453 if (IS_BELT_ACTIVE(element))
6454 PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6456 if (game.magic_wall_active)
6458 int jx = local_player->jx, jy = local_player->jy;
6460 /* play the element sound at the position nearest to the player */
6461 if ((element == EL_MAGIC_WALL_FULL ||
6462 element == EL_MAGIC_WALL_ACTIVE ||
6463 element == EL_MAGIC_WALL_EMPTYING ||
6464 element == EL_BD_MAGIC_WALL_FULL ||
6465 element == EL_BD_MAGIC_WALL_ACTIVE ||
6466 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6467 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6475 #if USE_NEW_AMOEBA_CODE
6476 /* new experimental amoeba growth stuff */
6478 if (!(FrameCounter % 8))
6481 static unsigned long random = 1684108901;
6483 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6486 x = (random >> 10) % lev_fieldx;
6487 y = (random >> 20) % lev_fieldy;
6489 x = RND(lev_fieldx);
6490 y = RND(lev_fieldy);
6492 element = Feld[x][y];
6494 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6495 if (!IS_PLAYER(x,y) &&
6496 (element == EL_EMPTY ||
6497 element == EL_SAND ||
6498 element == EL_QUICKSAND_EMPTY ||
6499 element == EL_ACID_SPLASH_LEFT ||
6500 element == EL_ACID_SPLASH_RIGHT))
6502 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6503 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6504 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6505 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6506 Feld[x][y] = EL_AMOEBA_DROP;
6509 random = random * 129 + 1;
6515 if (game.explosions_delayed)
6518 game.explosions_delayed = FALSE;
6520 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6522 element = Feld[x][y];
6524 if (ExplodeField[x][y])
6525 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6526 else if (element == EL_EXPLOSION)
6527 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6529 ExplodeField[x][y] = EX_NO_EXPLOSION;
6532 game.explosions_delayed = TRUE;
6535 if (game.magic_wall_active)
6537 if (!(game.magic_wall_time_left % 4))
6539 int element = Feld[magic_wall_x][magic_wall_y];
6541 if (element == EL_BD_MAGIC_WALL_FULL ||
6542 element == EL_BD_MAGIC_WALL_ACTIVE ||
6543 element == EL_BD_MAGIC_WALL_EMPTYING)
6544 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6546 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6549 if (game.magic_wall_time_left > 0)
6551 game.magic_wall_time_left--;
6552 if (!game.magic_wall_time_left)
6554 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6556 element = Feld[x][y];
6558 if (element == EL_MAGIC_WALL_ACTIVE ||
6559 element == EL_MAGIC_WALL_FULL)
6561 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6562 DrawLevelField(x, y);
6564 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6565 element == EL_BD_MAGIC_WALL_FULL)
6567 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6568 DrawLevelField(x, y);
6572 game.magic_wall_active = FALSE;
6577 if (game.light_time_left > 0)
6579 game.light_time_left--;
6581 if (game.light_time_left == 0)
6582 RedrawAllLightSwitchesAndInvisibleElements();
6585 if (game.timegate_time_left > 0)
6587 game.timegate_time_left--;
6589 if (game.timegate_time_left == 0)
6590 CloseAllOpenTimegates();
6593 for (i=0; i<MAX_PLAYERS; i++)
6595 struct PlayerInfo *player = &stored_player[i];
6597 if (SHIELD_ON(player))
6599 if (player->shield_deadly_time_left)
6600 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6601 else if (player->shield_normal_time_left)
6602 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6606 if (TimeFrames >= FRAMES_PER_SECOND)
6611 for (i=0; i<MAX_PLAYERS; i++)
6613 struct PlayerInfo *player = &stored_player[i];
6615 if (SHIELD_ON(player))
6617 player->shield_normal_time_left--;
6619 if (player->shield_deadly_time_left > 0)
6620 player->shield_deadly_time_left--;
6624 if (tape.recording || tape.playing)
6625 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6631 if (TimeLeft <= 10 && setup.time_limit)
6632 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6634 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6636 if (!TimeLeft && setup.time_limit)
6637 for (i=0; i<MAX_PLAYERS; i++)
6638 KillHero(&stored_player[i]);
6640 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6641 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6646 if (options.debug) /* calculate frames per second */
6648 static unsigned long fps_counter = 0;
6649 static int fps_frames = 0;
6650 unsigned long fps_delay_ms = Counter() - fps_counter;
6654 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6656 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6659 fps_counter = Counter();
6662 redraw_mask |= REDRAW_FPS;
6666 if (stored_player[0].jx != stored_player[0].last_jx ||
6667 stored_player[0].jy != stored_player[0].last_jy)
6668 printf("::: %d, %d, %d, %d, %d\n",
6669 stored_player[0].MovDir,
6670 stored_player[0].MovPos,
6671 stored_player[0].GfxPos,
6672 stored_player[0].Frame,
6673 stored_player[0].StepFrame);
6680 for (i=0; i<MAX_PLAYERS; i++)
6683 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6685 stored_player[i].Frame += move_frames;
6687 if (stored_player[i].MovPos != 0)
6688 stored_player[i].StepFrame += move_frames;
6693 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6695 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6697 local_player->show_envelope = 0;
6702 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6704 int min_x = x, min_y = y, max_x = x, max_y = y;
6707 for (i=0; i<MAX_PLAYERS; i++)
6709 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6711 if (!stored_player[i].active || &stored_player[i] == player)
6714 min_x = MIN(min_x, jx);
6715 min_y = MIN(min_y, jy);
6716 max_x = MAX(max_x, jx);
6717 max_y = MAX(max_y, jy);
6720 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6723 static boolean AllPlayersInVisibleScreen()
6727 for (i=0; i<MAX_PLAYERS; i++)
6729 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6731 if (!stored_player[i].active)
6734 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6741 void ScrollLevel(int dx, int dy)
6743 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6746 BlitBitmap(drawto_field, drawto_field,
6747 FX + TILEX * (dx == -1) - softscroll_offset,
6748 FY + TILEY * (dy == -1) - softscroll_offset,
6749 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6750 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6751 FX + TILEX * (dx == 1) - softscroll_offset,
6752 FY + TILEY * (dy == 1) - softscroll_offset);
6756 x = (dx == 1 ? BX1 : BX2);
6757 for (y=BY1; y <= BY2; y++)
6758 DrawScreenField(x, y);
6763 y = (dy == 1 ? BY1 : BY2);
6764 for (x=BX1; x <= BX2; x++)
6765 DrawScreenField(x, y);
6768 redraw_mask |= REDRAW_FIELD;
6771 static void CheckGravityMovement(struct PlayerInfo *player)
6773 if (game.gravity && !player->programmed_action)
6775 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6776 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6778 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6779 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6780 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6781 int jx = player->jx, jy = player->jy;
6782 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6783 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6784 int new_jx = jx + dx, new_jy = jy + dy;
6785 boolean field_under_player_is_free =
6786 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6787 boolean player_is_moving_to_valid_field =
6788 (IN_LEV_FIELD(new_jx, new_jy) &&
6789 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6790 Feld[new_jx][new_jy] == EL_SAND));
6791 /* !!! extend EL_SAND to anything diggable !!! */
6793 if (field_under_player_is_free &&
6794 !player_is_moving_to_valid_field &&
6795 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6796 player->programmed_action = MV_DOWN;
6802 -----------------------------------------------------------------------------
6803 dx, dy: direction (non-diagonal) to try to move the player to
6804 real_dx, real_dy: direction as read from input device (can be diagonal)
6807 boolean MovePlayerOneStep(struct PlayerInfo *player,
6808 int dx, int dy, int real_dx, int real_dy)
6811 static int change_sides[4][2] =
6813 /* enter side leave side */
6814 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6815 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6816 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6817 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6819 int move_direction = (dx == -1 ? MV_LEFT :
6820 dx == +1 ? MV_RIGHT :
6822 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6823 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6824 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6826 int jx = player->jx, jy = player->jy;
6827 int new_jx = jx + dx, new_jy = jy + dy;
6831 if (!player->active || (!dx && !dy))
6832 return MF_NO_ACTION;
6834 player->MovDir = (dx < 0 ? MV_LEFT :
6837 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6839 if (!IN_LEV_FIELD(new_jx, new_jy))
6840 return MF_NO_ACTION;
6842 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6843 return MF_NO_ACTION;
6846 element = MovingOrBlocked2Element(new_jx, new_jy);
6848 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6851 if (DONT_RUN_INTO(element))
6853 if (element == EL_ACID && dx == 0 && dy == 1)
6856 Feld[jx][jy] = EL_PLAYER_1;
6857 InitMovingField(jx, jy, MV_DOWN);
6858 Store[jx][jy] = EL_ACID;
6859 ContinueMoving(jx, jy);
6863 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6868 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6869 if (can_move != MF_MOVING)
6872 /* check if DigField() has caused relocation of the player */
6873 if (player->jx != jx || player->jy != jy)
6874 return MF_NO_ACTION;
6876 StorePlayer[jx][jy] = 0;
6877 player->last_jx = jx;
6878 player->last_jy = jy;
6879 player->jx = new_jx;
6880 player->jy = new_jy;
6881 StorePlayer[new_jx][new_jy] = player->element_nr;
6884 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6886 ScrollPlayer(player, SCROLL_INIT);
6889 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6891 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6892 CE_OTHER_GETS_LEFT);
6893 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6894 CE_LEFT_BY_PLAYER, -1);
6897 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6899 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6900 enter_side, CE_OTHER_GETS_ENTERED);
6901 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6902 CE_ENTERED_BY_PLAYER, -1);
6909 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6911 int jx = player->jx, jy = player->jy;
6912 int old_jx = jx, old_jy = jy;
6913 int moved = MF_NO_ACTION;
6915 if (!player->active || (!dx && !dy))
6919 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6923 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6924 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6928 /* remove the last programmed player action */
6929 player->programmed_action = 0;
6933 /* should only happen if pre-1.2 tape recordings are played */
6934 /* this is only for backward compatibility */
6936 int original_move_delay_value = player->move_delay_value;
6939 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6943 /* scroll remaining steps with finest movement resolution */
6944 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6946 while (player->MovPos)
6948 ScrollPlayer(player, SCROLL_GO_ON);
6949 ScrollScreen(NULL, SCROLL_GO_ON);
6955 player->move_delay_value = original_move_delay_value;
6958 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6960 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6961 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6965 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6966 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6972 if (moved & MF_MOVING && !ScreenMovPos &&
6973 (player == local_player || !options.network))
6975 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6976 int offset = (setup.scroll_delay ? 3 : 0);
6978 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6980 /* actual player has left the screen -- scroll in that direction */
6981 if (jx != old_jx) /* player has moved horizontally */
6982 scroll_x += (jx - old_jx);
6983 else /* player has moved vertically */
6984 scroll_y += (jy - old_jy);
6988 if (jx != old_jx) /* player has moved horizontally */
6990 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6991 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6992 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6994 /* don't scroll over playfield boundaries */
6995 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6996 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6998 /* don't scroll more than one field at a time */
6999 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7001 /* don't scroll against the player's moving direction */
7002 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7003 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7004 scroll_x = old_scroll_x;
7006 else /* player has moved vertically */
7008 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7009 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7010 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7012 /* don't scroll over playfield boundaries */
7013 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7014 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7016 /* don't scroll more than one field at a time */
7017 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7019 /* don't scroll against the player's moving direction */
7020 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7021 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7022 scroll_y = old_scroll_y;
7026 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7028 if (!options.network && !AllPlayersInVisibleScreen())
7030 scroll_x = old_scroll_x;
7031 scroll_y = old_scroll_y;
7035 ScrollScreen(player, SCROLL_INIT);
7036 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7043 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7045 if (!(moved & MF_MOVING) && !player->is_pushing)
7050 player->StepFrame = 0;
7052 if (moved & MF_MOVING)
7054 if (old_jx != jx && old_jy == jy)
7055 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7056 else if (old_jx == jx && old_jy != jy)
7057 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7059 DrawLevelField(jx, jy); /* for "crumbled sand" */
7061 player->last_move_dir = player->MovDir;
7062 player->is_moving = TRUE;
7064 player->is_snapping = FALSE;
7068 player->is_switching = FALSE;
7074 static int change_sides[4][2] =
7076 /* enter side leave side */
7077 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7078 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7079 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7080 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7082 int move_direction = player->MovDir;
7083 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7084 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7087 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7089 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7090 leave_side, CE_OTHER_GETS_LEFT);
7091 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7092 leave_side, CE_LEFT_BY_PLAYER, -1);
7095 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7097 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7098 enter_side, CE_OTHER_GETS_ENTERED);
7099 CheckElementSideChange(jx, jy, Feld[jx][jy],
7100 enter_side, CE_ENTERED_BY_PLAYER, -1);
7111 CheckGravityMovement(player);
7114 player->last_move_dir = MV_NO_MOVING;
7116 player->is_moving = FALSE;
7119 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7121 TestIfHeroTouchesBadThing(jx, jy);
7122 TestIfPlayerTouchesCustomElement(jx, jy);
7125 if (!player->active)
7131 void ScrollPlayer(struct PlayerInfo *player, int mode)
7133 int jx = player->jx, jy = player->jy;
7134 int last_jx = player->last_jx, last_jy = player->last_jy;
7135 int move_stepsize = TILEX / player->move_delay_value;
7137 if (!player->active || !player->MovPos)
7140 if (mode == SCROLL_INIT)
7142 player->actual_frame_counter = FrameCounter;
7143 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7145 if (Feld[last_jx][last_jy] == EL_EMPTY)
7146 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7153 else if (!FrameReached(&player->actual_frame_counter, 1))
7156 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7157 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7159 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7160 Feld[last_jx][last_jy] = EL_EMPTY;
7162 /* before DrawPlayer() to draw correct player graphic for this case */
7163 if (player->MovPos == 0)
7164 CheckGravityMovement(player);
7167 DrawPlayer(player); /* needed here only to cleanup last field */
7170 if (player->MovPos == 0) /* player reached destination field */
7172 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7174 /* continue with normal speed after quickly moving through gate */
7175 HALVE_PLAYER_SPEED(player);
7177 /* be able to make the next move without delay */
7178 player->move_delay = 0;
7181 player->last_jx = jx;
7182 player->last_jy = jy;
7184 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7185 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7186 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7188 DrawPlayer(player); /* needed here only to cleanup last field */
7191 if (local_player->friends_still_needed == 0 ||
7192 IS_SP_ELEMENT(Feld[jx][jy]))
7193 player->LevelSolved = player->GameOver = TRUE;
7196 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7198 TestIfHeroTouchesBadThing(jx, jy);
7199 TestIfPlayerTouchesCustomElement(jx, jy);
7201 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7204 if (!player->active)
7208 if (tape.single_step && tape.recording && !tape.pausing &&
7209 !player->programmed_action)
7210 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7214 void ScrollScreen(struct PlayerInfo *player, int mode)
7216 static unsigned long screen_frame_counter = 0;
7218 if (mode == SCROLL_INIT)
7220 /* set scrolling step size according to actual player's moving speed */
7221 ScrollStepSize = TILEX / player->move_delay_value;
7223 screen_frame_counter = FrameCounter;
7224 ScreenMovDir = player->MovDir;
7225 ScreenMovPos = player->MovPos;
7226 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7229 else if (!FrameReached(&screen_frame_counter, 1))
7234 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7235 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7236 redraw_mask |= REDRAW_FIELD;
7239 ScreenMovDir = MV_NO_MOVING;
7242 void TestIfPlayerTouchesCustomElement(int x, int y)
7244 static int xy[4][2] =
7251 static int change_sides[4][2] =
7253 /* center side border side */
7254 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7255 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7256 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7257 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7259 static int touch_dir[4] =
7266 int center_element = Feld[x][y]; /* should always be non-moving! */
7271 int xx = x + xy[i][0];
7272 int yy = y + xy[i][1];
7273 int center_side = change_sides[i][0];
7274 int border_side = change_sides[i][1];
7277 if (!IN_LEV_FIELD(xx, yy))
7280 if (IS_PLAYER(x, y))
7282 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7283 border_element = Feld[xx][yy]; /* may be moving! */
7284 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7285 border_element = Feld[xx][yy];
7286 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7287 border_element = MovingOrBlocked2Element(xx, yy);
7289 continue; /* center and border element do not touch */
7291 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7292 CE_OTHER_GETS_TOUCHED);
7293 CheckElementSideChange(xx, yy, border_element, border_side,
7294 CE_TOUCHED_BY_PLAYER, -1);
7296 else if (IS_PLAYER(xx, yy))
7298 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7300 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7302 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7303 continue; /* center and border element do not touch */
7306 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7307 CE_OTHER_GETS_TOUCHED);
7308 CheckElementSideChange(x, y, center_element, center_side,
7309 CE_TOUCHED_BY_PLAYER, -1);
7316 void TestIfElementTouchesCustomElement(int x, int y)
7318 static int xy[4][2] =
7325 static int change_sides[4][2] =
7327 /* center side border side */
7328 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7329 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7330 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7331 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7333 static int touch_dir[4] =
7340 boolean change_center_element = FALSE;
7341 int center_element_change_page = 0;
7342 int center_element = Feld[x][y]; /* should always be non-moving! */
7347 int xx = x + xy[i][0];
7348 int yy = y + xy[i][1];
7349 int center_side = change_sides[i][0];
7350 int border_side = change_sides[i][1];
7353 if (!IN_LEV_FIELD(xx, yy))
7356 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7357 border_element = Feld[xx][yy]; /* may be moving! */
7358 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7359 border_element = Feld[xx][yy];
7360 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7361 border_element = MovingOrBlocked2Element(xx, yy);
7363 continue; /* center and border element do not touch */
7365 /* check for change of center element (but change it only once) */
7366 if (IS_CUSTOM_ELEMENT(center_element) &&
7367 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7368 !change_center_element)
7370 for (j=0; j < element_info[center_element].num_change_pages; j++)
7372 struct ElementChangeInfo *change =
7373 &element_info[center_element].change_page[j];
7375 if (change->can_change &&
7376 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7377 change->sides & border_side &&
7378 change->trigger_element == border_element)
7380 change_center_element = TRUE;
7381 center_element_change_page = j;
7388 /* check for change of border element */
7389 if (IS_CUSTOM_ELEMENT(border_element) &&
7390 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7392 for (j=0; j < element_info[border_element].num_change_pages; j++)
7394 struct ElementChangeInfo *change =
7395 &element_info[border_element].change_page[j];
7397 if (change->can_change &&
7398 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7399 change->sides & center_side &&
7400 change->trigger_element == center_element)
7402 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7403 CE_OTHER_IS_TOUCHING, j);
7410 if (change_center_element)
7411 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7412 CE_OTHER_IS_TOUCHING, center_element_change_page);
7415 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7417 int i, kill_x = -1, kill_y = -1;
7418 static int test_xy[4][2] =
7425 static int test_dir[4] =
7435 int test_x, test_y, test_move_dir, test_element;
7437 test_x = good_x + test_xy[i][0];
7438 test_y = good_y + test_xy[i][1];
7439 if (!IN_LEV_FIELD(test_x, test_y))
7443 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7446 test_element = Feld[test_x][test_y];
7448 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7451 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7452 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7454 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7455 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7463 if (kill_x != -1 || kill_y != -1)
7465 if (IS_PLAYER(good_x, good_y))
7467 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7469 if (player->shield_deadly_time_left > 0)
7470 Bang(kill_x, kill_y);
7471 else if (!PLAYER_PROTECTED(good_x, good_y))
7475 Bang(good_x, good_y);
7479 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7481 int i, kill_x = -1, kill_y = -1;
7482 int bad_element = Feld[bad_x][bad_y];
7483 static int test_xy[4][2] =
7490 static int touch_dir[4] =
7497 static int test_dir[4] =
7505 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7510 int test_x, test_y, test_move_dir, test_element;
7512 test_x = bad_x + test_xy[i][0];
7513 test_y = bad_y + test_xy[i][1];
7514 if (!IN_LEV_FIELD(test_x, test_y))
7518 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7520 test_element = Feld[test_x][test_y];
7522 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7523 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7525 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7526 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7528 /* good thing is player or penguin that does not move away */
7529 if (IS_PLAYER(test_x, test_y))
7531 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7533 if (bad_element == EL_ROBOT && player->is_moving)
7534 continue; /* robot does not kill player if he is moving */
7536 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7538 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7539 continue; /* center and border element do not touch */
7546 else if (test_element == EL_PENGUIN)
7555 if (kill_x != -1 || kill_y != -1)
7557 if (IS_PLAYER(kill_x, kill_y))
7559 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7561 if (player->shield_deadly_time_left > 0)
7563 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7567 Bang(kill_x, kill_y);
7571 void TestIfHeroTouchesBadThing(int x, int y)
7573 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7576 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7578 TestIfGoodThingHitsBadThing(x, y, move_dir);
7581 void TestIfBadThingTouchesHero(int x, int y)
7583 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7586 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7588 TestIfBadThingHitsGoodThing(x, y, move_dir);
7591 void TestIfFriendTouchesBadThing(int x, int y)
7593 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7596 void TestIfBadThingTouchesFriend(int x, int y)
7598 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7601 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7603 int i, kill_x = bad_x, kill_y = bad_y;
7604 static int xy[4][2] =
7616 x = bad_x + xy[i][0];
7617 y = bad_y + xy[i][1];
7618 if (!IN_LEV_FIELD(x, y))
7621 element = Feld[x][y];
7622 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7623 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7631 if (kill_x != bad_x || kill_y != bad_y)
7635 void KillHero(struct PlayerInfo *player)
7637 int jx = player->jx, jy = player->jy;
7639 if (!player->active)
7642 /* remove accessible field at the player's position */
7643 Feld[jx][jy] = EL_EMPTY;
7645 /* deactivate shield (else Bang()/Explode() would not work right) */
7646 player->shield_normal_time_left = 0;
7647 player->shield_deadly_time_left = 0;
7653 static void KillHeroUnlessProtected(int x, int y)
7655 if (!PLAYER_PROTECTED(x, y))
7656 KillHero(PLAYERINFO(x, y));
7659 void BuryHero(struct PlayerInfo *player)
7661 int jx = player->jx, jy = player->jy;
7663 if (!player->active)
7667 PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7669 PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7671 PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7673 player->GameOver = TRUE;
7677 void RemoveHero(struct PlayerInfo *player)
7679 int jx = player->jx, jy = player->jy;
7680 int i, found = FALSE;
7682 player->present = FALSE;
7683 player->active = FALSE;
7685 if (!ExplodeField[jx][jy])
7686 StorePlayer[jx][jy] = 0;
7688 for (i=0; i<MAX_PLAYERS; i++)
7689 if (stored_player[i].active)
7693 AllPlayersGone = TRUE;
7700 =============================================================================
7701 checkDiagonalPushing()
7702 -----------------------------------------------------------------------------
7703 check if diagonal input device direction results in pushing of object
7704 (by checking if the alternative direction is walkable, diggable, ...)
7705 =============================================================================
7708 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7709 int x, int y, int real_dx, int real_dy)
7711 int jx, jy, dx, dy, xx, yy;
7713 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7716 /* diagonal direction: check alternative direction */
7721 xx = jx + (dx == 0 ? real_dx : 0);
7722 yy = jy + (dy == 0 ? real_dy : 0);
7724 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7728 =============================================================================
7730 -----------------------------------------------------------------------------
7731 x, y: field next to player (non-diagonal) to try to dig to
7732 real_dx, real_dy: direction as read from input device (can be diagonal)
7733 =============================================================================
7736 int DigField(struct PlayerInfo *player,
7737 int x, int y, int real_dx, int real_dy, int mode)
7739 static int change_sides[4] =
7741 CH_SIDE_RIGHT, /* moving left */
7742 CH_SIDE_LEFT, /* moving right */
7743 CH_SIDE_BOTTOM, /* moving up */
7744 CH_SIDE_TOP, /* moving down */
7746 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7747 int jx = player->jx, jy = player->jy;
7748 int dx = x - jx, dy = y - jy;
7749 int nextx = x + dx, nexty = y + dy;
7750 int move_direction = (dx == -1 ? MV_LEFT :
7751 dx == +1 ? MV_RIGHT :
7753 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7754 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7757 if (player->MovPos == 0)
7759 player->is_digging = FALSE;
7760 player->is_collecting = FALSE;
7763 if (player->MovPos == 0) /* last pushing move finished */
7764 player->is_pushing = FALSE;
7766 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7768 player->is_switching = FALSE;
7769 player->push_delay = 0;
7771 return MF_NO_ACTION;
7774 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7775 return MF_NO_ACTION;
7778 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7780 if (IS_TUBE(Feld[jx][jy]) ||
7781 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7785 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7786 int tube_leave_directions[][2] =
7788 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7789 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7790 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7791 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7792 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7793 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7794 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7795 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7796 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7797 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7798 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7799 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7802 while (tube_leave_directions[i][0] != tube_element)
7805 if (tube_leave_directions[i][0] == -1) /* should not happen */
7809 if (!(tube_leave_directions[i][1] & move_direction))
7810 return MF_NO_ACTION; /* tube has no opening in this direction */
7813 element = Feld[x][y];
7815 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7816 game.engine_version >= VERSION_IDENT(2,2,0,0))
7817 return MF_NO_ACTION;
7821 case EL_SP_PORT_LEFT:
7822 case EL_SP_PORT_RIGHT:
7824 case EL_SP_PORT_DOWN:
7825 case EL_SP_PORT_HORIZONTAL:
7826 case EL_SP_PORT_VERTICAL:
7827 case EL_SP_PORT_ANY:
7828 case EL_SP_GRAVITY_PORT_LEFT:
7829 case EL_SP_GRAVITY_PORT_RIGHT:
7830 case EL_SP_GRAVITY_PORT_UP:
7831 case EL_SP_GRAVITY_PORT_DOWN:
7833 element != EL_SP_PORT_LEFT &&
7834 element != EL_SP_GRAVITY_PORT_LEFT &&
7835 element != EL_SP_PORT_HORIZONTAL &&
7836 element != EL_SP_PORT_ANY) ||
7838 element != EL_SP_PORT_RIGHT &&
7839 element != EL_SP_GRAVITY_PORT_RIGHT &&
7840 element != EL_SP_PORT_HORIZONTAL &&
7841 element != EL_SP_PORT_ANY) ||
7843 element != EL_SP_PORT_UP &&
7844 element != EL_SP_GRAVITY_PORT_UP &&
7845 element != EL_SP_PORT_VERTICAL &&
7846 element != EL_SP_PORT_ANY) ||
7848 element != EL_SP_PORT_DOWN &&
7849 element != EL_SP_GRAVITY_PORT_DOWN &&
7850 element != EL_SP_PORT_VERTICAL &&
7851 element != EL_SP_PORT_ANY) ||
7852 !IN_LEV_FIELD(nextx, nexty) ||
7853 !IS_FREE(nextx, nexty))
7854 return MF_NO_ACTION;
7856 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7857 element == EL_SP_GRAVITY_PORT_RIGHT ||
7858 element == EL_SP_GRAVITY_PORT_UP ||
7859 element == EL_SP_GRAVITY_PORT_DOWN)
7860 game.gravity = !game.gravity;
7862 /* automatically move to the next field with double speed */
7863 player->programmed_action = move_direction;
7864 DOUBLE_PLAYER_SPEED(player);
7866 PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7870 case EL_TUBE_VERTICAL:
7871 case EL_TUBE_HORIZONTAL:
7872 case EL_TUBE_VERTICAL_LEFT:
7873 case EL_TUBE_VERTICAL_RIGHT:
7874 case EL_TUBE_HORIZONTAL_UP:
7875 case EL_TUBE_HORIZONTAL_DOWN:
7876 case EL_TUBE_LEFT_UP:
7877 case EL_TUBE_LEFT_DOWN:
7878 case EL_TUBE_RIGHT_UP:
7879 case EL_TUBE_RIGHT_DOWN:
7882 int tube_enter_directions[][2] =
7884 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7885 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7886 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7887 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7888 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7889 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7890 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7891 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7892 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7893 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7894 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7895 { -1, MV_NO_MOVING }
7898 while (tube_enter_directions[i][0] != element)
7901 if (tube_enter_directions[i][0] == -1) /* should not happen */
7905 if (!(tube_enter_directions[i][1] & move_direction))
7906 return MF_NO_ACTION; /* tube has no opening in this direction */
7908 PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7914 if (IS_WALKABLE(element))
7916 int sound_action = ACTION_WALKING;
7918 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7920 if (!player->key[element - EL_GATE_1])
7921 return MF_NO_ACTION;
7923 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7925 if (!player->key[element - EL_GATE_1_GRAY])
7926 return MF_NO_ACTION;
7928 else if (element == EL_EXIT_OPEN ||
7929 element == EL_SP_EXIT_OPEN ||
7930 element == EL_SP_EXIT_OPENING)
7932 sound_action = ACTION_PASSING; /* player is passing exit */
7934 else if (element == EL_EMPTY)
7936 sound_action = ACTION_MOVING; /* nothing to walk on */
7939 /* play sound from background or player, whatever is available */
7940 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7941 PlaySoundLevelElementAction(x, y, element, sound_action);
7943 PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7947 else if (IS_PASSABLE(element))
7949 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7950 return MF_NO_ACTION;
7953 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7954 return MF_NO_ACTION;
7957 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7959 if (!player->key[element - EL_EM_GATE_1])
7960 return MF_NO_ACTION;
7962 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7964 if (!player->key[element - EL_EM_GATE_1_GRAY])
7965 return MF_NO_ACTION;
7968 /* automatically move to the next field with double speed */
7969 player->programmed_action = move_direction;
7970 DOUBLE_PLAYER_SPEED(player);
7972 PlaySoundLevelAction(x, y, ACTION_PASSING);
7976 else if (IS_DIGGABLE(element))
7980 if (mode != DF_SNAP)
7983 GfxElement[x][y] = GFX_ELEMENT(element);
7986 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7988 player->is_digging = TRUE;
7991 PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7993 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7996 if (mode == DF_SNAP)
7997 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8002 else if (IS_COLLECTIBLE(element))
8006 if (mode != DF_SNAP)
8008 GfxElement[x][y] = element;
8009 player->is_collecting = TRUE;
8012 if (element == EL_SPEED_PILL)
8013 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8014 else if (element == EL_EXTRA_TIME && level.time > 0)
8017 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8019 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8021 player->shield_normal_time_left += 10;
8022 if (element == EL_SHIELD_DEADLY)
8023 player->shield_deadly_time_left += 10;
8025 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8027 if (player->inventory_size < MAX_INVENTORY_SIZE)
8028 player->inventory_element[player->inventory_size++] = element;
8030 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8031 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8033 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8035 player->dynabomb_count++;
8036 player->dynabombs_left++;
8038 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8040 player->dynabomb_size++;
8042 else if (element == EL_DYNABOMB_INCREASE_POWER)
8044 player->dynabomb_xl = TRUE;
8046 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8047 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8049 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8050 element - EL_KEY_1 : element - EL_EM_KEY_1);
8052 player->key[key_nr] = TRUE;
8054 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8055 el2edimg(EL_KEY_1 + key_nr));
8056 redraw_mask |= REDRAW_DOOR_1;
8058 else if (IS_ENVELOPE(element))
8061 player->show_envelope = element;
8063 ShowEnvelope(element - EL_ENVELOPE_1);
8066 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8070 for (i=0; i < element_info[element].collect_count; i++)
8071 if (player->inventory_size < MAX_INVENTORY_SIZE)
8072 player->inventory_element[player->inventory_size++] = element;
8074 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8075 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8077 else if (element_info[element].collect_count > 0)
8079 local_player->gems_still_needed -=
8080 element_info[element].collect_count;
8081 if (local_player->gems_still_needed < 0)
8082 local_player->gems_still_needed = 0;
8084 DrawText(DX_EMERALDS, DY_EMERALDS,
8085 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8088 RaiseScoreElement(element);
8089 PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
8091 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8094 if (mode == DF_SNAP)
8095 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8100 else if (IS_PUSHABLE(element))
8102 if (mode == DF_SNAP && element != EL_BD_ROCK)
8103 return MF_NO_ACTION;
8105 if (CAN_FALL(element) && dy)
8106 return MF_NO_ACTION;
8108 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8109 !(element == EL_SPRING && use_spring_bug))
8110 return MF_NO_ACTION;
8113 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8114 ((move_direction & MV_VERTICAL &&
8115 ((element_info[element].move_pattern & MV_LEFT &&
8116 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8117 (element_info[element].move_pattern & MV_RIGHT &&
8118 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8119 (move_direction & MV_HORIZONTAL &&
8120 ((element_info[element].move_pattern & MV_UP &&
8121 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8122 (element_info[element].move_pattern & MV_DOWN &&
8123 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8124 return MF_NO_ACTION;
8128 /* do not push elements already moving away faster than player */
8129 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8130 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8131 return MF_NO_ACTION;
8133 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8134 return MF_NO_ACTION;
8138 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8140 if (player->push_delay_value == -1)
8141 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8143 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8145 if (!player->is_pushing)
8146 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8150 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8151 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8152 !player_is_pushing))
8153 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8156 if (!player->is_pushing &&
8157 game.engine_version >= VERSION_IDENT(2,2,0,7))
8158 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8162 printf("::: push delay: %ld [%d, %d] [%d]\n",
8163 player->push_delay_value, FrameCounter, game.engine_version,
8164 player->is_pushing);
8167 player->is_pushing = TRUE;
8169 if (!(IN_LEV_FIELD(nextx, nexty) &&
8170 (IS_FREE(nextx, nexty) ||
8171 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8172 IS_SB_ELEMENT(element)))))
8173 return MF_NO_ACTION;
8175 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8176 return MF_NO_ACTION;
8178 if (player->push_delay == 0) /* new pushing; restart delay */
8179 player->push_delay = FrameCounter;
8181 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8182 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8183 element != EL_SPRING && element != EL_BALLOON)
8185 /* make sure that there is no move delay before next try to push */
8186 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8187 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8189 return MF_NO_ACTION;
8193 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8196 if (IS_SB_ELEMENT(element))
8198 if (element == EL_SOKOBAN_FIELD_FULL)
8200 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8201 local_player->sokobanfields_still_needed++;
8204 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8206 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8207 local_player->sokobanfields_still_needed--;
8210 Feld[x][y] = EL_SOKOBAN_OBJECT;
8212 if (Back[x][y] == Back[nextx][nexty])
8213 PlaySoundLevelAction(x, y, ACTION_PUSHING);
8214 else if (Back[x][y] != 0)
8215 PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8218 PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8221 if (local_player->sokobanfields_still_needed == 0 &&
8222 game.emulation == EMU_SOKOBAN)
8224 player->LevelSolved = player->GameOver = TRUE;
8225 PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
8229 PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
8231 InitMovingField(x, y, move_direction);
8232 GfxAction[x][y] = ACTION_PUSHING;
8234 if (mode == DF_SNAP)
8235 ContinueMoving(x, y);
8237 MovPos[x][y] = (dx != 0 ? dx : dy);
8239 Pushed[x][y] = TRUE;
8240 Pushed[nextx][nexty] = TRUE;
8242 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8243 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8245 player->push_delay_value = -1; /* get new value later */
8247 CheckTriggeredElementSideChange(x, y, element, dig_side,
8248 CE_OTHER_GETS_PUSHED);
8249 CheckElementSideChange(x, y, element, dig_side,
8250 CE_PUSHED_BY_PLAYER, -1);
8254 else if (IS_SWITCHABLE(element))
8256 if (PLAYER_SWITCHING(player, x, y))
8259 player->is_switching = TRUE;
8260 player->switch_x = x;
8261 player->switch_y = y;
8263 PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
8265 if (element == EL_ROBOT_WHEEL)
8267 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8271 DrawLevelField(x, y);
8273 else if (element == EL_SP_TERMINAL)
8277 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8279 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8281 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8282 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8285 else if (IS_BELT_SWITCH(element))
8287 ToggleBeltSwitch(x, y);
8289 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8290 element == EL_SWITCHGATE_SWITCH_DOWN)
8292 ToggleSwitchgateSwitch(x, y);
8294 else if (element == EL_LIGHT_SWITCH ||
8295 element == EL_LIGHT_SWITCH_ACTIVE)
8297 ToggleLightSwitch(x, y);
8300 PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8301 SND_LIGHT_SWITCH_ACTIVATING :
8302 SND_LIGHT_SWITCH_DEACTIVATING);
8305 else if (element == EL_TIMEGATE_SWITCH)
8307 ActivateTimegateSwitch(x, y);
8309 else if (element == EL_BALLOON_SWITCH_LEFT ||
8310 element == EL_BALLOON_SWITCH_RIGHT ||
8311 element == EL_BALLOON_SWITCH_UP ||
8312 element == EL_BALLOON_SWITCH_DOWN ||
8313 element == EL_BALLOON_SWITCH_ANY)
8315 if (element == EL_BALLOON_SWITCH_ANY)
8316 game.balloon_dir = move_direction;
8318 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8319 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8320 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8321 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8324 else if (element == EL_LAMP)
8326 Feld[x][y] = EL_LAMP_ACTIVE;
8327 local_player->lights_still_needed--;
8329 DrawLevelField(x, y);
8331 else if (element == EL_TIME_ORB_FULL)
8333 Feld[x][y] = EL_TIME_ORB_EMPTY;
8335 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8337 DrawLevelField(x, y);
8340 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8348 if (!PLAYER_SWITCHING(player, x, y))
8350 player->is_switching = TRUE;
8351 player->switch_x = x;
8352 player->switch_y = y;
8354 CheckTriggeredElementSideChange(x, y, element, dig_side,
8355 CE_OTHER_IS_SWITCHING);
8356 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8359 CheckTriggeredElementSideChange(x, y, element, dig_side,
8360 CE_OTHER_GETS_PRESSED);
8361 CheckElementSideChange(x, y, element, dig_side,
8362 CE_PRESSED_BY_PLAYER, -1);
8365 return MF_NO_ACTION;
8368 player->push_delay = 0;
8370 if (Feld[x][y] != element) /* really digged/collected something */
8371 player->is_collecting = !player->is_digging;
8376 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8378 int jx = player->jx, jy = player->jy;
8379 int x = jx + dx, y = jy + dy;
8380 int snap_direction = (dx == -1 ? MV_LEFT :
8381 dx == +1 ? MV_RIGHT :
8383 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8385 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8388 if (!player->active || !IN_LEV_FIELD(x, y))
8396 if (player->MovPos == 0)
8397 player->is_pushing = FALSE;
8399 player->is_snapping = FALSE;
8401 if (player->MovPos == 0)
8403 player->is_moving = FALSE;
8404 player->is_digging = FALSE;
8405 player->is_collecting = FALSE;
8411 if (player->is_snapping)
8414 player->MovDir = snap_direction;
8416 player->is_moving = FALSE;
8417 player->is_digging = FALSE;
8418 player->is_collecting = FALSE;
8420 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8423 player->is_snapping = TRUE;
8425 player->is_moving = FALSE;
8426 player->is_digging = FALSE;
8427 player->is_collecting = FALSE;
8429 DrawLevelField(x, y);
8435 boolean DropElement(struct PlayerInfo *player)
8437 int jx = player->jx, jy = player->jy;
8440 if (!player->active || player->MovPos)
8443 old_element = Feld[jx][jy];
8445 /* check if player has anything that can be dropped */
8446 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8449 /* check if anything can be dropped at the current position */
8450 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8453 /* collected custom elements can only be dropped on empty fields */
8454 if (player->inventory_size > 0 &&
8455 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8456 && old_element != EL_EMPTY)
8459 if (old_element != EL_EMPTY)
8460 Back[jx][jy] = old_element; /* store old element on this field */
8462 MovDelay[jx][jy] = 96;
8464 ResetGfxAnimation(jx, jy);
8465 ResetRandomAnimationValue(jx, jy);
8467 if (player->inventory_size > 0)
8469 int new_element = player->inventory_element[--player->inventory_size];
8471 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8472 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8475 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8476 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8478 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8479 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8481 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8483 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8484 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8486 TestIfElementTouchesCustomElement(jx, jy);
8488 else /* player is dropping a dyna bomb */
8490 player->dynabombs_left--;
8493 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8495 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8496 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8498 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8504 /* ------------------------------------------------------------------------- */
8505 /* game sound playing functions */
8506 /* ------------------------------------------------------------------------- */
8508 static int *loop_sound_frame = NULL;
8509 static int *loop_sound_volume = NULL;
8511 void InitPlaySoundLevel()
8513 int num_sounds = getSoundListSize();
8515 if (loop_sound_frame != NULL)
8516 free(loop_sound_frame);
8518 if (loop_sound_volume != NULL)
8519 free(loop_sound_volume);
8521 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8522 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8525 static void PlaySoundLevel(int x, int y, int nr)
8527 int sx = SCREENX(x), sy = SCREENY(y);
8528 int volume, stereo_position;
8529 int max_distance = 8;
8530 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8532 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8533 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8536 if (!IN_LEV_FIELD(x, y) ||
8537 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8538 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8541 volume = SOUND_MAX_VOLUME;
8543 if (!IN_SCR_FIELD(sx, sy))
8545 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8546 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8548 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8551 stereo_position = (SOUND_MAX_LEFT +
8552 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8553 (SCR_FIELDX + 2 * max_distance));
8555 if (IS_LOOP_SOUND(nr))
8557 /* This assures that quieter loop sounds do not overwrite louder ones,
8558 while restarting sound volume comparison with each new game frame. */
8560 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8563 loop_sound_volume[nr] = volume;
8564 loop_sound_frame[nr] = FrameCounter;
8567 PlaySoundExt(nr, volume, stereo_position, type);
8570 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8572 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8573 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8574 y < LEVELY(BY1) ? LEVELY(BY1) :
8575 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8579 static void PlaySoundLevelAction(int x, int y, int action)
8581 PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8584 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8586 int sound_effect = element_info[element].sound[action];
8588 if (sound_effect != SND_UNDEFINED)
8589 PlaySoundLevel(x, y, sound_effect);
8592 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8594 int sound_effect = element_info[Feld[x][y]].sound[action];
8596 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8597 PlaySoundLevel(x, y, sound_effect);
8600 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8602 int sound_effect = element_info[Feld[x][y]].sound[action];
8604 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8605 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8608 void RaiseScore(int value)
8610 local_player->score += value;
8611 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8614 void RaiseScoreElement(int element)
8620 case EL_EMERALD_YELLOW:
8621 case EL_EMERALD_RED:
8622 case EL_EMERALD_PURPLE:
8623 case EL_SP_INFOTRON:
8624 RaiseScore(level.score[SC_EMERALD]);
8627 RaiseScore(level.score[SC_DIAMOND]);
8630 RaiseScore(level.score[SC_CRYSTAL]);
8633 RaiseScore(level.score[SC_PEARL]);
8636 case EL_BD_BUTTERFLY:
8637 case EL_SP_ELECTRON:
8638 RaiseScore(level.score[SC_BUG]);
8642 case EL_SP_SNIKSNAK:
8643 RaiseScore(level.score[SC_SPACESHIP]);
8646 case EL_DARK_YAMYAM:
8647 RaiseScore(level.score[SC_YAMYAM]);
8650 RaiseScore(level.score[SC_ROBOT]);
8653 RaiseScore(level.score[SC_PACMAN]);
8656 RaiseScore(level.score[SC_NUT]);
8659 case EL_SP_DISK_RED:
8660 case EL_DYNABOMB_INCREASE_NUMBER:
8661 case EL_DYNABOMB_INCREASE_SIZE:
8662 case EL_DYNABOMB_INCREASE_POWER:
8663 RaiseScore(level.score[SC_DYNAMITE]);
8665 case EL_SHIELD_NORMAL:
8666 case EL_SHIELD_DEADLY:
8667 RaiseScore(level.score[SC_SHIELD]);
8670 RaiseScore(level.score[SC_TIME_BONUS]);
8676 RaiseScore(level.score[SC_KEY]);
8679 RaiseScore(element_info[element].collect_score);
8684 void RequestQuitGame(boolean ask_if_really_quit)
8686 if (AllPlayersGone ||
8687 !ask_if_really_quit ||
8688 level_editor_test_game ||
8689 Request("Do you really want to quit the game ?",
8690 REQ_ASK | REQ_STAY_CLOSED))
8692 #if defined(PLATFORM_UNIX)
8693 if (options.network)
8694 SendToServer_StopPlaying();
8698 game_status = GAME_MODE_MAIN;
8704 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8709 /* ---------- new game button stuff ---------------------------------------- */
8711 /* graphic position values for game buttons */
8712 #define GAME_BUTTON_XSIZE 30
8713 #define GAME_BUTTON_YSIZE 30
8714 #define GAME_BUTTON_XPOS 5
8715 #define GAME_BUTTON_YPOS 215
8716 #define SOUND_BUTTON_XPOS 5
8717 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8719 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8720 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8721 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8722 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8723 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8724 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8731 } gamebutton_info[NUM_GAME_BUTTONS] =
8734 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8739 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8744 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8749 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8750 SOUND_CTRL_ID_MUSIC,
8751 "background music on/off"
8754 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8755 SOUND_CTRL_ID_LOOPS,
8756 "sound loops on/off"
8759 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8760 SOUND_CTRL_ID_SIMPLE,
8761 "normal sounds on/off"
8765 void CreateGameButtons()
8769 for (i=0; i<NUM_GAME_BUTTONS; i++)
8771 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8772 struct GadgetInfo *gi;
8775 unsigned long event_mask;
8776 int gd_xoffset, gd_yoffset;
8777 int gd_x1, gd_x2, gd_y1, gd_y2;
8780 gd_xoffset = gamebutton_info[i].x;
8781 gd_yoffset = gamebutton_info[i].y;
8782 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8783 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8785 if (id == GAME_CTRL_ID_STOP ||
8786 id == GAME_CTRL_ID_PAUSE ||
8787 id == GAME_CTRL_ID_PLAY)
8789 button_type = GD_TYPE_NORMAL_BUTTON;
8791 event_mask = GD_EVENT_RELEASED;
8792 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8793 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8797 button_type = GD_TYPE_CHECK_BUTTON;
8799 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8800 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8801 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8802 event_mask = GD_EVENT_PRESSED;
8803 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8804 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8807 gi = CreateGadget(GDI_CUSTOM_ID, id,
8808 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8809 GDI_X, DX + gd_xoffset,
8810 GDI_Y, DY + gd_yoffset,
8811 GDI_WIDTH, GAME_BUTTON_XSIZE,
8812 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8813 GDI_TYPE, button_type,
8814 GDI_STATE, GD_BUTTON_UNPRESSED,
8815 GDI_CHECKED, checked,
8816 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8817 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8818 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8819 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8820 GDI_EVENT_MASK, event_mask,
8821 GDI_CALLBACK_ACTION, HandleGameButtons,
8825 Error(ERR_EXIT, "cannot create gadget");
8827 game_gadget[id] = gi;
8831 void FreeGameButtons()
8835 for (i=0; i<NUM_GAME_BUTTONS; i++)
8836 FreeGadget(game_gadget[i]);
8839 static void MapGameButtons()
8843 for (i=0; i<NUM_GAME_BUTTONS; i++)
8844 MapGadget(game_gadget[i]);
8847 void UnmapGameButtons()
8851 for (i=0; i<NUM_GAME_BUTTONS; i++)
8852 UnmapGadget(game_gadget[i]);
8855 static void HandleGameButtons(struct GadgetInfo *gi)
8857 int id = gi->custom_id;
8859 if (game_status != GAME_MODE_PLAYING)
8864 case GAME_CTRL_ID_STOP:
8865 RequestQuitGame(TRUE);
8868 case GAME_CTRL_ID_PAUSE:
8869 if (options.network)
8871 #if defined(PLATFORM_UNIX)
8873 SendToServer_ContinuePlaying();
8875 SendToServer_PausePlaying();
8879 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8882 case GAME_CTRL_ID_PLAY:
8885 #if defined(PLATFORM_UNIX)
8886 if (options.network)
8887 SendToServer_ContinuePlaying();
8891 tape.pausing = FALSE;
8892 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8897 case SOUND_CTRL_ID_MUSIC:
8898 if (setup.sound_music)
8900 setup.sound_music = FALSE;
8903 else if (audio.music_available)
8905 setup.sound = setup.sound_music = TRUE;
8907 SetAudioMode(setup.sound);
8908 PlayMusic(level_nr);
8912 case SOUND_CTRL_ID_LOOPS:
8913 if (setup.sound_loops)
8914 setup.sound_loops = FALSE;
8915 else if (audio.loops_available)
8917 setup.sound = setup.sound_loops = TRUE;
8918 SetAudioMode(setup.sound);
8922 case SOUND_CTRL_ID_SIMPLE:
8923 if (setup.sound_simple)
8924 setup.sound_simple = FALSE;
8925 else if (audio.sound_available)
8927 setup.sound = setup.sound_simple = TRUE;
8928 SetAudioMode(setup.sound);