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 PlayLevelSound(int, int, int);
186 static void PlayLevelSoundNearest(int, int, int);
187 static void PlayLevelSoundAction(int, int, int);
188 static void PlayLevelSoundElementAction(int, int, int, int);
189 static void PlayLevelSoundActionIfLoop(int, int, int);
190 static void StopLevelSoundActionIfLoop(int, int, int);
191 static void PlayLevelMusic();
193 static void MapGameButtons();
194 static void HandleGameButtons(struct GadgetInfo *);
196 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
199 /* ------------------------------------------------------------------------- */
200 /* definition of elements that automatically change to other elements after */
201 /* a specified time, eventually calling a function when changing */
202 /* ------------------------------------------------------------------------- */
204 /* forward declaration for changer functions */
205 static void InitBuggyBase(int x, int y);
206 static void WarnBuggyBase(int x, int y);
208 static void InitTrap(int x, int y);
209 static void ActivateTrap(int x, int y);
210 static void ChangeActiveTrap(int x, int y);
212 static void InitRobotWheel(int x, int y);
213 static void RunRobotWheel(int x, int y);
214 static void StopRobotWheel(int x, int y);
216 static void InitTimegateWheel(int x, int y);
217 static void RunTimegateWheel(int x, int y);
219 struct ChangingElementInfo
224 void (*pre_change_function)(int x, int y);
225 void (*change_function)(int x, int y);
226 void (*post_change_function)(int x, int y);
229 static struct ChangingElementInfo change_delay_list[] =
280 EL_SWITCHGATE_OPENING,
288 EL_SWITCHGATE_CLOSING,
289 EL_SWITCHGATE_CLOSED,
321 EL_ACID_SPLASH_RIGHT,
330 EL_SP_BUGGY_BASE_ACTIVATING,
337 EL_SP_BUGGY_BASE_ACTIVATING,
338 EL_SP_BUGGY_BASE_ACTIVE,
345 EL_SP_BUGGY_BASE_ACTIVE,
369 EL_ROBOT_WHEEL_ACTIVE,
377 EL_TIMEGATE_SWITCH_ACTIVE,
398 int push_delay_fixed, push_delay_random;
403 { EL_BALLOON, 0, 0 },
405 { EL_SOKOBAN_OBJECT, 2, 0 },
406 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
407 { EL_SATELLITE, 2, 0 },
408 { EL_SP_DISK_YELLOW, 2, 0 },
410 { EL_UNDEFINED, 0, 0 },
418 move_stepsize_list[] =
420 { EL_AMOEBA_DROP, 2 },
421 { EL_AMOEBA_DROPPING, 2 },
422 { EL_QUICKSAND_FILLING, 1 },
423 { EL_QUICKSAND_EMPTYING, 1 },
424 { EL_MAGIC_WALL_FILLING, 2 },
425 { EL_BD_MAGIC_WALL_FILLING, 2 },
426 { EL_MAGIC_WALL_EMPTYING, 2 },
427 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
437 collect_count_list[] =
440 { EL_BD_DIAMOND, 1 },
441 { EL_EMERALD_YELLOW, 1 },
442 { EL_EMERALD_RED, 1 },
443 { EL_EMERALD_PURPLE, 1 },
445 { EL_SP_INFOTRON, 1 },
452 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
454 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
455 CH_EVENT_BIT(CE_DELAY))
456 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
457 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
458 IS_JUST_CHANGING(x, y))
460 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
463 void GetPlayerConfig()
465 if (!audio.sound_available)
466 setup.sound_simple = FALSE;
468 if (!audio.loops_available)
469 setup.sound_loops = FALSE;
471 if (!audio.music_available)
472 setup.sound_music = FALSE;
474 if (!video.fullscreen_available)
475 setup.fullscreen = FALSE;
477 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
479 SetAudioMode(setup.sound);
483 static int getBeltNrFromBeltElement(int element)
485 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
486 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
487 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
490 static int getBeltNrFromBeltActiveElement(int element)
492 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
493 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
494 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
497 static int getBeltNrFromBeltSwitchElement(int element)
499 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
500 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
501 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
504 static int getBeltDirNrFromBeltSwitchElement(int element)
506 static int belt_base_element[4] =
508 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
509 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
510 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
511 EL_CONVEYOR_BELT_4_SWITCH_LEFT
514 int belt_nr = getBeltNrFromBeltSwitchElement(element);
515 int belt_dir_nr = element - belt_base_element[belt_nr];
517 return (belt_dir_nr % 3);
520 static int getBeltDirFromBeltSwitchElement(int element)
522 static int belt_move_dir[3] =
529 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
531 return belt_move_dir[belt_dir_nr];
534 static void InitPlayerField(int x, int y, int element, boolean init_game)
536 if (element == EL_SP_MURPHY)
540 if (stored_player[0].present)
542 Feld[x][y] = EL_SP_MURPHY_CLONE;
548 stored_player[0].use_murphy_graphic = TRUE;
551 Feld[x][y] = EL_PLAYER_1;
557 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
558 int jx = player->jx, jy = player->jy;
560 player->present = TRUE;
562 if (!options.network || player->connected)
564 player->active = TRUE;
566 /* remove potentially duplicate players */
567 if (StorePlayer[jx][jy] == Feld[x][y])
568 StorePlayer[jx][jy] = 0;
570 StorePlayer[x][y] = Feld[x][y];
574 printf("Player %d activated.\n", player->element_nr);
575 printf("[Local player is %d and currently %s.]\n",
576 local_player->element_nr,
577 local_player->active ? "active" : "not active");
581 Feld[x][y] = EL_EMPTY;
582 player->jx = player->last_jx = x;
583 player->jy = player->last_jy = y;
587 static void InitField(int x, int y, boolean init_game)
589 int element = Feld[x][y];
598 InitPlayerField(x, y, element, init_game);
602 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
603 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
604 else if (x > 0 && Feld[x-1][y] == EL_ACID)
605 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
606 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
607 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
608 else if (y > 0 && Feld[x][y-1] == EL_ACID)
609 Feld[x][y] = EL_ACID_POOL_BOTTOM;
610 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
611 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
619 case EL_SPACESHIP_RIGHT:
620 case EL_SPACESHIP_UP:
621 case EL_SPACESHIP_LEFT:
622 case EL_SPACESHIP_DOWN:
624 case EL_BD_BUTTERFLY_RIGHT:
625 case EL_BD_BUTTERFLY_UP:
626 case EL_BD_BUTTERFLY_LEFT:
627 case EL_BD_BUTTERFLY_DOWN:
628 case EL_BD_BUTTERFLY:
629 case EL_BD_FIREFLY_RIGHT:
630 case EL_BD_FIREFLY_UP:
631 case EL_BD_FIREFLY_LEFT:
632 case EL_BD_FIREFLY_DOWN:
634 case EL_PACMAN_RIGHT:
658 if (y == lev_fieldy - 1)
660 Feld[x][y] = EL_AMOEBA_GROWING;
661 Store[x][y] = EL_AMOEBA_WET;
665 case EL_DYNAMITE_ACTIVE:
670 local_player->lights_still_needed++;
673 case EL_SOKOBAN_FIELD_EMPTY:
674 local_player->sokobanfields_still_needed++;
678 local_player->friends_still_needed++;
683 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
688 Feld[x][y] = EL_EMPTY;
693 case EL_EM_KEY_1_FILE:
694 Feld[x][y] = EL_EM_KEY_1;
696 case EL_EM_KEY_2_FILE:
697 Feld[x][y] = EL_EM_KEY_2;
699 case EL_EM_KEY_3_FILE:
700 Feld[x][y] = EL_EM_KEY_3;
702 case EL_EM_KEY_4_FILE:
703 Feld[x][y] = EL_EM_KEY_4;
707 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
708 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
709 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
710 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
711 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
712 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
713 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
714 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
715 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
716 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
717 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
718 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
721 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
722 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
723 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
725 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
727 game.belt_dir[belt_nr] = belt_dir;
728 game.belt_dir_nr[belt_nr] = belt_dir_nr;
730 else /* more than one switch -- set it like the first switch */
732 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
737 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
739 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
742 case EL_LIGHT_SWITCH_ACTIVE:
744 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
748 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
754 void DrawGameDoorValues()
758 for (i=0; i<MAX_PLAYERS; i++)
760 if (stored_player[i].key[j])
761 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
762 el2edimg(EL_KEY_1 + j));
764 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
765 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
766 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
767 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
768 DrawText(DX + XX_SCORE, DY + YY_SCORE,
769 int2str(local_player->score, 5), FONT_TEXT_2);
770 DrawText(DX + XX_TIME, DY + YY_TIME,
771 int2str(TimeLeft, 3), FONT_TEXT_2);
776 =============================================================================
778 -----------------------------------------------------------------------------
779 initialize game engine due to level / tape version number
780 =============================================================================
783 static void InitGameEngine()
787 /* set game engine from tape file when re-playing, else from level file */
788 game.engine_version = (tape.playing ? tape.engine_version :
791 /* dynamically adjust element properties according to game engine version */
792 InitElementPropertiesEngine(game.engine_version);
795 printf("level %d: level version == %06d\n", level_nr, level.game_version);
796 printf(" tape version == %06d [%s] [file: %06d]\n",
797 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
799 printf(" => game.engine_version == %06d\n", game.engine_version);
802 /* ---------- initialize player's initial move delay --------------------- */
804 /* dynamically adjust player properties according to game engine version */
805 game.initial_move_delay =
806 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
807 INITIAL_MOVE_DELAY_OFF);
809 /* dynamically adjust player properties according to level information */
810 game.initial_move_delay_value =
811 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
813 /* ---------- initialize player's initial push delay --------------------- */
815 /* dynamically adjust player properties according to game engine version */
816 game.initial_push_delay_value =
817 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
819 /* ---------- initialize changing elements ------------------------------- */
821 /* initialize changing elements information */
822 for (i=0; i < MAX_NUM_ELEMENTS; i++)
824 struct ElementInfo *ei = &element_info[i];
826 /* this pointer might have been changed in the level editor */
827 ei->change = &ei->change_page[0];
829 if (!IS_CUSTOM_ELEMENT(i))
831 ei->change->target_element = EL_EMPTY_SPACE;
832 ei->change->delay_fixed = 0;
833 ei->change->delay_random = 0;
834 ei->change->delay_frames = 1;
837 ei->change_events = CE_BITMASK_DEFAULT;
838 for (j=0; j < NUM_CHANGE_EVENTS; j++)
840 ei->event_page_nr[j] = 0;
841 ei->event_page[j] = &ei->change_page[0];
845 /* add changing elements from pre-defined list */
846 for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
848 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
849 struct ElementInfo *ei = &element_info[ch_delay->element];
851 ei->change->target_element = ch_delay->target_element;
852 ei->change->delay_fixed = ch_delay->change_delay;
854 ei->change->pre_change_function = ch_delay->pre_change_function;
855 ei->change->change_function = ch_delay->change_function;
856 ei->change->post_change_function = ch_delay->post_change_function;
858 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
862 /* add change events from custom element configuration */
863 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
865 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
867 for (j=0; j < ei->num_change_pages; j++)
869 if (!ei->change_page[j].can_change)
872 for (k=0; k < NUM_CHANGE_EVENTS; k++)
874 /* only add event page for the first page found with this event */
875 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
876 !(ei->change_events & CH_EVENT_BIT(k)))
878 ei->change_events |= CH_EVENT_BIT(k);
879 ei->event_page_nr[k] = j;
880 ei->event_page[k] = &ei->change_page[j];
888 /* add change events from custom element configuration */
889 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
891 int element = EL_CUSTOM_START + i;
893 /* only add custom elements that change after fixed/random frame delay */
894 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
895 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
899 /* ---------- initialize trigger events ---------------------------------- */
901 /* initialize trigger events information */
902 for (i=0; i<MAX_NUM_ELEMENTS; i++)
903 trigger_events[i] = EP_BITMASK_DEFAULT;
906 /* add trigger events from element change event properties */
907 for (i=0; i<MAX_NUM_ELEMENTS; i++)
909 struct ElementInfo *ei = &element_info[i];
911 for (j=0; j < ei->num_change_pages; j++)
913 if (!ei->change_page[j].can_change)
916 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
918 int trigger_element = ei->change_page[j].trigger_element;
920 trigger_events[trigger_element] |= ei->change_page[j].events;
925 /* add trigger events from element change event properties */
926 for (i=0; i<MAX_NUM_ELEMENTS; i++)
927 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
928 trigger_events[element_info[i].change->trigger_element] |=
929 element_info[i].change->events;
932 /* ---------- initialize push delay -------------------------------------- */
934 /* initialize push delay values to default */
935 for (i=0; i<MAX_NUM_ELEMENTS; i++)
937 if (!IS_CUSTOM_ELEMENT(i))
939 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
940 element_info[i].push_delay_random = game.default_push_delay_random;
944 /* set push delay value for certain elements from pre-defined list */
945 for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
947 int e = push_delay_list[i].element;
949 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
950 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
953 /* ---------- initialize move stepsize ----------------------------------- */
955 /* initialize move stepsize values to default */
956 for (i=0; i<MAX_NUM_ELEMENTS; i++)
957 if (!IS_CUSTOM_ELEMENT(i))
958 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
960 /* set move stepsize value for certain elements from pre-defined list */
961 for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
963 int e = move_stepsize_list[i].element;
965 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
968 /* ---------- initialize gem count --------------------------------------- */
970 /* initialize gem count values for each element */
971 for (i=0; i<MAX_NUM_ELEMENTS; i++)
972 if (!IS_CUSTOM_ELEMENT(i))
973 element_info[i].collect_count = 0;
975 /* add gem count values for all elements from pre-defined list */
976 for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
977 element_info[collect_count_list[i].element].collect_count =
978 collect_count_list[i].count;
983 =============================================================================
985 -----------------------------------------------------------------------------
986 initialize and start new game
987 =============================================================================
992 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
993 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
994 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1001 #if USE_NEW_AMOEBA_CODE
1002 printf("Using new amoeba code.\n");
1004 printf("Using old amoeba code.\n");
1009 /* don't play tapes over network */
1010 network_playing = (options.network && !tape.playing);
1012 for (i=0; i<MAX_PLAYERS; i++)
1014 struct PlayerInfo *player = &stored_player[i];
1016 player->index_nr = i;
1017 player->element_nr = EL_PLAYER_1 + i;
1019 player->present = FALSE;
1020 player->active = FALSE;
1023 player->effective_action = 0;
1024 player->programmed_action = 0;
1027 player->gems_still_needed = level.gems_needed;
1028 player->sokobanfields_still_needed = 0;
1029 player->lights_still_needed = 0;
1030 player->friends_still_needed = 0;
1033 player->key[j] = FALSE;
1035 player->dynabomb_count = 0;
1036 player->dynabomb_size = 1;
1037 player->dynabombs_left = 0;
1038 player->dynabomb_xl = FALSE;
1040 player->MovDir = MV_NO_MOVING;
1043 player->GfxDir = MV_NO_MOVING;
1044 player->GfxAction = ACTION_DEFAULT;
1046 player->StepFrame = 0;
1048 player->use_murphy_graphic = FALSE;
1050 player->actual_frame_counter = 0;
1052 player->last_move_dir = MV_NO_MOVING;
1054 player->is_waiting = FALSE;
1055 player->is_moving = FALSE;
1056 player->is_digging = FALSE;
1057 player->is_snapping = FALSE;
1058 player->is_collecting = FALSE;
1059 player->is_pushing = FALSE;
1060 player->is_switching = FALSE;
1062 player->switch_x = -1;
1063 player->switch_y = -1;
1065 player->show_envelope = 0;
1067 player->move_delay = game.initial_move_delay;
1068 player->move_delay_value = game.initial_move_delay_value;
1070 player->push_delay = 0;
1071 player->push_delay_value = game.initial_push_delay_value;
1073 player->last_jx = player->last_jy = 0;
1074 player->jx = player->jy = 0;
1076 player->shield_normal_time_left = 0;
1077 player->shield_deadly_time_left = 0;
1079 player->inventory_size = 0;
1081 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1082 SnapField(player, 0, 0);
1084 player->LevelSolved = FALSE;
1085 player->GameOver = FALSE;
1088 network_player_action_received = FALSE;
1090 #if defined(PLATFORM_UNIX)
1091 /* initial null action */
1092 if (network_playing)
1093 SendToServer_MovePlayer(MV_NO_MOVING);
1101 TimeLeft = level.time;
1103 ScreenMovDir = MV_NO_MOVING;
1107 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1109 AllPlayersGone = FALSE;
1111 game.yamyam_content_nr = 0;
1112 game.magic_wall_active = FALSE;
1113 game.magic_wall_time_left = 0;
1114 game.light_time_left = 0;
1115 game.timegate_time_left = 0;
1116 game.switchgate_pos = 0;
1117 game.balloon_dir = MV_NO_MOVING;
1118 game.gravity = level.initial_gravity;
1119 game.explosions_delayed = TRUE;
1121 game.envelope_active = FALSE;
1125 game.belt_dir[i] = MV_NO_MOVING;
1126 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1129 for (i=0; i<MAX_NUM_AMOEBA; i++)
1130 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1132 for (x=0; x<lev_fieldx; x++)
1134 for (y=0; y<lev_fieldy; y++)
1136 Feld[x][y] = level.field[x][y];
1137 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1138 ChangeDelay[x][y] = 0;
1139 ChangePage[x][y] = -1;
1140 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1142 WasJustMoving[x][y] = 0;
1143 WasJustFalling[x][y] = 0;
1145 Pushed[x][y] = FALSE;
1147 Changed[x][y] = CE_BITMASK_DEFAULT;
1148 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1150 ExplodePhase[x][y] = 0;
1151 ExplodeField[x][y] = EX_NO_EXPLOSION;
1154 GfxRandom[x][y] = INIT_GFX_RANDOM();
1155 GfxElement[x][y] = EL_UNDEFINED;
1156 GfxAction[x][y] = ACTION_DEFAULT;
1157 GfxDir[x][y] = MV_NO_MOVING;
1161 for(y=0; y<lev_fieldy; y++)
1163 for(x=0; x<lev_fieldx; x++)
1165 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1167 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1169 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1172 InitField(x, y, TRUE);
1178 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1179 emulate_sb ? EMU_SOKOBAN :
1180 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1182 /* correct non-moving belts to start moving left */
1184 if (game.belt_dir[i] == MV_NO_MOVING)
1185 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1187 /* check if any connected player was not found in playfield */
1188 for (i=0; i<MAX_PLAYERS; i++)
1190 struct PlayerInfo *player = &stored_player[i];
1192 if (player->connected && !player->present)
1194 for (j=0; j<MAX_PLAYERS; j++)
1196 struct PlayerInfo *some_player = &stored_player[j];
1197 int jx = some_player->jx, jy = some_player->jy;
1199 /* assign first free player found that is present in the playfield */
1200 if (some_player->present && !some_player->connected)
1202 player->present = TRUE;
1203 player->active = TRUE;
1204 some_player->present = FALSE;
1206 StorePlayer[jx][jy] = player->element_nr;
1207 player->jx = player->last_jx = jx;
1208 player->jy = player->last_jy = jy;
1218 /* when playing a tape, eliminate all players who do not participate */
1220 for (i=0; i<MAX_PLAYERS; i++)
1222 if (stored_player[i].active && !tape.player_participates[i])
1224 struct PlayerInfo *player = &stored_player[i];
1225 int jx = player->jx, jy = player->jy;
1227 player->active = FALSE;
1228 StorePlayer[jx][jy] = 0;
1229 Feld[jx][jy] = EL_EMPTY;
1233 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1235 /* when in single player mode, eliminate all but the first active player */
1237 for (i=0; i<MAX_PLAYERS; i++)
1239 if (stored_player[i].active)
1241 for (j=i+1; j<MAX_PLAYERS; j++)
1243 if (stored_player[j].active)
1245 struct PlayerInfo *player = &stored_player[j];
1246 int jx = player->jx, jy = player->jy;
1248 player->active = FALSE;
1249 StorePlayer[jx][jy] = 0;
1250 Feld[jx][jy] = EL_EMPTY;
1257 /* when recording the game, store which players take part in the game */
1260 for (i=0; i<MAX_PLAYERS; i++)
1261 if (stored_player[i].active)
1262 tape.player_participates[i] = TRUE;
1267 for (i=0; i<MAX_PLAYERS; i++)
1269 struct PlayerInfo *player = &stored_player[i];
1271 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1276 if (local_player == player)
1277 printf("Player %d is local player.\n", i+1);
1281 if (BorderElement == EL_EMPTY)
1284 SBX_Right = lev_fieldx - SCR_FIELDX;
1286 SBY_Lower = lev_fieldy - SCR_FIELDY;
1291 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1293 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1296 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1297 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1299 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1300 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1302 /* if local player not found, look for custom element that might create
1303 the player (make some assumptions about the right custom element) */
1304 if (!local_player->present)
1306 int start_x = 0, start_y = 0;
1307 int found_rating = 0;
1308 int found_element = EL_UNDEFINED;
1310 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1312 int element = Feld[x][y];
1317 if (!IS_CUSTOM_ELEMENT(element))
1320 if (CAN_CHANGE(element))
1322 for (i=0; i < element_info[element].num_change_pages; i++)
1324 content = element_info[element].change_page[i].target_element;
1325 is_player = ELEM_IS_PLAYER(content);
1327 if (is_player && (found_rating < 3 || element < found_element))
1333 found_element = element;
1338 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1340 content = element_info[element].content[xx][yy];
1341 is_player = ELEM_IS_PLAYER(content);
1343 if (is_player && (found_rating < 2 || element < found_element))
1345 start_x = x + xx - 1;
1346 start_y = y + yy - 1;
1349 found_element = element;
1352 if (!CAN_CHANGE(element))
1355 for (i=0; i < element_info[element].num_change_pages; i++)
1357 content = element_info[element].change_page[i].content[xx][yy];
1358 is_player = ELEM_IS_PLAYER(content);
1360 if (is_player && (found_rating < 1 || element < found_element))
1362 start_x = x + xx - 1;
1363 start_y = y + yy - 1;
1366 found_element = element;
1372 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1373 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1376 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1377 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1383 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1384 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1385 local_player->jx - MIDPOSX);
1387 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1388 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1389 local_player->jy - MIDPOSY);
1391 scroll_x = SBX_Left;
1392 scroll_y = SBY_Upper;
1393 if (local_player->jx >= SBX_Left + MIDPOSX)
1394 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1395 local_player->jx - MIDPOSX :
1397 if (local_player->jy >= SBY_Upper + MIDPOSY)
1398 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1399 local_player->jy - MIDPOSY :
1404 CloseDoor(DOOR_CLOSE_1);
1409 /* after drawing the level, correct some elements */
1410 if (game.timegate_time_left == 0)
1411 CloseAllOpenTimegates();
1413 if (setup.soft_scrolling)
1414 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1416 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1419 /* copy default game door content to main double buffer */
1420 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1421 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1424 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1427 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1428 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1429 BlitBitmap(drawto, drawto,
1430 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1431 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1432 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1433 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1436 DrawGameDoorValues();
1440 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1441 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1442 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1446 /* copy actual game door content to door double buffer for OpenDoor() */
1447 BlitBitmap(drawto, bitmap_db_door,
1448 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1450 OpenDoor(DOOR_OPEN_ALL);
1452 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1454 if (setup.sound_music)
1457 KeyboardAutoRepeatOffUnlessAutoplay();
1462 printf("Player %d %sactive.\n",
1463 i + 1, (stored_player[i].active ? "" : "not "));
1467 printf("::: starting game [%d]\n", FrameCounter);
1471 void InitMovDir(int x, int y)
1473 int i, element = Feld[x][y];
1474 static int xy[4][2] =
1481 static int direction[3][4] =
1483 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1484 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1485 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1494 Feld[x][y] = EL_BUG;
1495 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1498 case EL_SPACESHIP_RIGHT:
1499 case EL_SPACESHIP_UP:
1500 case EL_SPACESHIP_LEFT:
1501 case EL_SPACESHIP_DOWN:
1502 Feld[x][y] = EL_SPACESHIP;
1503 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1506 case EL_BD_BUTTERFLY_RIGHT:
1507 case EL_BD_BUTTERFLY_UP:
1508 case EL_BD_BUTTERFLY_LEFT:
1509 case EL_BD_BUTTERFLY_DOWN:
1510 Feld[x][y] = EL_BD_BUTTERFLY;
1511 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1514 case EL_BD_FIREFLY_RIGHT:
1515 case EL_BD_FIREFLY_UP:
1516 case EL_BD_FIREFLY_LEFT:
1517 case EL_BD_FIREFLY_DOWN:
1518 Feld[x][y] = EL_BD_FIREFLY;
1519 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1522 case EL_PACMAN_RIGHT:
1524 case EL_PACMAN_LEFT:
1525 case EL_PACMAN_DOWN:
1526 Feld[x][y] = EL_PACMAN;
1527 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1530 case EL_SP_SNIKSNAK:
1531 MovDir[x][y] = MV_UP;
1534 case EL_SP_ELECTRON:
1535 MovDir[x][y] = MV_LEFT;
1542 Feld[x][y] = EL_MOLE;
1543 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1547 if (IS_CUSTOM_ELEMENT(element))
1549 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1550 MovDir[x][y] = element_info[element].move_direction_initial;
1551 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1552 element_info[element].move_pattern == MV_TURNING_LEFT ||
1553 element_info[element].move_pattern == MV_TURNING_RIGHT)
1554 MovDir[x][y] = 1 << RND(4);
1555 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1556 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1557 else if (element_info[element].move_pattern == MV_VERTICAL)
1558 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1559 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1560 MovDir[x][y] = element_info[element].move_pattern;
1561 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1562 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1566 int x1 = x + xy[i][0];
1567 int y1 = y + xy[i][1];
1569 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1571 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1572 MovDir[x][y] = direction[0][i];
1574 MovDir[x][y] = direction[1][i];
1583 MovDir[x][y] = 1 << RND(4);
1585 if (element != EL_BUG &&
1586 element != EL_SPACESHIP &&
1587 element != EL_BD_BUTTERFLY &&
1588 element != EL_BD_FIREFLY)
1593 int x1 = x + xy[i][0];
1594 int y1 = y + xy[i][1];
1596 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1598 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1600 MovDir[x][y] = direction[0][i];
1603 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1604 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1606 MovDir[x][y] = direction[1][i];
1615 GfxDir[x][y] = MovDir[x][y];
1618 void InitAmoebaNr(int x, int y)
1621 int group_nr = AmoebeNachbarNr(x, y);
1625 for (i=1; i<MAX_NUM_AMOEBA; i++)
1627 if (AmoebaCnt[i] == 0)
1635 AmoebaNr[x][y] = group_nr;
1636 AmoebaCnt[group_nr]++;
1637 AmoebaCnt2[group_nr]++;
1643 boolean raise_level = FALSE;
1645 if (local_player->MovPos)
1649 if (tape.auto_play) /* tape might already be stopped here */
1650 tape.auto_play_level_solved = TRUE;
1652 if (tape.playing && tape.auto_play)
1653 tape.auto_play_level_solved = TRUE;
1656 local_player->LevelSolved = FALSE;
1658 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1662 if (!tape.playing && setup.sound_loops)
1663 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1664 SND_CTRL_PLAY_LOOP);
1666 while (TimeLeft > 0)
1668 if (!tape.playing && !setup.sound_loops)
1669 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1670 if (TimeLeft > 0 && !(TimeLeft % 10))
1671 RaiseScore(level.score[SC_TIME_BONUS]);
1672 if (TimeLeft > 100 && !(TimeLeft % 10))
1676 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1683 if (!tape.playing && setup.sound_loops)
1684 StopSound(SND_GAME_LEVELTIME_BONUS);
1686 else if (level.time == 0) /* level without time limit */
1688 if (!tape.playing && setup.sound_loops)
1689 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1690 SND_CTRL_PLAY_LOOP);
1692 while (TimePlayed < 999)
1694 if (!tape.playing && !setup.sound_loops)
1695 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1696 if (TimePlayed < 999 && !(TimePlayed % 10))
1697 RaiseScore(level.score[SC_TIME_BONUS]);
1698 if (TimePlayed < 900 && !(TimePlayed % 10))
1702 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1709 if (!tape.playing && setup.sound_loops)
1710 StopSound(SND_GAME_LEVELTIME_BONUS);
1713 /* close exit door after last player */
1714 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1715 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1717 int element = Feld[ExitX][ExitY];
1719 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1720 EL_SP_EXIT_CLOSING);
1722 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1725 /* Hero disappears */
1726 DrawLevelField(ExitX, ExitY);
1732 CloseDoor(DOOR_CLOSE_1);
1737 SaveTape(tape.level_nr); /* Ask to save tape */
1740 if (level_nr == leveldir_current->handicap_level)
1742 leveldir_current->handicap_level++;
1743 SaveLevelSetup_SeriesInfo();
1746 if (level_editor_test_game)
1747 local_player->score = -1; /* no highscore when playing from editor */
1748 else if (level_nr < leveldir_current->last_level)
1749 raise_level = TRUE; /* advance to next level */
1751 if ((hi_pos = NewHiScore()) >= 0)
1753 game_status = GAME_MODE_SCORES;
1754 DrawHallOfFame(hi_pos);
1763 game_status = GAME_MODE_MAIN;
1780 LoadScore(level_nr);
1782 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1783 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1786 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1788 if (local_player->score > highscore[k].Score)
1790 /* player has made it to the hall of fame */
1792 if (k < MAX_SCORE_ENTRIES - 1)
1794 int m = MAX_SCORE_ENTRIES - 1;
1797 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1798 if (!strcmp(setup.player_name, highscore[l].Name))
1800 if (m == k) /* player's new highscore overwrites his old one */
1806 strcpy(highscore[l].Name, highscore[l - 1].Name);
1807 highscore[l].Score = highscore[l - 1].Score;
1814 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1815 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1816 highscore[k].Score = local_player->score;
1822 else if (!strncmp(setup.player_name, highscore[k].Name,
1823 MAX_PLAYER_NAME_LEN))
1824 break; /* player already there with a higher score */
1830 SaveScore(level_nr);
1835 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1837 if (player->GfxAction != action || player->GfxDir != dir)
1840 printf("Player frame reset! (%d => %d, %d => %d)\n",
1841 player->GfxAction, action, player->GfxDir, dir);
1844 player->GfxAction = action;
1845 player->GfxDir = dir;
1847 player->StepFrame = 0;
1851 static void ResetRandomAnimationValue(int x, int y)
1853 GfxRandom[x][y] = INIT_GFX_RANDOM();
1856 static void ResetGfxAnimation(int x, int y)
1859 GfxAction[x][y] = ACTION_DEFAULT;
1860 GfxDir[x][y] = MovDir[x][y];
1863 void InitMovingField(int x, int y, int direction)
1865 int element = Feld[x][y];
1866 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1867 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1871 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1872 ResetGfxAnimation(x, y);
1874 MovDir[newx][newy] = MovDir[x][y] = direction;
1875 GfxDir[x][y] = direction;
1877 if (Feld[newx][newy] == EL_EMPTY)
1878 Feld[newx][newy] = EL_BLOCKED;
1880 if (direction == MV_DOWN && CAN_FALL(element))
1881 GfxAction[x][y] = ACTION_FALLING;
1883 GfxAction[x][y] = ACTION_MOVING;
1885 GfxFrame[newx][newy] = GfxFrame[x][y];
1886 GfxRandom[newx][newy] = GfxRandom[x][y];
1887 GfxAction[newx][newy] = GfxAction[x][y];
1888 GfxDir[newx][newy] = GfxDir[x][y];
1891 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1893 int direction = MovDir[x][y];
1894 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1895 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1901 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1903 int oldx = x, oldy = y;
1904 int direction = MovDir[x][y];
1906 if (direction == MV_LEFT)
1908 else if (direction == MV_RIGHT)
1910 else if (direction == MV_UP)
1912 else if (direction == MV_DOWN)
1915 *comes_from_x = oldx;
1916 *comes_from_y = oldy;
1919 int MovingOrBlocked2Element(int x, int y)
1921 int element = Feld[x][y];
1923 if (element == EL_BLOCKED)
1927 Blocked2Moving(x, y, &oldx, &oldy);
1928 return Feld[oldx][oldy];
1934 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1936 /* like MovingOrBlocked2Element(), but if element is moving
1937 and (x,y) is the field the moving element is just leaving,
1938 return EL_BLOCKED instead of the element value */
1939 int element = Feld[x][y];
1941 if (IS_MOVING(x, y))
1943 if (element == EL_BLOCKED)
1947 Blocked2Moving(x, y, &oldx, &oldy);
1948 return Feld[oldx][oldy];
1957 static void RemoveField(int x, int y)
1959 Feld[x][y] = EL_EMPTY;
1966 ChangeDelay[x][y] = 0;
1967 ChangePage[x][y] = -1;
1968 Pushed[x][y] = FALSE;
1970 GfxElement[x][y] = EL_UNDEFINED;
1971 GfxAction[x][y] = ACTION_DEFAULT;
1972 GfxDir[x][y] = MV_NO_MOVING;
1975 void RemoveMovingField(int x, int y)
1977 int oldx = x, oldy = y, newx = x, newy = y;
1978 int element = Feld[x][y];
1979 int next_element = EL_UNDEFINED;
1981 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1984 if (IS_MOVING(x, y))
1986 Moving2Blocked(x, y, &newx, &newy);
1987 if (Feld[newx][newy] != EL_BLOCKED)
1990 else if (element == EL_BLOCKED)
1992 Blocked2Moving(x, y, &oldx, &oldy);
1993 if (!IS_MOVING(oldx, oldy))
1997 if (element == EL_BLOCKED &&
1998 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1999 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2000 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2001 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2002 next_element = get_next_element(Feld[oldx][oldy]);
2004 RemoveField(oldx, oldy);
2005 RemoveField(newx, newy);
2007 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2009 if (next_element != EL_UNDEFINED)
2010 Feld[oldx][oldy] = next_element;
2012 DrawLevelField(oldx, oldy);
2013 DrawLevelField(newx, newy);
2016 void DrawDynamite(int x, int y)
2018 int sx = SCREENX(x), sy = SCREENY(y);
2019 int graphic = el2img(Feld[x][y]);
2022 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2025 if (IS_WALKABLE_INSIDE(Back[x][y]))
2029 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2030 else if (Store[x][y])
2031 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2033 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2036 if (Back[x][y] || Store[x][y])
2037 DrawGraphicThruMask(sx, sy, graphic, frame);
2039 DrawGraphic(sx, sy, graphic, frame);
2041 if (game.emulation == EMU_SUPAPLEX)
2042 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2043 else if (Store[x][y])
2044 DrawGraphicThruMask(sx, sy, graphic, frame);
2046 DrawGraphic(sx, sy, graphic, frame);
2050 void CheckDynamite(int x, int y)
2052 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2056 if (MovDelay[x][y] != 0)
2059 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2066 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2068 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2069 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2070 StopSound(SND_DYNAMITE_ACTIVE);
2072 StopSound(SND_DYNABOMB_ACTIVE);
2078 void RelocatePlayer(int x, int y, int element)
2080 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2083 RemoveField(x, y); /* temporarily remove newly placed player */
2084 DrawLevelField(x, y);
2087 if (player->present)
2089 while (player->MovPos)
2091 ScrollPlayer(player, SCROLL_GO_ON);
2092 ScrollScreen(NULL, SCROLL_GO_ON);
2098 Delay(GAME_FRAME_DELAY);
2101 DrawPlayer(player); /* needed here only to cleanup last field */
2102 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2104 player->is_moving = FALSE;
2107 Feld[x][y] = element;
2108 InitPlayerField(x, y, element, TRUE);
2110 if (player == local_player)
2112 int scroll_xx = -999, scroll_yy = -999;
2114 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2117 int fx = FX, fy = FY;
2119 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2120 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2121 local_player->jx - MIDPOSX);
2123 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2124 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2125 local_player->jy - MIDPOSY);
2127 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2128 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2133 fx += dx * TILEX / 2;
2134 fy += dy * TILEY / 2;
2136 ScrollLevel(dx, dy);
2139 /* scroll in two steps of half tile size to make things smoother */
2140 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2142 Delay(GAME_FRAME_DELAY);
2144 /* scroll second step to align at full tile size */
2146 Delay(GAME_FRAME_DELAY);
2151 void Explode(int ex, int ey, int phase, int mode)
2155 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2156 int last_phase = num_phase * delay;
2157 int half_phase = (num_phase / 2) * delay;
2158 int first_phase_after_start = EX_PHASE_START + 1;
2160 if (game.explosions_delayed)
2162 ExplodeField[ex][ey] = mode;
2166 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2168 int center_element = Feld[ex][ey];
2171 /* --- This is only really needed (and now handled) in "Impact()". --- */
2172 /* do not explode moving elements that left the explode field in time */
2173 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2174 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2178 if (mode == EX_NORMAL || mode == EX_CENTER)
2179 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2181 /* remove things displayed in background while burning dynamite */
2182 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2185 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2187 /* put moving element to center field (and let it explode there) */
2188 center_element = MovingOrBlocked2Element(ex, ey);
2189 RemoveMovingField(ex, ey);
2190 Feld[ex][ey] = center_element;
2193 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2195 int xx = x - ex + 1;
2196 int yy = y - ey + 1;
2199 if (!IN_LEV_FIELD(x, y) ||
2200 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2201 (x != ex || y != ey)))
2204 element = Feld[x][y];
2206 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2208 element = MovingOrBlocked2Element(x, y);
2210 if (!IS_EXPLOSION_PROOF(element))
2211 RemoveMovingField(x, y);
2217 if (IS_EXPLOSION_PROOF(element))
2220 /* indestructible elements can only explode in center (but not flames) */
2221 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2222 element == EL_FLAMES)
2227 if ((IS_INDESTRUCTIBLE(element) &&
2228 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2229 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2230 element == EL_FLAMES)
2234 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2236 if (IS_ACTIVE_BOMB(element))
2238 /* re-activate things under the bomb like gate or penguin */
2239 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2246 /* save walkable background elements while explosion on same tile */
2248 if (IS_INDESTRUCTIBLE(element))
2249 Back[x][y] = element;
2251 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2252 Back[x][y] = element;
2255 /* ignite explodable elements reached by other explosion */
2256 if (element == EL_EXPLOSION)
2257 element = Store2[x][y];
2260 if (AmoebaNr[x][y] &&
2261 (element == EL_AMOEBA_FULL ||
2262 element == EL_BD_AMOEBA ||
2263 element == EL_AMOEBA_GROWING))
2265 AmoebaCnt[AmoebaNr[x][y]]--;
2266 AmoebaCnt2[AmoebaNr[x][y]]--;
2272 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2274 switch(StorePlayer[ex][ey])
2277 Store[x][y] = EL_EMERALD_RED;
2280 Store[x][y] = EL_EMERALD;
2283 Store[x][y] = EL_EMERALD_PURPLE;
2287 Store[x][y] = EL_EMERALD_YELLOW;
2291 if (game.emulation == EMU_SUPAPLEX)
2292 Store[x][y] = EL_EMPTY;
2294 else if (center_element == EL_MOLE)
2295 Store[x][y] = EL_EMERALD_RED;
2296 else if (center_element == EL_PENGUIN)
2297 Store[x][y] = EL_EMERALD_PURPLE;
2298 else if (center_element == EL_BUG)
2299 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2300 else if (center_element == EL_BD_BUTTERFLY)
2301 Store[x][y] = EL_BD_DIAMOND;
2302 else if (center_element == EL_SP_ELECTRON)
2303 Store[x][y] = EL_SP_INFOTRON;
2304 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2305 Store[x][y] = level.amoeba_content;
2306 else if (center_element == EL_YAMYAM)
2307 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2308 else if (IS_CUSTOM_ELEMENT(center_element) &&
2309 element_info[center_element].content[xx][yy] != EL_EMPTY)
2310 Store[x][y] = element_info[center_element].content[xx][yy];
2311 else if (element == EL_WALL_EMERALD)
2312 Store[x][y] = EL_EMERALD;
2313 else if (element == EL_WALL_DIAMOND)
2314 Store[x][y] = EL_DIAMOND;
2315 else if (element == EL_WALL_BD_DIAMOND)
2316 Store[x][y] = EL_BD_DIAMOND;
2317 else if (element == EL_WALL_EMERALD_YELLOW)
2318 Store[x][y] = EL_EMERALD_YELLOW;
2319 else if (element == EL_WALL_EMERALD_RED)
2320 Store[x][y] = EL_EMERALD_RED;
2321 else if (element == EL_WALL_EMERALD_PURPLE)
2322 Store[x][y] = EL_EMERALD_PURPLE;
2323 else if (element == EL_WALL_PEARL)
2324 Store[x][y] = EL_PEARL;
2325 else if (element == EL_WALL_CRYSTAL)
2326 Store[x][y] = EL_CRYSTAL;
2327 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2328 Store[x][y] = element_info[element].content[1][1];
2330 Store[x][y] = EL_EMPTY;
2332 if (x != ex || y != ey ||
2333 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2334 Store2[x][y] = element;
2337 if (AmoebaNr[x][y] &&
2338 (element == EL_AMOEBA_FULL ||
2339 element == EL_BD_AMOEBA ||
2340 element == EL_AMOEBA_GROWING))
2342 AmoebaCnt[AmoebaNr[x][y]]--;
2343 AmoebaCnt2[AmoebaNr[x][y]]--;
2349 MovDir[x][y] = MovPos[x][y] = 0;
2350 GfxDir[x][y] = MovDir[x][y];
2355 Feld[x][y] = EL_EXPLOSION;
2357 GfxElement[x][y] = center_element;
2359 GfxElement[x][y] = EL_UNDEFINED;
2362 ExplodePhase[x][y] = 1;
2366 if (center_element == EL_YAMYAM)
2367 game.yamyam_content_nr =
2368 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2379 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2383 /* activate this even in non-DEBUG version until cause for crash in
2384 getGraphicAnimationFrame() (see below) is found and eliminated */
2388 if (GfxElement[x][y] == EL_UNDEFINED)
2391 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2392 printf("Explode(): This should never happen!\n");
2395 GfxElement[x][y] = EL_EMPTY;
2399 if (phase == first_phase_after_start)
2401 int element = Store2[x][y];
2403 if (element == EL_BLACK_ORB)
2405 Feld[x][y] = Store2[x][y];
2410 else if (phase == half_phase)
2412 int element = Store2[x][y];
2414 if (IS_PLAYER(x, y))
2415 KillHeroUnlessProtected(x, y);
2416 else if (CAN_EXPLODE_BY_FIRE(element))
2418 Feld[x][y] = Store2[x][y];
2422 else if (element == EL_AMOEBA_TO_DIAMOND)
2423 AmoebeUmwandeln(x, y);
2426 if (phase == last_phase)
2430 element = Feld[x][y] = Store[x][y];
2431 Store[x][y] = Store2[x][y] = 0;
2432 GfxElement[x][y] = EL_UNDEFINED;
2434 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2435 element = Feld[x][y] = Back[x][y];
2438 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2439 GfxDir[x][y] = MV_NO_MOVING;
2440 ChangeDelay[x][y] = 0;
2441 ChangePage[x][y] = -1;
2443 InitField(x, y, FALSE);
2444 if (CAN_MOVE(element))
2446 DrawLevelField(x, y);
2448 TestIfElementTouchesCustomElement(x, y);
2450 if (GFX_CRUMBLED(element))
2451 DrawLevelFieldCrumbledSandNeighbours(x, y);
2453 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2454 StorePlayer[x][y] = 0;
2456 if (ELEM_IS_PLAYER(element))
2457 RelocatePlayer(x, y, element);
2459 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2462 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2464 int stored = Store[x][y];
2465 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2466 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2469 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2472 DrawLevelFieldCrumbledSand(x, y);
2474 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2476 DrawLevelElement(x, y, Back[x][y]);
2477 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2479 else if (IS_WALKABLE_UNDER(Back[x][y]))
2481 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2482 DrawLevelElementThruMask(x, y, Back[x][y]);
2484 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2485 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2489 void DynaExplode(int ex, int ey)
2492 int dynabomb_size = 1;
2493 boolean dynabomb_xl = FALSE;
2494 struct PlayerInfo *player;
2495 static int xy[4][2] =
2503 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2505 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2506 dynabomb_size = player->dynabomb_size;
2507 dynabomb_xl = player->dynabomb_xl;
2508 player->dynabombs_left++;
2511 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2515 for (j=1; j<=dynabomb_size; j++)
2517 int x = ex + j * xy[i % 4][0];
2518 int y = ey + j * xy[i % 4][1];
2521 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2524 element = Feld[x][y];
2526 /* do not restart explosions of fields with active bombs */
2527 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2530 Explode(x, y, EX_PHASE_START, EX_BORDER);
2532 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2533 if (element != EL_EMPTY &&
2534 element != EL_SAND &&
2535 element != EL_EXPLOSION &&
2542 void Bang(int x, int y)
2545 int element = MovingOrBlocked2Element(x, y);
2547 int element = Feld[x][y];
2551 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2553 if (IS_PLAYER(x, y))
2556 struct PlayerInfo *player = PLAYERINFO(x, y);
2558 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2559 player->element_nr);
2564 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2566 if (game.emulation == EMU_SUPAPLEX)
2567 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2569 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2574 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2582 case EL_BD_BUTTERFLY:
2585 case EL_DARK_YAMYAM:
2589 RaiseScoreElement(element);
2590 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2592 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2593 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2594 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2595 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2596 case EL_DYNABOMB_INCREASE_NUMBER:
2597 case EL_DYNABOMB_INCREASE_SIZE:
2598 case EL_DYNABOMB_INCREASE_POWER:
2603 case EL_LAMP_ACTIVE:
2604 if (IS_PLAYER(x, y))
2605 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2607 Explode(x, y, EX_PHASE_START, EX_CENTER);
2610 if (CAN_EXPLODE_1X1(element))
2611 Explode(x, y, EX_PHASE_START, EX_CENTER);
2613 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2617 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2620 void SplashAcid(int x, int y)
2622 int element = Feld[x][y];
2624 if (element != EL_ACID_SPLASH_LEFT &&
2625 element != EL_ACID_SPLASH_RIGHT)
2627 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2629 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2630 (!IN_LEV_FIELD(x-1, y-1) ||
2631 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2632 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2634 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2635 (!IN_LEV_FIELD(x+1, y-1) ||
2636 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2637 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2641 static void InitBeltMovement()
2643 static int belt_base_element[4] =
2645 EL_CONVEYOR_BELT_1_LEFT,
2646 EL_CONVEYOR_BELT_2_LEFT,
2647 EL_CONVEYOR_BELT_3_LEFT,
2648 EL_CONVEYOR_BELT_4_LEFT
2650 static int belt_base_active_element[4] =
2652 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2653 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2654 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2655 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2660 /* set frame order for belt animation graphic according to belt direction */
2667 int element = belt_base_active_element[belt_nr] + j;
2668 int graphic = el2img(element);
2670 if (game.belt_dir[i] == MV_LEFT)
2671 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2673 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2677 for(y=0; y<lev_fieldy; y++)
2679 for(x=0; x<lev_fieldx; x++)
2681 int element = Feld[x][y];
2685 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2687 int e_belt_nr = getBeltNrFromBeltElement(element);
2690 if (e_belt_nr == belt_nr)
2692 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2694 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2702 static void ToggleBeltSwitch(int x, int y)
2704 static int belt_base_element[4] =
2706 EL_CONVEYOR_BELT_1_LEFT,
2707 EL_CONVEYOR_BELT_2_LEFT,
2708 EL_CONVEYOR_BELT_3_LEFT,
2709 EL_CONVEYOR_BELT_4_LEFT
2711 static int belt_base_active_element[4] =
2713 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2714 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2715 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2716 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2718 static int belt_base_switch_element[4] =
2720 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2721 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2722 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2723 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2725 static int belt_move_dir[4] =
2733 int element = Feld[x][y];
2734 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2735 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2736 int belt_dir = belt_move_dir[belt_dir_nr];
2739 if (!IS_BELT_SWITCH(element))
2742 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2743 game.belt_dir[belt_nr] = belt_dir;
2745 if (belt_dir_nr == 3)
2748 /* set frame order for belt animation graphic according to belt direction */
2751 int element = belt_base_active_element[belt_nr] + i;
2752 int graphic = el2img(element);
2754 if (belt_dir == MV_LEFT)
2755 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2757 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2760 for (yy=0; yy<lev_fieldy; yy++)
2762 for (xx=0; xx<lev_fieldx; xx++)
2764 int element = Feld[xx][yy];
2766 if (IS_BELT_SWITCH(element))
2768 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2770 if (e_belt_nr == belt_nr)
2772 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2773 DrawLevelField(xx, yy);
2776 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2778 int e_belt_nr = getBeltNrFromBeltElement(element);
2780 if (e_belt_nr == belt_nr)
2782 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2784 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2785 DrawLevelField(xx, yy);
2788 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2790 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2792 if (e_belt_nr == belt_nr)
2794 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2796 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2797 DrawLevelField(xx, yy);
2804 static void ToggleSwitchgateSwitch(int x, int y)
2808 game.switchgate_pos = !game.switchgate_pos;
2810 for (yy=0; yy<lev_fieldy; yy++)
2812 for (xx=0; xx<lev_fieldx; xx++)
2814 int element = Feld[xx][yy];
2816 if (element == EL_SWITCHGATE_SWITCH_UP ||
2817 element == EL_SWITCHGATE_SWITCH_DOWN)
2819 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2820 DrawLevelField(xx, yy);
2822 else if (element == EL_SWITCHGATE_OPEN ||
2823 element == EL_SWITCHGATE_OPENING)
2825 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2827 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2829 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2832 else if (element == EL_SWITCHGATE_CLOSED ||
2833 element == EL_SWITCHGATE_CLOSING)
2835 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2837 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2839 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2846 static int getInvisibleActiveFromInvisibleElement(int element)
2848 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2849 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2850 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2854 static int getInvisibleFromInvisibleActiveElement(int element)
2856 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2857 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2858 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2862 static void RedrawAllLightSwitchesAndInvisibleElements()
2866 for (y=0; y<lev_fieldy; y++)
2868 for (x=0; x<lev_fieldx; x++)
2870 int element = Feld[x][y];
2872 if (element == EL_LIGHT_SWITCH &&
2873 game.light_time_left > 0)
2875 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2876 DrawLevelField(x, y);
2878 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2879 game.light_time_left == 0)
2881 Feld[x][y] = EL_LIGHT_SWITCH;
2882 DrawLevelField(x, y);
2884 else if (element == EL_INVISIBLE_STEELWALL ||
2885 element == EL_INVISIBLE_WALL ||
2886 element == EL_INVISIBLE_SAND)
2888 if (game.light_time_left > 0)
2889 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2891 DrawLevelField(x, y);
2893 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2894 element == EL_INVISIBLE_WALL_ACTIVE ||
2895 element == EL_INVISIBLE_SAND_ACTIVE)
2897 if (game.light_time_left == 0)
2898 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2900 DrawLevelField(x, y);
2906 static void ToggleLightSwitch(int x, int y)
2908 int element = Feld[x][y];
2910 game.light_time_left =
2911 (element == EL_LIGHT_SWITCH ?
2912 level.time_light * FRAMES_PER_SECOND : 0);
2914 RedrawAllLightSwitchesAndInvisibleElements();
2917 static void ActivateTimegateSwitch(int x, int y)
2921 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2923 for (yy=0; yy<lev_fieldy; yy++)
2925 for (xx=0; xx<lev_fieldx; xx++)
2927 int element = Feld[xx][yy];
2929 if (element == EL_TIMEGATE_CLOSED ||
2930 element == EL_TIMEGATE_CLOSING)
2932 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2933 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
2937 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2939 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2940 DrawLevelField(xx, yy);
2947 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2950 inline static int getElementMoveStepsize(int x, int y)
2952 int element = Feld[x][y];
2953 int direction = MovDir[x][y];
2954 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2955 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2956 int horiz_move = (dx != 0);
2957 int sign = (horiz_move ? dx : dy);
2958 int step = sign * element_info[element].move_stepsize;
2960 /* special values for move stepsize for spring and things on conveyor belt */
2963 if (CAN_FALL(element) &&
2964 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2965 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2966 else if (element == EL_SPRING)
2967 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2973 void Impact(int x, int y)
2975 boolean lastline = (y == lev_fieldy-1);
2976 boolean object_hit = FALSE;
2977 boolean impact = (lastline || object_hit);
2978 int element = Feld[x][y];
2979 int smashed = EL_UNDEFINED;
2981 if (!lastline) /* check if element below was hit */
2983 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2986 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2987 MovDir[x][y + 1] != MV_DOWN ||
2988 MovPos[x][y + 1] <= TILEY / 2));
2990 /* do not smash moving elements that left the smashed field in time */
2991 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2992 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2996 smashed = MovingOrBlocked2Element(x, y + 1);
2998 impact = (lastline || object_hit);
3001 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3007 /* only reset graphic animation if graphic really changes after impact */
3009 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3011 ResetGfxAnimation(x, y);
3012 DrawLevelField(x, y);
3015 if (impact && CAN_EXPLODE_IMPACT(element))
3020 else if (impact && element == EL_PEARL)
3022 Feld[x][y] = EL_PEARL_BREAKING;
3023 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3026 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3028 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3033 if (impact && element == EL_AMOEBA_DROP)
3035 if (object_hit && IS_PLAYER(x, y + 1))
3036 KillHeroUnlessProtected(x, y + 1);
3037 else if (object_hit && smashed == EL_PENGUIN)
3041 Feld[x][y] = EL_AMOEBA_GROWING;
3042 Store[x][y] = EL_AMOEBA_WET;
3044 ResetRandomAnimationValue(x, y);
3049 if (object_hit) /* check which object was hit */
3051 if (CAN_PASS_MAGIC_WALL(element) &&
3052 (smashed == EL_MAGIC_WALL ||
3053 smashed == EL_BD_MAGIC_WALL))
3056 int activated_magic_wall =
3057 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3058 EL_BD_MAGIC_WALL_ACTIVE);
3060 /* activate magic wall / mill */
3061 for (yy=0; yy<lev_fieldy; yy++)
3062 for (xx=0; xx<lev_fieldx; xx++)
3063 if (Feld[xx][yy] == smashed)
3064 Feld[xx][yy] = activated_magic_wall;
3066 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3067 game.magic_wall_active = TRUE;
3069 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3070 SND_MAGIC_WALL_ACTIVATING :
3071 SND_BD_MAGIC_WALL_ACTIVATING));
3074 if (IS_PLAYER(x, y + 1))
3076 if (CAN_SMASH_PLAYER(element))
3078 KillHeroUnlessProtected(x, y + 1);
3082 else if (smashed == EL_PENGUIN)
3084 if (CAN_SMASH_PLAYER(element))
3090 else if (element == EL_BD_DIAMOND)
3092 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3098 else if ((element == EL_SP_INFOTRON ||
3099 element == EL_SP_ZONK) &&
3100 (smashed == EL_SP_SNIKSNAK ||
3101 smashed == EL_SP_ELECTRON ||
3102 smashed == EL_SP_DISK_ORANGE))
3108 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3114 else if (CAN_SMASH_EVERYTHING(element))
3116 if (IS_CLASSIC_ENEMY(smashed) ||
3117 CAN_EXPLODE_SMASHED(smashed))
3122 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3124 if (smashed == EL_LAMP ||
3125 smashed == EL_LAMP_ACTIVE)
3130 else if (smashed == EL_NUT)
3132 Feld[x][y + 1] = EL_NUT_BREAKING;
3133 PlayLevelSound(x, y, SND_NUT_BREAKING);
3134 RaiseScoreElement(EL_NUT);
3137 else if (smashed == EL_PEARL)
3139 Feld[x][y + 1] = EL_PEARL_BREAKING;
3140 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3143 else if (smashed == EL_DIAMOND)
3145 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3146 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3149 else if (IS_BELT_SWITCH(smashed))
3151 ToggleBeltSwitch(x, y + 1);
3153 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3154 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3156 ToggleSwitchgateSwitch(x, y + 1);
3158 else if (smashed == EL_LIGHT_SWITCH ||
3159 smashed == EL_LIGHT_SWITCH_ACTIVE)
3161 ToggleLightSwitch(x, y + 1);
3165 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3167 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3168 CE_OTHER_IS_SWITCHING);
3169 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3175 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3180 /* play sound of magic wall / mill */
3182 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3183 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3185 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3186 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3187 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3188 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3193 /* play sound of object that hits the ground */
3194 if (lastline || object_hit)
3195 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3198 inline static void TurnRoundExt(int x, int y)
3210 { 0, 0 }, { 0, 0 }, { 0, 0 },
3215 int left, right, back;
3219 { MV_DOWN, MV_UP, MV_RIGHT },
3220 { MV_UP, MV_DOWN, MV_LEFT },
3222 { MV_LEFT, MV_RIGHT, MV_DOWN },
3226 { MV_RIGHT, MV_LEFT, MV_UP }
3229 int element = Feld[x][y];
3230 int old_move_dir = MovDir[x][y];
3231 int left_dir = turn[old_move_dir].left;
3232 int right_dir = turn[old_move_dir].right;
3233 int back_dir = turn[old_move_dir].back;
3235 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3236 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3237 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3238 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3240 int left_x = x + left_dx, left_y = y + left_dy;
3241 int right_x = x + right_dx, right_y = y + right_dy;
3242 int move_x = x + move_dx, move_y = y + move_dy;
3246 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3248 TestIfBadThingTouchesOtherBadThing(x, y);
3250 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3251 MovDir[x][y] = right_dir;
3252 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3253 MovDir[x][y] = left_dir;
3255 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3257 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3260 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3261 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3263 TestIfBadThingTouchesOtherBadThing(x, y);
3265 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3266 MovDir[x][y] = left_dir;
3267 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3268 MovDir[x][y] = right_dir;
3270 if ((element == EL_SPACESHIP ||
3271 element == EL_SP_SNIKSNAK ||
3272 element == EL_SP_ELECTRON)
3273 && MovDir[x][y] != old_move_dir)
3275 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3278 else if (element == EL_YAMYAM)
3280 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3281 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3283 if (can_turn_left && can_turn_right)
3284 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3285 else if (can_turn_left)
3286 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3287 else if (can_turn_right)
3288 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3290 MovDir[x][y] = back_dir;
3292 MovDelay[x][y] = 16 + 16 * RND(3);
3294 else if (element == EL_DARK_YAMYAM)
3296 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3297 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3299 if (can_turn_left && can_turn_right)
3300 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3301 else if (can_turn_left)
3302 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3303 else if (can_turn_right)
3304 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3306 MovDir[x][y] = back_dir;
3308 MovDelay[x][y] = 16 + 16 * RND(3);
3310 else if (element == EL_PACMAN)
3312 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3313 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3315 if (can_turn_left && can_turn_right)
3316 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3317 else if (can_turn_left)
3318 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3319 else if (can_turn_right)
3320 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3322 MovDir[x][y] = back_dir;
3324 MovDelay[x][y] = 6 + RND(40);
3326 else if (element == EL_PIG)
3328 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3329 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3330 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3331 boolean should_turn_left, should_turn_right, should_move_on;
3333 int rnd = RND(rnd_value);
3335 should_turn_left = (can_turn_left &&
3337 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3338 y + back_dy + left_dy)));
3339 should_turn_right = (can_turn_right &&
3341 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3342 y + back_dy + right_dy)));
3343 should_move_on = (can_move_on &&
3346 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3347 y + move_dy + left_dy) ||
3348 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3349 y + move_dy + right_dy)));
3351 if (should_turn_left || should_turn_right || should_move_on)
3353 if (should_turn_left && should_turn_right && should_move_on)
3354 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3355 rnd < 2 * rnd_value / 3 ? right_dir :
3357 else if (should_turn_left && should_turn_right)
3358 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3359 else if (should_turn_left && should_move_on)
3360 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3361 else if (should_turn_right && should_move_on)
3362 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3363 else if (should_turn_left)
3364 MovDir[x][y] = left_dir;
3365 else if (should_turn_right)
3366 MovDir[x][y] = right_dir;
3367 else if (should_move_on)
3368 MovDir[x][y] = old_move_dir;
3370 else if (can_move_on && rnd > rnd_value / 8)
3371 MovDir[x][y] = old_move_dir;
3372 else if (can_turn_left && can_turn_right)
3373 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3374 else if (can_turn_left && rnd > rnd_value / 8)
3375 MovDir[x][y] = left_dir;
3376 else if (can_turn_right && rnd > rnd_value/8)
3377 MovDir[x][y] = right_dir;
3379 MovDir[x][y] = back_dir;
3381 xx = x + move_xy[MovDir[x][y]].x;
3382 yy = y + move_xy[MovDir[x][y]].y;
3384 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3385 MovDir[x][y] = old_move_dir;
3389 else if (element == EL_DRAGON)
3391 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3392 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3393 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3395 int rnd = RND(rnd_value);
3398 if (FrameCounter < 1 && x == 0 && y == 29)
3399 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3402 if (can_move_on && rnd > rnd_value / 8)
3403 MovDir[x][y] = old_move_dir;
3404 else if (can_turn_left && can_turn_right)
3405 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3406 else if (can_turn_left && rnd > rnd_value / 8)
3407 MovDir[x][y] = left_dir;
3408 else if (can_turn_right && rnd > rnd_value / 8)
3409 MovDir[x][y] = right_dir;
3411 MovDir[x][y] = back_dir;
3413 xx = x + move_xy[MovDir[x][y]].x;
3414 yy = y + move_xy[MovDir[x][y]].y;
3417 if (FrameCounter < 1 && x == 0 && y == 29)
3418 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3419 xx, yy, Feld[xx][yy],
3424 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3425 MovDir[x][y] = old_move_dir;
3427 if (!IS_FREE(xx, yy))
3428 MovDir[x][y] = old_move_dir;
3432 if (FrameCounter < 1 && x == 0 && y == 29)
3433 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3438 else if (element == EL_MOLE)
3440 boolean can_move_on =
3441 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3442 IS_AMOEBOID(Feld[move_x][move_y]) ||
3443 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3446 boolean can_turn_left =
3447 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3448 IS_AMOEBOID(Feld[left_x][left_y])));
3450 boolean can_turn_right =
3451 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3452 IS_AMOEBOID(Feld[right_x][right_y])));
3454 if (can_turn_left && can_turn_right)
3455 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3456 else if (can_turn_left)
3457 MovDir[x][y] = left_dir;
3459 MovDir[x][y] = right_dir;
3462 if (MovDir[x][y] != old_move_dir)
3465 else if (element == EL_BALLOON)
3467 MovDir[x][y] = game.balloon_dir;
3470 else if (element == EL_SPRING)
3472 if (MovDir[x][y] & MV_HORIZONTAL &&
3473 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3474 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3475 MovDir[x][y] = MV_NO_MOVING;
3479 else if (element == EL_ROBOT ||
3480 element == EL_SATELLITE ||
3481 element == EL_PENGUIN)
3483 int attr_x = -1, attr_y = -1;
3494 for (i=0; i<MAX_PLAYERS; i++)
3496 struct PlayerInfo *player = &stored_player[i];
3497 int jx = player->jx, jy = player->jy;
3499 if (!player->active)
3503 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3511 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3517 if (element == EL_PENGUIN)
3520 static int xy[4][2] =
3530 int ex = x + xy[i % 4][0];
3531 int ey = y + xy[i % 4][1];
3533 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3542 MovDir[x][y] = MV_NO_MOVING;
3544 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3545 else if (attr_x > x)
3546 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3548 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3549 else if (attr_y > y)
3550 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3552 if (element == EL_ROBOT)
3556 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3557 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3558 Moving2Blocked(x, y, &newx, &newy);
3560 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3561 MovDelay[x][y] = 8 + 8 * !RND(3);
3563 MovDelay[x][y] = 16;
3565 else if (element == EL_PENGUIN)
3571 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3573 boolean first_horiz = RND(2);
3574 int new_move_dir = MovDir[x][y];
3577 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3578 Moving2Blocked(x, y, &newx, &newy);
3580 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3584 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3585 Moving2Blocked(x, y, &newx, &newy);
3587 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3590 MovDir[x][y] = old_move_dir;
3594 else /* (element == EL_SATELLITE) */
3600 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3602 boolean first_horiz = RND(2);
3603 int new_move_dir = MovDir[x][y];
3606 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3607 Moving2Blocked(x, y, &newx, &newy);
3609 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3613 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3614 Moving2Blocked(x, y, &newx, &newy);
3616 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3619 MovDir[x][y] = old_move_dir;
3624 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3625 element_info[element].move_pattern == MV_TURNING_LEFT ||
3626 element_info[element].move_pattern == MV_TURNING_RIGHT)
3628 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3629 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3631 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3632 MovDir[x][y] = left_dir;
3633 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3634 MovDir[x][y] = right_dir;
3635 else if (can_turn_left && can_turn_right)
3636 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3637 else if (can_turn_left)
3638 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3639 else if (can_turn_right)
3640 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3642 MovDir[x][y] = back_dir;
3644 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3646 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3647 element_info[element].move_pattern == MV_VERTICAL)
3649 if (element_info[element].move_pattern & old_move_dir)
3650 MovDir[x][y] = back_dir;
3651 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3652 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3653 else if (element_info[element].move_pattern == MV_VERTICAL)
3654 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3656 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3658 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3660 MovDir[x][y] = element_info[element].move_pattern;
3661 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3663 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3665 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3666 MovDir[x][y] = left_dir;
3667 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3668 MovDir[x][y] = right_dir;
3670 if (MovDir[x][y] != old_move_dir)
3671 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3673 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3675 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3676 MovDir[x][y] = right_dir;
3677 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3678 MovDir[x][y] = left_dir;
3680 if (MovDir[x][y] != old_move_dir)
3681 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3683 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3684 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3686 int attr_x = -1, attr_y = -1;
3689 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3700 for (i=0; i<MAX_PLAYERS; i++)
3702 struct PlayerInfo *player = &stored_player[i];
3703 int jx = player->jx, jy = player->jy;
3705 if (!player->active)
3709 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3717 MovDir[x][y] = MV_NO_MOVING;
3719 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3720 else if (attr_x > x)
3721 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3723 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3724 else if (attr_y > y)
3725 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3727 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3729 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3731 boolean first_horiz = RND(2);
3732 int new_move_dir = MovDir[x][y];
3735 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3736 Moving2Blocked(x, y, &newx, &newy);
3738 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3742 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3743 Moving2Blocked(x, y, &newx, &newy);
3745 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3748 MovDir[x][y] = old_move_dir;
3751 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3753 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3754 MovDir[x][y] = MV_NO_MOVING;
3760 static void TurnRound(int x, int y)
3762 int direction = MovDir[x][y];
3765 GfxDir[x][y] = MovDir[x][y];
3771 GfxDir[x][y] = MovDir[x][y];
3774 if (direction != MovDir[x][y])
3779 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3782 GfxAction[x][y] = ACTION_WAITING;
3786 static boolean JustBeingPushed(int x, int y)
3790 for (i=0; i<MAX_PLAYERS; i++)
3792 struct PlayerInfo *player = &stored_player[i];
3794 if (player->active && player->is_pushing && player->MovPos)
3796 int next_jx = player->jx + (player->jx - player->last_jx);
3797 int next_jy = player->jy + (player->jy - player->last_jy);
3799 if (x == next_jx && y == next_jy)
3807 void StartMoving(int x, int y)
3809 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3810 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3811 int element = Feld[x][y];
3817 if (MovDelay[x][y] == 0)
3818 GfxAction[x][y] = ACTION_DEFAULT;
3820 /* !!! this should be handled more generic (not only for mole) !!! */
3821 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3822 GfxAction[x][y] = ACTION_DEFAULT;
3825 if (CAN_FALL(element) && y < lev_fieldy - 1)
3827 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3828 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3829 if (JustBeingPushed(x, y))
3832 if (element == EL_QUICKSAND_FULL)
3834 if (IS_FREE(x, y + 1))
3836 InitMovingField(x, y, MV_DOWN);
3837 started_moving = TRUE;
3839 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3840 Store[x][y] = EL_ROCK;
3842 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
3844 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
3847 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3849 if (!MovDelay[x][y])
3850 MovDelay[x][y] = TILEY + 1;
3859 Feld[x][y] = EL_QUICKSAND_EMPTY;
3860 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3861 Store[x][y + 1] = Store[x][y];
3864 PlayLevelSoundAction(x, y, ACTION_FILLING);
3866 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
3870 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3871 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3873 InitMovingField(x, y, MV_DOWN);
3874 started_moving = TRUE;
3876 Feld[x][y] = EL_QUICKSAND_FILLING;
3877 Store[x][y] = element;
3879 PlayLevelSoundAction(x, y, ACTION_FILLING);
3881 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
3884 else if (element == EL_MAGIC_WALL_FULL)
3886 if (IS_FREE(x, y + 1))
3888 InitMovingField(x, y, MV_DOWN);
3889 started_moving = TRUE;
3891 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3892 Store[x][y] = EL_CHANGED(Store[x][y]);
3894 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3896 if (!MovDelay[x][y])
3897 MovDelay[x][y] = TILEY/4 + 1;
3906 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3907 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3908 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3912 else if (element == EL_BD_MAGIC_WALL_FULL)
3914 if (IS_FREE(x, y + 1))
3916 InitMovingField(x, y, MV_DOWN);
3917 started_moving = TRUE;
3919 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3920 Store[x][y] = EL_CHANGED2(Store[x][y]);
3922 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3924 if (!MovDelay[x][y])
3925 MovDelay[x][y] = TILEY/4 + 1;
3934 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3935 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3936 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3940 else if (CAN_PASS_MAGIC_WALL(element) &&
3941 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3942 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3944 InitMovingField(x, y, MV_DOWN);
3945 started_moving = TRUE;
3948 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3949 EL_BD_MAGIC_WALL_FILLING);
3950 Store[x][y] = element;
3953 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3955 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3960 InitMovingField(x, y, MV_DOWN);
3961 started_moving = TRUE;
3963 Store[x][y] = EL_ACID;
3965 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3966 GfxAction[x][y + 1] = ACTION_ACTIVE;
3970 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3971 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3972 (Feld[x][y + 1] == EL_BLOCKED)) ||
3973 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3974 CAN_SMASH(element) && WasJustFalling[x][y] &&
3975 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3979 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3980 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3981 WasJustMoving[x][y] && !Pushed[x][y + 1])
3983 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3984 WasJustMoving[x][y])
3989 /* this is needed for a special case not covered by calling "Impact()"
3990 from "ContinueMoving()": if an element moves to a tile directly below
3991 another element which was just falling on that tile (which was empty
3992 in the previous frame), the falling element above would just stop
3993 instead of smashing the element below (in previous version, the above
3994 element was just checked for "moving" instead of "falling", resulting
3995 in incorrect smashes caused by horizontal movement of the above
3996 element; also, the case of the player being the element to smash was
3997 simply not covered here... :-/ ) */
4001 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4003 if (MovDir[x][y] == MV_NO_MOVING)
4005 InitMovingField(x, y, MV_DOWN);
4006 started_moving = TRUE;
4009 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4011 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4012 MovDir[x][y] = MV_DOWN;
4014 InitMovingField(x, y, MV_DOWN);
4015 started_moving = TRUE;
4017 else if (element == EL_AMOEBA_DROP)
4019 Feld[x][y] = EL_AMOEBA_GROWING;
4020 Store[x][y] = EL_AMOEBA_WET;
4022 /* Store[x][y + 1] must be zero, because:
4023 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4026 #if OLD_GAME_BEHAVIOUR
4027 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4029 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4030 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4031 element != EL_DX_SUPABOMB)
4034 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4035 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4036 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4037 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4040 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4041 (IS_FREE(x - 1, y + 1) ||
4042 Feld[x - 1][y + 1] == EL_ACID));
4043 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4044 (IS_FREE(x + 1, y + 1) ||
4045 Feld[x + 1][y + 1] == EL_ACID));
4046 boolean can_fall_any = (can_fall_left || can_fall_right);
4047 boolean can_fall_both = (can_fall_left && can_fall_right);
4049 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4051 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4053 if (slippery_type == SLIPPERY_ONLY_LEFT)
4054 can_fall_right = FALSE;
4055 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4056 can_fall_left = FALSE;
4057 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4058 can_fall_right = FALSE;
4059 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4060 can_fall_left = FALSE;
4062 can_fall_any = (can_fall_left || can_fall_right);
4063 can_fall_both = (can_fall_left && can_fall_right);
4068 if (can_fall_both &&
4069 (game.emulation != EMU_BOULDERDASH &&
4070 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4071 can_fall_left = !(can_fall_right = RND(2));
4073 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4074 started_moving = TRUE;
4077 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4079 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4080 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4081 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4082 int belt_dir = game.belt_dir[belt_nr];
4084 if ((belt_dir == MV_LEFT && left_is_free) ||
4085 (belt_dir == MV_RIGHT && right_is_free))
4087 InitMovingField(x, y, belt_dir);
4088 started_moving = TRUE;
4090 GfxAction[x][y] = ACTION_DEFAULT;
4095 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4096 if (CAN_MOVE(element) && !started_moving)
4101 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4104 if ((element == EL_SATELLITE ||
4105 element == EL_BALLOON ||
4106 element == EL_SPRING)
4107 && JustBeingPushed(x, y))
4113 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4114 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4116 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4118 Moving2Blocked(x, y, &newx, &newy);
4119 if (Feld[newx][newy] == EL_BLOCKED)
4120 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4126 if (FrameCounter < 1 && x == 0 && y == 29)
4127 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4130 if (!MovDelay[x][y]) /* start new movement phase */
4132 /* all objects that can change their move direction after each step
4133 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4135 if (element != EL_YAMYAM &&
4136 element != EL_DARK_YAMYAM &&
4137 element != EL_PACMAN &&
4138 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4139 element_info[element].move_pattern != MV_TURNING_LEFT &&
4140 element_info[element].move_pattern != MV_TURNING_RIGHT)
4145 if (FrameCounter < 1 && x == 0 && y == 29)
4146 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4149 if (MovDelay[x][y] && (element == EL_BUG ||
4150 element == EL_SPACESHIP ||
4151 element == EL_SP_SNIKSNAK ||
4152 element == EL_SP_ELECTRON ||
4153 element == EL_MOLE))
4154 DrawLevelField(x, y);
4158 if (MovDelay[x][y]) /* wait some time before next movement */
4163 if (element == EL_YAMYAM)
4166 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4167 DrawLevelElementAnimation(x, y, element);
4171 if (MovDelay[x][y]) /* element still has to wait some time */
4174 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4175 ResetGfxAnimation(x, y);
4179 if (GfxAction[x][y] != ACTION_WAITING)
4180 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4182 GfxAction[x][y] = ACTION_WAITING;
4186 if (element == EL_ROBOT ||
4188 element == EL_PACMAN ||
4190 element == EL_YAMYAM ||
4191 element == EL_DARK_YAMYAM)
4194 DrawLevelElementAnimation(x, y, element);
4196 DrawLevelElementAnimationIfNeeded(x, y, element);
4198 PlayLevelSoundAction(x, y, ACTION_WAITING);
4200 else if (element == EL_SP_ELECTRON)
4201 DrawLevelElementAnimationIfNeeded(x, y, element);
4202 else if (element == EL_DRAGON)
4205 int dir = MovDir[x][y];
4206 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4207 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4208 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4209 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4210 dir == MV_UP ? IMG_FLAMES_1_UP :
4211 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4212 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4215 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4218 GfxAction[x][y] = ACTION_ATTACKING;
4220 if (IS_PLAYER(x, y))
4221 DrawPlayerField(x, y);
4223 DrawLevelField(x, y);
4225 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4227 for (i=1; i <= 3; i++)
4229 int xx = x + i * dx;
4230 int yy = y + i * dy;
4231 int sx = SCREENX(xx);
4232 int sy = SCREENY(yy);
4233 int flame_graphic = graphic + (i - 1);
4235 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4240 int flamed = MovingOrBlocked2Element(xx, yy);
4242 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4245 RemoveMovingField(xx, yy);
4247 Feld[xx][yy] = EL_FLAMES;
4248 if (IN_SCR_FIELD(sx, sy))
4250 DrawLevelFieldCrumbledSand(xx, yy);
4251 DrawGraphic(sx, sy, flame_graphic, frame);
4256 if (Feld[xx][yy] == EL_FLAMES)
4257 Feld[xx][yy] = EL_EMPTY;
4258 DrawLevelField(xx, yy);
4263 if (MovDelay[x][y]) /* element still has to wait some time */
4265 PlayLevelSoundAction(x, y, ACTION_WAITING);
4271 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4272 for all other elements GfxAction will be set by InitMovingField() */
4273 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4274 GfxAction[x][y] = ACTION_MOVING;
4278 /* now make next step */
4280 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4282 if (DONT_COLLIDE_WITH(element) &&
4283 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4284 !PLAYER_PROTECTED(newx, newy))
4287 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4290 /* player killed by element which is deadly when colliding with */
4292 KillHero(PLAYERINFO(newx, newy));
4297 else if ((element == EL_PENGUIN ||
4298 element == EL_ROBOT ||
4299 element == EL_SATELLITE ||
4300 element == EL_BALLOON ||
4301 IS_CUSTOM_ELEMENT(element)) &&
4302 IN_LEV_FIELD(newx, newy) &&
4303 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4306 Store[x][y] = EL_ACID;
4308 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4310 if (Feld[newx][newy] == EL_EXIT_OPEN)
4312 Feld[x][y] = EL_EMPTY;
4313 DrawLevelField(x, y);
4315 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4316 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4317 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4319 local_player->friends_still_needed--;
4320 if (!local_player->friends_still_needed &&
4321 !local_player->GameOver && AllPlayersGone)
4322 local_player->LevelSolved = local_player->GameOver = TRUE;
4326 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4328 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4329 DrawLevelField(newx, newy);
4331 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4333 else if (!IS_FREE(newx, newy))
4335 GfxAction[x][y] = ACTION_WAITING;
4337 if (IS_PLAYER(x, y))
4338 DrawPlayerField(x, y);
4340 DrawLevelField(x, y);
4344 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4346 if (IS_FOOD_PIG(Feld[newx][newy]))
4348 if (IS_MOVING(newx, newy))
4349 RemoveMovingField(newx, newy);
4352 Feld[newx][newy] = EL_EMPTY;
4353 DrawLevelField(newx, newy);
4356 PlayLevelSound(x, y, SND_PIG_DIGGING);
4358 else if (!IS_FREE(newx, newy))
4360 if (IS_PLAYER(x, y))
4361 DrawPlayerField(x, y);
4363 DrawLevelField(x, y);
4367 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4369 if (!IS_FREE(newx, newy))
4371 if (IS_PLAYER(x, y))
4372 DrawPlayerField(x, y);
4374 DrawLevelField(x, y);
4380 boolean wanna_flame = !RND(10);
4381 int dx = newx - x, dy = newy - y;
4382 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4383 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4384 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4385 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4386 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4387 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4390 IS_CLASSIC_ENEMY(element1) ||
4391 IS_CLASSIC_ENEMY(element2)) &&
4392 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4393 element1 != EL_FLAMES && element2 != EL_FLAMES)
4396 ResetGfxAnimation(x, y);
4397 GfxAction[x][y] = ACTION_ATTACKING;
4400 if (IS_PLAYER(x, y))
4401 DrawPlayerField(x, y);
4403 DrawLevelField(x, y);
4405 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4407 MovDelay[x][y] = 50;
4409 Feld[newx][newy] = EL_FLAMES;
4410 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4411 Feld[newx1][newy1] = EL_FLAMES;
4412 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4413 Feld[newx2][newy2] = EL_FLAMES;
4419 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4420 Feld[newx][newy] == EL_DIAMOND)
4422 if (IS_MOVING(newx, newy))
4423 RemoveMovingField(newx, newy);
4426 Feld[newx][newy] = EL_EMPTY;
4427 DrawLevelField(newx, newy);
4430 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4432 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4433 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4435 if (AmoebaNr[newx][newy])
4437 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4438 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4439 Feld[newx][newy] == EL_BD_AMOEBA)
4440 AmoebaCnt[AmoebaNr[newx][newy]]--;
4443 if (IS_MOVING(newx, newy))
4444 RemoveMovingField(newx, newy);
4447 Feld[newx][newy] = EL_EMPTY;
4448 DrawLevelField(newx, newy);
4451 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4453 else if ((element == EL_PACMAN || element == EL_MOLE)
4454 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4456 if (AmoebaNr[newx][newy])
4458 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4459 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4460 Feld[newx][newy] == EL_BD_AMOEBA)
4461 AmoebaCnt[AmoebaNr[newx][newy]]--;
4464 if (element == EL_MOLE)
4466 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4467 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4469 ResetGfxAnimation(x, y);
4470 GfxAction[x][y] = ACTION_DIGGING;
4471 DrawLevelField(x, y);
4473 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4474 return; /* wait for shrinking amoeba */
4476 else /* element == EL_PACMAN */
4478 Feld[newx][newy] = EL_EMPTY;
4479 DrawLevelField(newx, newy);
4480 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4483 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4484 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4485 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4487 /* wait for shrinking amoeba to completely disappear */
4490 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4492 /* object was running against a wall */
4497 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4498 DrawLevelElementAnimation(x, y, element);
4500 if (element == EL_BUG ||
4501 element == EL_SPACESHIP ||
4502 element == EL_SP_SNIKSNAK)
4503 DrawLevelField(x, y);
4504 else if (element == EL_MOLE)
4505 DrawLevelField(x, y);
4506 else if (element == EL_BD_BUTTERFLY ||
4507 element == EL_BD_FIREFLY)
4508 DrawLevelElementAnimationIfNeeded(x, y, element);
4509 else if (element == EL_SATELLITE)
4510 DrawLevelElementAnimationIfNeeded(x, y, element);
4511 else if (element == EL_SP_ELECTRON)
4512 DrawLevelElementAnimationIfNeeded(x, y, element);
4515 if (DONT_TOUCH(element))
4516 TestIfBadThingTouchesHero(x, y);
4519 PlayLevelSoundAction(x, y, ACTION_WAITING);
4525 InitMovingField(x, y, MovDir[x][y]);
4527 PlayLevelSoundAction(x, y, ACTION_MOVING);
4531 ContinueMoving(x, y);
4534 void ContinueMoving(int x, int y)
4536 int element = Feld[x][y];
4537 int direction = MovDir[x][y];
4538 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4539 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4540 int newx = x + dx, newy = y + dy;
4541 int nextx = newx + dx, nexty = newy + dy;
4542 boolean pushed = Pushed[x][y];
4544 MovPos[x][y] += getElementMoveStepsize(x, y);
4546 if (pushed) /* special case: moving object pushed by player */
4547 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4549 if (ABS(MovPos[x][y]) < TILEX)
4551 DrawLevelField(x, y);
4553 return; /* element is still moving */
4556 /* element reached destination field */
4558 Feld[x][y] = EL_EMPTY;
4559 Feld[newx][newy] = element;
4560 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4562 if (element == EL_MOLE)
4564 Feld[x][y] = EL_SAND;
4566 DrawLevelFieldCrumbledSandNeighbours(x, y);
4568 else if (element == EL_QUICKSAND_FILLING)
4570 element = Feld[newx][newy] = get_next_element(element);
4571 Store[newx][newy] = Store[x][y];
4573 else if (element == EL_QUICKSAND_EMPTYING)
4575 Feld[x][y] = get_next_element(element);
4576 element = Feld[newx][newy] = Store[x][y];
4578 else if (element == EL_MAGIC_WALL_FILLING)
4580 element = Feld[newx][newy] = get_next_element(element);
4581 if (!game.magic_wall_active)
4582 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4583 Store[newx][newy] = Store[x][y];
4585 else if (element == EL_MAGIC_WALL_EMPTYING)
4587 Feld[x][y] = get_next_element(element);
4588 if (!game.magic_wall_active)
4589 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4590 element = Feld[newx][newy] = Store[x][y];
4592 else if (element == EL_BD_MAGIC_WALL_FILLING)
4594 element = Feld[newx][newy] = get_next_element(element);
4595 if (!game.magic_wall_active)
4596 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4597 Store[newx][newy] = Store[x][y];
4599 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4601 Feld[x][y] = get_next_element(element);
4602 if (!game.magic_wall_active)
4603 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4604 element = Feld[newx][newy] = Store[x][y];
4606 else if (element == EL_AMOEBA_DROPPING)
4608 Feld[x][y] = get_next_element(element);
4609 element = Feld[newx][newy] = Store[x][y];
4611 else if (element == EL_SOKOBAN_OBJECT)
4614 Feld[x][y] = Back[x][y];
4616 if (Back[newx][newy])
4617 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4619 Back[x][y] = Back[newx][newy] = 0;
4621 else if (Store[x][y] == EL_ACID)
4623 element = Feld[newx][newy] = EL_ACID;
4627 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4628 MovDelay[newx][newy] = 0;
4630 /* copy element change control values to new field */
4631 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4632 ChangePage[newx][newy] = ChangePage[x][y];
4633 Changed[newx][newy] = Changed[x][y];
4634 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4636 ChangeDelay[x][y] = 0;
4637 ChangePage[x][y] = -1;
4638 Changed[x][y] = CE_BITMASK_DEFAULT;
4639 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4641 /* copy animation control values to new field */
4642 GfxFrame[newx][newy] = GfxFrame[x][y];
4643 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4644 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4645 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4647 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4649 ResetGfxAnimation(x, y); /* reset animation values for old field */
4652 /* 2.1.1 (does not work correctly for spring) */
4653 if (!CAN_MOVE(element))
4654 MovDir[newx][newy] = 0;
4658 /* (does not work for falling objects that slide horizontally) */
4659 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4660 MovDir[newx][newy] = 0;
4663 if (!CAN_MOVE(element) ||
4664 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4665 MovDir[newx][newy] = 0;
4668 if (!CAN_MOVE(element) ||
4669 (CAN_FALL(element) && direction == MV_DOWN))
4670 GfxDir[x][y] = MovDir[newx][newy] = 0;
4675 DrawLevelField(x, y);
4676 DrawLevelField(newx, newy);
4678 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4680 /* prevent pushed element from moving on in pushed direction */
4681 if (pushed && CAN_MOVE(element) &&
4682 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4683 !(element_info[element].move_pattern & direction))
4684 TurnRound(newx, newy);
4686 if (!pushed) /* special case: moving object pushed by player */
4688 WasJustMoving[newx][newy] = 3;
4690 if (CAN_FALL(element) && direction == MV_DOWN)
4691 WasJustFalling[newx][newy] = 3;
4694 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4696 TestIfBadThingTouchesHero(newx, newy);
4697 TestIfBadThingTouchesFriend(newx, newy);
4699 if (!IS_CUSTOM_ELEMENT(element))
4700 TestIfBadThingTouchesOtherBadThing(newx, newy);
4702 else if (element == EL_PENGUIN)
4703 TestIfFriendTouchesBadThing(newx, newy);
4705 if (CAN_FALL(element) && direction == MV_DOWN &&
4706 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4710 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4714 if (ChangePage[newx][newy] != -1) /* delayed change */
4715 ChangeElement(newx, newy, ChangePage[newx][newy]);
4718 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4719 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4722 TestIfPlayerTouchesCustomElement(newx, newy);
4723 TestIfElementTouchesCustomElement(newx, newy);
4726 int AmoebeNachbarNr(int ax, int ay)
4729 int element = Feld[ax][ay];
4731 static int xy[4][2] =
4741 int x = ax + xy[i][0];
4742 int y = ay + xy[i][1];
4744 if (!IN_LEV_FIELD(x, y))
4747 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4748 group_nr = AmoebaNr[x][y];
4754 void AmoebenVereinigen(int ax, int ay)
4756 int i, x, y, xx, yy;
4757 int new_group_nr = AmoebaNr[ax][ay];
4758 static int xy[4][2] =
4766 if (new_group_nr == 0)
4774 if (!IN_LEV_FIELD(x, y))
4777 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4778 Feld[x][y] == EL_BD_AMOEBA ||
4779 Feld[x][y] == EL_AMOEBA_DEAD) &&
4780 AmoebaNr[x][y] != new_group_nr)
4782 int old_group_nr = AmoebaNr[x][y];
4784 if (old_group_nr == 0)
4787 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4788 AmoebaCnt[old_group_nr] = 0;
4789 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4790 AmoebaCnt2[old_group_nr] = 0;
4792 for (yy=0; yy<lev_fieldy; yy++)
4794 for (xx=0; xx<lev_fieldx; xx++)
4796 if (AmoebaNr[xx][yy] == old_group_nr)
4797 AmoebaNr[xx][yy] = new_group_nr;
4804 void AmoebeUmwandeln(int ax, int ay)
4808 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4810 int group_nr = AmoebaNr[ax][ay];
4815 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4816 printf("AmoebeUmwandeln(): This should never happen!\n");
4821 for (y=0; y<lev_fieldy; y++)
4823 for (x=0; x<lev_fieldx; x++)
4825 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4828 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4832 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
4833 SND_AMOEBA_TURNING_TO_GEM :
4834 SND_AMOEBA_TURNING_TO_ROCK));
4839 static int xy[4][2] =
4852 if (!IN_LEV_FIELD(x, y))
4855 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4857 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
4858 SND_AMOEBA_TURNING_TO_GEM :
4859 SND_AMOEBA_TURNING_TO_ROCK));
4866 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4869 int group_nr = AmoebaNr[ax][ay];
4870 boolean done = FALSE;
4875 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4876 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4881 for (y=0; y<lev_fieldy; y++)
4883 for (x=0; x<lev_fieldx; x++)
4885 if (AmoebaNr[x][y] == group_nr &&
4886 (Feld[x][y] == EL_AMOEBA_DEAD ||
4887 Feld[x][y] == EL_BD_AMOEBA ||
4888 Feld[x][y] == EL_AMOEBA_GROWING))
4891 Feld[x][y] = new_element;
4892 InitField(x, y, FALSE);
4893 DrawLevelField(x, y);
4900 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
4901 SND_BD_AMOEBA_TURNING_TO_ROCK :
4902 SND_BD_AMOEBA_TURNING_TO_GEM));
4905 void AmoebeWaechst(int x, int y)
4907 static unsigned long sound_delay = 0;
4908 static unsigned long sound_delay_value = 0;
4910 if (!MovDelay[x][y]) /* start new growing cycle */
4914 if (DelayReached(&sound_delay, sound_delay_value))
4917 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
4919 if (Store[x][y] == EL_BD_AMOEBA)
4920 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
4922 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
4924 sound_delay_value = 30;
4928 if (MovDelay[x][y]) /* wait some time before growing bigger */
4931 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4933 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4934 6 - MovDelay[x][y]);
4936 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4939 if (!MovDelay[x][y])
4941 Feld[x][y] = Store[x][y];
4943 DrawLevelField(x, y);
4948 void AmoebaDisappearing(int x, int y)
4950 static unsigned long sound_delay = 0;
4951 static unsigned long sound_delay_value = 0;
4953 if (!MovDelay[x][y]) /* start new shrinking cycle */
4957 if (DelayReached(&sound_delay, sound_delay_value))
4958 sound_delay_value = 30;
4961 if (MovDelay[x][y]) /* wait some time before shrinking */
4964 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4966 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4967 6 - MovDelay[x][y]);
4969 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4972 if (!MovDelay[x][y])
4974 Feld[x][y] = EL_EMPTY;
4975 DrawLevelField(x, y);
4977 /* don't let mole enter this field in this cycle;
4978 (give priority to objects falling to this field from above) */
4984 void AmoebeAbleger(int ax, int ay)
4987 int element = Feld[ax][ay];
4988 int graphic = el2img(element);
4989 int newax = ax, neway = ay;
4990 static int xy[4][2] =
4998 if (!level.amoeba_speed)
5000 Feld[ax][ay] = EL_AMOEBA_DEAD;
5001 DrawLevelField(ax, ay);
5005 if (IS_ANIMATED(graphic))
5006 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5008 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5009 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5011 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5014 if (MovDelay[ax][ay])
5018 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5021 int x = ax + xy[start][0];
5022 int y = ay + xy[start][1];
5024 if (!IN_LEV_FIELD(x, y))
5027 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5028 if (IS_FREE(x, y) ||
5029 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5035 if (newax == ax && neway == ay)
5038 else /* normal or "filled" (BD style) amoeba */
5041 boolean waiting_for_player = FALSE;
5045 int j = (start + i) % 4;
5046 int x = ax + xy[j][0];
5047 int y = ay + xy[j][1];
5049 if (!IN_LEV_FIELD(x, y))
5052 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5053 if (IS_FREE(x, y) ||
5054 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5060 else if (IS_PLAYER(x, y))
5061 waiting_for_player = TRUE;
5064 if (newax == ax && neway == ay) /* amoeba cannot grow */
5066 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5068 Feld[ax][ay] = EL_AMOEBA_DEAD;
5069 DrawLevelField(ax, ay);
5070 AmoebaCnt[AmoebaNr[ax][ay]]--;
5072 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5074 if (element == EL_AMOEBA_FULL)
5075 AmoebeUmwandeln(ax, ay);
5076 else if (element == EL_BD_AMOEBA)
5077 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5082 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5084 /* amoeba gets larger by growing in some direction */
5086 int new_group_nr = AmoebaNr[ax][ay];
5089 if (new_group_nr == 0)
5091 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5092 printf("AmoebeAbleger(): This should never happen!\n");
5097 AmoebaNr[newax][neway] = new_group_nr;
5098 AmoebaCnt[new_group_nr]++;
5099 AmoebaCnt2[new_group_nr]++;
5101 /* if amoeba touches other amoeba(s) after growing, unify them */
5102 AmoebenVereinigen(newax, neway);
5104 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5106 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5112 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5113 (neway == lev_fieldy - 1 && newax != ax))
5115 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5116 Store[newax][neway] = element;
5118 else if (neway == ay)
5120 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5122 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5124 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5129 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5130 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5131 Store[ax][ay] = EL_AMOEBA_DROP;
5132 ContinueMoving(ax, ay);
5136 DrawLevelField(newax, neway);
5139 void Life(int ax, int ay)
5142 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5144 int element = Feld[ax][ay];
5145 int graphic = el2img(element);
5146 boolean changed = FALSE;
5148 if (IS_ANIMATED(graphic))
5149 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5154 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5155 MovDelay[ax][ay] = life_time;
5157 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5160 if (MovDelay[ax][ay])
5164 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5166 int xx = ax+x1, yy = ay+y1;
5169 if (!IN_LEV_FIELD(xx, yy))
5172 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5174 int x = xx+x2, y = yy+y2;
5176 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5179 if (((Feld[x][y] == element ||
5180 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5182 (IS_FREE(x, y) && Stop[x][y]))
5186 if (xx == ax && yy == ay) /* field in the middle */
5188 if (nachbarn < life[0] || nachbarn > life[1])
5190 Feld[xx][yy] = EL_EMPTY;
5192 DrawLevelField(xx, yy);
5193 Stop[xx][yy] = TRUE;
5197 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5198 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5199 { /* free border field */
5200 if (nachbarn >= life[2] && nachbarn <= life[3])
5202 Feld[xx][yy] = element;
5203 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5205 DrawLevelField(xx, yy);
5206 Stop[xx][yy] = TRUE;
5213 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5214 SND_GAME_OF_LIFE_GROWING);
5217 static void InitRobotWheel(int x, int y)
5219 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5222 static void RunRobotWheel(int x, int y)
5224 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5227 static void StopRobotWheel(int x, int y)
5229 if (ZX == x && ZY == y)
5233 static void InitTimegateWheel(int x, int y)
5235 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5238 static void RunTimegateWheel(int x, int y)
5240 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5243 void CheckExit(int x, int y)
5245 if (local_player->gems_still_needed > 0 ||
5246 local_player->sokobanfields_still_needed > 0 ||
5247 local_player->lights_still_needed > 0)
5249 int element = Feld[x][y];
5250 int graphic = el2img(element);
5252 if (IS_ANIMATED(graphic))
5253 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5258 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5261 Feld[x][y] = EL_EXIT_OPENING;
5263 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5266 void CheckExitSP(int x, int y)
5268 if (local_player->gems_still_needed > 0)
5270 int element = Feld[x][y];
5271 int graphic = el2img(element);
5273 if (IS_ANIMATED(graphic))
5274 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5279 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5282 Feld[x][y] = EL_SP_EXIT_OPENING;
5284 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5287 static void CloseAllOpenTimegates()
5291 for (y=0; y<lev_fieldy; y++)
5293 for (x=0; x<lev_fieldx; x++)
5295 int element = Feld[x][y];
5297 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5299 Feld[x][y] = EL_TIMEGATE_CLOSING;
5301 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5303 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5310 void EdelsteinFunkeln(int x, int y)
5312 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5315 if (Feld[x][y] == EL_BD_DIAMOND)
5318 if (MovDelay[x][y] == 0) /* next animation frame */
5319 MovDelay[x][y] = 11 * !SimpleRND(500);
5321 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5325 if (setup.direct_draw && MovDelay[x][y])
5326 SetDrawtoField(DRAW_BUFFERED);
5328 DrawLevelElementAnimation(x, y, Feld[x][y]);
5330 if (MovDelay[x][y] != 0)
5332 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5333 10 - MovDelay[x][y]);
5335 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5337 if (setup.direct_draw)
5341 dest_x = FX + SCREENX(x) * TILEX;
5342 dest_y = FY + SCREENY(y) * TILEY;
5344 BlitBitmap(drawto_field, window,
5345 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5346 SetDrawtoField(DRAW_DIRECT);
5352 void MauerWaechst(int x, int y)
5356 if (!MovDelay[x][y]) /* next animation frame */
5357 MovDelay[x][y] = 3 * delay;
5359 if (MovDelay[x][y]) /* wait some time before next frame */
5363 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5365 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5366 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5368 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5371 if (!MovDelay[x][y])
5373 if (MovDir[x][y] == MV_LEFT)
5375 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5376 DrawLevelField(x - 1, y);
5378 else if (MovDir[x][y] == MV_RIGHT)
5380 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5381 DrawLevelField(x + 1, y);
5383 else if (MovDir[x][y] == MV_UP)
5385 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5386 DrawLevelField(x, y - 1);
5390 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5391 DrawLevelField(x, y + 1);
5394 Feld[x][y] = Store[x][y];
5396 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5397 DrawLevelField(x, y);
5402 void MauerAbleger(int ax, int ay)
5404 int element = Feld[ax][ay];
5405 int graphic = el2img(element);
5406 boolean oben_frei = FALSE, unten_frei = FALSE;
5407 boolean links_frei = FALSE, rechts_frei = FALSE;
5408 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5409 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5410 boolean new_wall = FALSE;
5412 if (IS_ANIMATED(graphic))
5413 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5415 if (!MovDelay[ax][ay]) /* start building new wall */
5416 MovDelay[ax][ay] = 6;
5418 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5421 if (MovDelay[ax][ay])
5425 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5427 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5429 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5431 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5434 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5435 element == EL_EXPANDABLE_WALL_ANY)
5439 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5440 Store[ax][ay-1] = element;
5441 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5442 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5443 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5444 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5449 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5450 Store[ax][ay+1] = element;
5451 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5452 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5453 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5454 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5459 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5460 element == EL_EXPANDABLE_WALL_ANY ||
5461 element == EL_EXPANDABLE_WALL)
5465 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5466 Store[ax-1][ay] = element;
5467 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5468 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5469 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5470 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5476 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5477 Store[ax+1][ay] = element;
5478 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5479 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5480 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5481 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5486 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5487 DrawLevelField(ax, ay);
5489 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5491 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5492 unten_massiv = TRUE;
5493 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5494 links_massiv = TRUE;
5495 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5496 rechts_massiv = TRUE;
5498 if (((oben_massiv && unten_massiv) ||
5499 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5500 element == EL_EXPANDABLE_WALL) &&
5501 ((links_massiv && rechts_massiv) ||
5502 element == EL_EXPANDABLE_WALL_VERTICAL))
5503 Feld[ax][ay] = EL_WALL;
5507 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5509 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5513 void CheckForDragon(int x, int y)
5516 boolean dragon_found = FALSE;
5517 static int xy[4][2] =
5529 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5531 if (IN_LEV_FIELD(xx, yy) &&
5532 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5534 if (Feld[xx][yy] == EL_DRAGON)
5535 dragon_found = TRUE;
5548 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5550 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5552 Feld[xx][yy] = EL_EMPTY;
5553 DrawLevelField(xx, yy);
5562 static void InitBuggyBase(int x, int y)
5564 int element = Feld[x][y];
5565 int activating_delay = FRAMES_PER_SECOND / 4;
5568 (element == EL_SP_BUGGY_BASE ?
5569 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5570 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5572 element == EL_SP_BUGGY_BASE_ACTIVE ?
5573 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5576 static void WarnBuggyBase(int x, int y)
5579 static int xy[4][2] =
5589 int xx = x + xy[i][0], yy = y + xy[i][1];
5591 if (IS_PLAYER(xx, yy))
5593 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5600 static void InitTrap(int x, int y)
5602 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5605 static void ActivateTrap(int x, int y)
5607 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5610 static void ChangeActiveTrap(int x, int y)
5612 int graphic = IMG_TRAP_ACTIVE;
5614 /* if new animation frame was drawn, correct crumbled sand border */
5615 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5616 DrawLevelFieldCrumbledSand(x, y);
5619 static void ChangeElementNowExt(int x, int y, int target_element)
5621 /* check if element under player changes from accessible to unaccessible
5622 (needed for special case of dropping element which then changes) */
5623 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5624 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5631 Feld[x][y] = target_element;
5633 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5635 ResetGfxAnimation(x, y);
5636 ResetRandomAnimationValue(x, y);
5638 InitField(x, y, FALSE);
5639 if (CAN_MOVE(Feld[x][y]))
5642 DrawLevelField(x, y);
5644 if (GFX_CRUMBLED(Feld[x][y]))
5645 DrawLevelFieldCrumbledSandNeighbours(x, y);
5647 TestIfBadThingTouchesHero(x, y);
5648 TestIfPlayerTouchesCustomElement(x, y);
5649 TestIfElementTouchesCustomElement(x, y);
5651 if (ELEM_IS_PLAYER(target_element))
5652 RelocatePlayer(x, y, target_element);
5655 static boolean ChangeElementNow(int x, int y, int element, int page)
5657 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5659 /* always use default change event to prevent running into a loop */
5660 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5661 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5663 /* do not change already changed elements with same change event */
5665 if (Changed[x][y] & ChangeEvent[x][y])
5672 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5674 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5676 if (change->explode)
5683 if (change->use_content)
5685 boolean complete_change = TRUE;
5686 boolean can_change[3][3];
5689 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5691 boolean half_destructible;
5692 int ex = x + xx - 1;
5693 int ey = y + yy - 1;
5696 can_change[xx][yy] = TRUE;
5698 if (ex == x && ey == y) /* do not check changing element itself */
5701 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5703 can_change[xx][yy] = FALSE; /* do not change empty borders */
5708 if (!IN_LEV_FIELD(ex, ey))
5710 can_change[xx][yy] = FALSE;
5711 complete_change = FALSE;
5718 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5719 e = MovingOrBlocked2Element(ex, ey);
5721 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5723 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5724 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5725 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5727 can_change[xx][yy] = FALSE;
5728 complete_change = FALSE;
5732 if (!change->only_complete || complete_change)
5734 boolean something_has_changed = FALSE;
5736 if (change->only_complete && change->use_random_change &&
5737 RND(100) < change->random)
5740 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5742 int ex = x + xx - 1;
5743 int ey = y + yy - 1;
5745 if (can_change[xx][yy] && (!change->use_random_change ||
5746 RND(100) < change->random))
5748 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5749 RemoveMovingField(ex, ey);
5751 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5753 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5755 something_has_changed = TRUE;
5757 /* for symmetry reasons, freeze newly created border elements */
5758 if (ex != x || ey != y)
5759 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5763 if (something_has_changed)
5764 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
5769 ChangeElementNowExt(x, y, change->target_element);
5771 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
5777 static void ChangeElement(int x, int y, int page)
5779 int element = MovingOrBlocked2Element(x, y);
5780 struct ElementInfo *ei = &element_info[element];
5781 struct ElementChangeInfo *change = &ei->change_page[page];
5785 if (!CAN_CHANGE(element))
5788 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5789 x, y, element, element_info[element].token_name);
5790 printf("ChangeElement(): This should never happen!\n");
5796 if (ChangeDelay[x][y] == 0) /* initialize element change */
5798 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5799 RND(change->delay_random * change->delay_frames)) + 1;
5801 ResetGfxAnimation(x, y);
5802 ResetRandomAnimationValue(x, y);
5804 if (change->pre_change_function)
5805 change->pre_change_function(x, y);
5808 ChangeDelay[x][y]--;
5810 if (ChangeDelay[x][y] != 0) /* continue element change */
5812 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5814 if (IS_ANIMATED(graphic))
5815 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5817 if (change->change_function)
5818 change->change_function(x, y);
5820 else /* finish element change */
5822 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5824 page = ChangePage[x][y];
5825 ChangePage[x][y] = -1;
5828 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5830 ChangeDelay[x][y] = 1; /* try change after next move step */
5831 ChangePage[x][y] = page; /* remember page to use for change */
5836 if (ChangeElementNow(x, y, element, page))
5838 if (change->post_change_function)
5839 change->post_change_function(x, y);
5844 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5845 int trigger_element,
5851 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5854 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5856 int element = EL_CUSTOM_START + i;
5858 boolean change_element = FALSE;
5861 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5864 for (j=0; j < element_info[element].num_change_pages; j++)
5866 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5868 if (change->can_change &&
5870 change->events & CH_EVENT_BIT(trigger_event) &&
5872 change->sides & trigger_side &&
5873 change->trigger_element == trigger_element)
5876 if (!(change->events & CH_EVENT_BIT(trigger_event)))
5877 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5878 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5881 change_element = TRUE;
5888 if (!change_element)
5891 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5894 if (x == lx && y == ly) /* do not change trigger element itself */
5898 if (Feld[x][y] == element)
5900 ChangeDelay[x][y] = 1;
5901 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5902 ChangeElement(x, y, page);
5910 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5913 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5917 static boolean CheckElementSideChange(int x, int y, int element, int side,
5918 int trigger_event, int page)
5920 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5923 if (Feld[x][y] == EL_BLOCKED)
5925 Blocked2Moving(x, y, &x, &y);
5926 element = Feld[x][y];
5930 page = element_info[element].event_page_nr[trigger_event];
5932 if (!(element_info[element].change_page[page].sides & side))
5935 ChangeDelay[x][y] = 1;
5936 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5937 ChangeElement(x, y, page);
5942 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5944 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5948 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
5951 static byte stored_player_action[MAX_PLAYERS];
5952 static int num_stored_actions = 0;
5954 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5955 int left = player_action & JOY_LEFT;
5956 int right = player_action & JOY_RIGHT;
5957 int up = player_action & JOY_UP;
5958 int down = player_action & JOY_DOWN;
5959 int button1 = player_action & JOY_BUTTON_1;
5960 int button2 = player_action & JOY_BUTTON_2;
5961 int dx = (left ? -1 : right ? 1 : 0);
5962 int dy = (up ? -1 : down ? 1 : 0);
5965 stored_player_action[player->index_nr] = 0;
5966 num_stored_actions++;
5970 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
5973 if (!player->active || tape.pausing)
5979 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
5983 snapped = SnapField(player, dx, dy);
5987 dropped = DropElement(player);
5989 moved = MovePlayer(player, dx, dy);
5992 if (tape.single_step && tape.recording && !tape.pausing)
5994 if (button1 || (dropped && !moved))
5996 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5997 SnapField(player, 0, 0); /* stop snapping */
6002 return player_action;
6004 stored_player_action[player->index_nr] = player_action;
6010 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6013 /* no actions for this player (no input at player's configured device) */
6015 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6016 SnapField(player, 0, 0);
6017 CheckGravityMovement(player);
6019 if (player->MovPos == 0)
6020 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6022 if (player->MovPos == 0) /* needed for tape.playing */
6023 player->is_moving = FALSE;
6029 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6031 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6033 TapeRecordAction(stored_player_action);
6034 num_stored_actions = 0;
6041 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6043 static byte stored_player_action[MAX_PLAYERS];
6044 static int num_stored_actions = 0;
6045 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6046 int left = player_action & JOY_LEFT;
6047 int right = player_action & JOY_RIGHT;
6048 int up = player_action & JOY_UP;
6049 int down = player_action & JOY_DOWN;
6050 int button1 = player_action & JOY_BUTTON_1;
6051 int button2 = player_action & JOY_BUTTON_2;
6052 int dx = (left ? -1 : right ? 1 : 0);
6053 int dy = (up ? -1 : down ? 1 : 0);
6055 stored_player_action[player->index_nr] = 0;
6056 num_stored_actions++;
6058 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6060 if (!player->active || tape.pausing)
6065 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6068 snapped = SnapField(player, dx, dy);
6072 dropped = DropElement(player);
6074 moved = MovePlayer(player, dx, dy);
6077 if (tape.single_step && tape.recording && !tape.pausing)
6079 if (button1 || (dropped && !moved))
6081 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6082 SnapField(player, 0, 0); /* stop snapping */
6086 stored_player_action[player->index_nr] = player_action;
6090 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6092 /* no actions for this player (no input at player's configured device) */
6094 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6095 SnapField(player, 0, 0);
6096 CheckGravityMovement(player);
6098 if (player->MovPos == 0)
6099 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6101 if (player->MovPos == 0) /* needed for tape.playing */
6102 player->is_moving = FALSE;
6105 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6107 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6109 TapeRecordAction(stored_player_action);
6110 num_stored_actions = 0;
6117 static unsigned long action_delay = 0;
6118 unsigned long action_delay_value;
6119 int magic_wall_x = 0, magic_wall_y = 0;
6120 int i, x, y, element, graphic;
6121 byte *recorded_player_action;
6122 byte summarized_player_action = 0;
6124 byte tape_action[MAX_PLAYERS];
6127 if (game_status != GAME_MODE_PLAYING)
6130 action_delay_value =
6131 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6133 if (tape.playing && tape.index_search && !tape.pausing)
6134 action_delay_value = 0;
6136 /* ---------- main game synchronization point ---------- */
6138 WaitUntilDelayReached(&action_delay, action_delay_value);
6140 if (network_playing && !network_player_action_received)
6144 printf("DEBUG: try to get network player actions in time\n");
6148 #if defined(PLATFORM_UNIX)
6149 /* last chance to get network player actions without main loop delay */
6153 if (game_status != GAME_MODE_PLAYING)
6156 if (!network_player_action_received)
6160 printf("DEBUG: failed to get network player actions in time\n");
6171 printf("::: getting new tape action [%d]\n", FrameCounter);
6174 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6176 for (i=0; i<MAX_PLAYERS; i++)
6178 summarized_player_action |= stored_player[i].action;
6180 if (!network_playing)
6181 stored_player[i].effective_action = stored_player[i].action;
6184 #if defined(PLATFORM_UNIX)
6185 if (network_playing)
6186 SendToServer_MovePlayer(summarized_player_action);
6189 if (!options.network && !setup.team_mode)
6190 local_player->effective_action = summarized_player_action;
6192 for (i=0; i < MAX_PLAYERS; i++)
6194 int actual_player_action = stored_player[i].effective_action;
6196 if (stored_player[i].programmed_action)
6197 actual_player_action = stored_player[i].programmed_action;
6199 if (recorded_player_action)
6200 actual_player_action = recorded_player_action[i];
6202 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6204 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6205 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6207 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6212 TapeRecordAction(tape_action);
6215 network_player_action_received = FALSE;
6217 ScrollScreen(NULL, SCROLL_GO_ON);
6223 for (i=0; i<MAX_PLAYERS; i++)
6224 stored_player[i].Frame++;
6228 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6230 for (i=0; i<MAX_PLAYERS; i++)
6232 struct PlayerInfo *player = &stored_player[i];
6236 if (player->active && player->is_pushing && player->is_moving &&
6239 ContinueMoving(x, y);
6241 /* continue moving after pushing (this is actually a bug) */
6242 if (!IS_MOVING(x, y))
6251 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6253 Changed[x][y] = CE_BITMASK_DEFAULT;
6254 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6257 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6259 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6260 printf("GameActions(): This should never happen!\n");
6262 ChangePage[x][y] = -1;
6267 if (WasJustMoving[x][y] > 0)
6268 WasJustMoving[x][y]--;
6269 if (WasJustFalling[x][y] > 0)
6270 WasJustFalling[x][y]--;
6275 /* reset finished pushing action (not done in ContinueMoving() to allow
6276 continous pushing animation for elements with zero push delay) */
6277 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6279 ResetGfxAnimation(x, y);
6280 DrawLevelField(x, y);
6285 if (IS_BLOCKED(x, y))
6289 Blocked2Moving(x, y, &oldx, &oldy);
6290 if (!IS_MOVING(oldx, oldy))
6292 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6293 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6294 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6295 printf("GameActions(): This should never happen!\n");
6301 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6303 element = Feld[x][y];
6305 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6307 graphic = el2img(element);
6313 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6315 element = graphic = 0;
6319 if (graphic_info[graphic].anim_global_sync)
6320 GfxFrame[x][y] = FrameCounter;
6322 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6323 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6324 ResetRandomAnimationValue(x, y);
6326 SetRandomAnimationValue(x, y);
6329 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6332 if (IS_INACTIVE(element))
6334 if (IS_ANIMATED(graphic))
6335 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6341 /* this may take place after moving, so 'element' may have changed */
6343 if (IS_CHANGING(x, y))
6345 if (IS_CHANGING(x, y) &&
6346 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6350 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6351 element_info[element].event_page_nr[CE_DELAY]);
6353 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6356 element = Feld[x][y];
6357 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6361 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6366 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6368 if (element == EL_MOLE)
6369 printf("::: %d, %d, %d [%d]\n",
6370 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6374 if (element == EL_YAMYAM)
6375 printf("::: %d, %d, %d\n",
6376 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6380 if (IS_ANIMATED(graphic) &&
6384 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6387 if (element == EL_BUG)
6388 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6392 if (element == EL_MOLE)
6393 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6397 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6398 EdelsteinFunkeln(x, y);
6400 else if ((element == EL_ACID ||
6401 element == EL_EXIT_OPEN ||
6402 element == EL_SP_EXIT_OPEN ||
6403 element == EL_SP_TERMINAL ||
6404 element == EL_SP_TERMINAL_ACTIVE ||
6405 element == EL_EXTRA_TIME ||
6406 element == EL_SHIELD_NORMAL ||
6407 element == EL_SHIELD_DEADLY) &&
6408 IS_ANIMATED(graphic))
6409 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6410 else if (IS_MOVING(x, y))
6411 ContinueMoving(x, y);
6412 else if (IS_ACTIVE_BOMB(element))
6413 CheckDynamite(x, y);
6415 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6416 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6418 else if (element == EL_AMOEBA_GROWING)
6419 AmoebeWaechst(x, y);
6420 else if (element == EL_AMOEBA_SHRINKING)
6421 AmoebaDisappearing(x, y);
6423 #if !USE_NEW_AMOEBA_CODE
6424 else if (IS_AMOEBALIVE(element))
6425 AmoebeAbleger(x, y);
6428 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6430 else if (element == EL_EXIT_CLOSED)
6432 else if (element == EL_SP_EXIT_CLOSED)
6434 else if (element == EL_EXPANDABLE_WALL_GROWING)
6436 else if (element == EL_EXPANDABLE_WALL ||
6437 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6438 element == EL_EXPANDABLE_WALL_VERTICAL ||
6439 element == EL_EXPANDABLE_WALL_ANY)
6441 else if (element == EL_FLAMES)
6442 CheckForDragon(x, y);
6444 else if (IS_AUTO_CHANGING(element))
6445 ChangeElement(x, y);
6447 else if (element == EL_EXPLOSION)
6448 ; /* drawing of correct explosion animation is handled separately */
6449 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6450 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6453 /* this may take place after moving, so 'element' may have changed */
6454 if (IS_AUTO_CHANGING(Feld[x][y]))
6455 ChangeElement(x, y);
6458 if (IS_BELT_ACTIVE(element))
6459 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6461 if (game.magic_wall_active)
6463 int jx = local_player->jx, jy = local_player->jy;
6465 /* play the element sound at the position nearest to the player */
6466 if ((element == EL_MAGIC_WALL_FULL ||
6467 element == EL_MAGIC_WALL_ACTIVE ||
6468 element == EL_MAGIC_WALL_EMPTYING ||
6469 element == EL_BD_MAGIC_WALL_FULL ||
6470 element == EL_BD_MAGIC_WALL_ACTIVE ||
6471 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6472 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6480 #if USE_NEW_AMOEBA_CODE
6481 /* new experimental amoeba growth stuff */
6483 if (!(FrameCounter % 8))
6486 static unsigned long random = 1684108901;
6488 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6491 x = (random >> 10) % lev_fieldx;
6492 y = (random >> 20) % lev_fieldy;
6494 x = RND(lev_fieldx);
6495 y = RND(lev_fieldy);
6497 element = Feld[x][y];
6499 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6500 if (!IS_PLAYER(x,y) &&
6501 (element == EL_EMPTY ||
6502 element == EL_SAND ||
6503 element == EL_QUICKSAND_EMPTY ||
6504 element == EL_ACID_SPLASH_LEFT ||
6505 element == EL_ACID_SPLASH_RIGHT))
6507 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6508 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6509 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6510 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6511 Feld[x][y] = EL_AMOEBA_DROP;
6514 random = random * 129 + 1;
6520 if (game.explosions_delayed)
6523 game.explosions_delayed = FALSE;
6525 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6527 element = Feld[x][y];
6529 if (ExplodeField[x][y])
6530 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6531 else if (element == EL_EXPLOSION)
6532 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6534 ExplodeField[x][y] = EX_NO_EXPLOSION;
6537 game.explosions_delayed = TRUE;
6540 if (game.magic_wall_active)
6542 if (!(game.magic_wall_time_left % 4))
6544 int element = Feld[magic_wall_x][magic_wall_y];
6546 if (element == EL_BD_MAGIC_WALL_FULL ||
6547 element == EL_BD_MAGIC_WALL_ACTIVE ||
6548 element == EL_BD_MAGIC_WALL_EMPTYING)
6549 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6551 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6554 if (game.magic_wall_time_left > 0)
6556 game.magic_wall_time_left--;
6557 if (!game.magic_wall_time_left)
6559 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6561 element = Feld[x][y];
6563 if (element == EL_MAGIC_WALL_ACTIVE ||
6564 element == EL_MAGIC_WALL_FULL)
6566 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6567 DrawLevelField(x, y);
6569 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6570 element == EL_BD_MAGIC_WALL_FULL)
6572 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6573 DrawLevelField(x, y);
6577 game.magic_wall_active = FALSE;
6582 if (game.light_time_left > 0)
6584 game.light_time_left--;
6586 if (game.light_time_left == 0)
6587 RedrawAllLightSwitchesAndInvisibleElements();
6590 if (game.timegate_time_left > 0)
6592 game.timegate_time_left--;
6594 if (game.timegate_time_left == 0)
6595 CloseAllOpenTimegates();
6598 for (i=0; i<MAX_PLAYERS; i++)
6600 struct PlayerInfo *player = &stored_player[i];
6602 if (SHIELD_ON(player))
6604 if (player->shield_deadly_time_left)
6605 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6606 else if (player->shield_normal_time_left)
6607 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6611 if (TimeFrames >= FRAMES_PER_SECOND)
6616 for (i=0; i<MAX_PLAYERS; i++)
6618 struct PlayerInfo *player = &stored_player[i];
6620 if (SHIELD_ON(player))
6622 player->shield_normal_time_left--;
6624 if (player->shield_deadly_time_left > 0)
6625 player->shield_deadly_time_left--;
6629 if (tape.recording || tape.playing)
6630 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6636 if (TimeLeft <= 10 && setup.time_limit)
6637 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6639 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6641 if (!TimeLeft && setup.time_limit)
6642 for (i=0; i<MAX_PLAYERS; i++)
6643 KillHero(&stored_player[i]);
6645 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6646 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6651 if (options.debug) /* calculate frames per second */
6653 static unsigned long fps_counter = 0;
6654 static int fps_frames = 0;
6655 unsigned long fps_delay_ms = Counter() - fps_counter;
6659 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6661 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6664 fps_counter = Counter();
6667 redraw_mask |= REDRAW_FPS;
6671 if (stored_player[0].jx != stored_player[0].last_jx ||
6672 stored_player[0].jy != stored_player[0].last_jy)
6673 printf("::: %d, %d, %d, %d, %d\n",
6674 stored_player[0].MovDir,
6675 stored_player[0].MovPos,
6676 stored_player[0].GfxPos,
6677 stored_player[0].Frame,
6678 stored_player[0].StepFrame);
6685 for (i=0; i<MAX_PLAYERS; i++)
6688 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6690 stored_player[i].Frame += move_frames;
6692 if (stored_player[i].MovPos != 0)
6693 stored_player[i].StepFrame += move_frames;
6698 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6700 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6702 local_player->show_envelope = 0;
6707 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6709 int min_x = x, min_y = y, max_x = x, max_y = y;
6712 for (i=0; i<MAX_PLAYERS; i++)
6714 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6716 if (!stored_player[i].active || &stored_player[i] == player)
6719 min_x = MIN(min_x, jx);
6720 min_y = MIN(min_y, jy);
6721 max_x = MAX(max_x, jx);
6722 max_y = MAX(max_y, jy);
6725 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6728 static boolean AllPlayersInVisibleScreen()
6732 for (i=0; i<MAX_PLAYERS; i++)
6734 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6736 if (!stored_player[i].active)
6739 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6746 void ScrollLevel(int dx, int dy)
6748 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6751 BlitBitmap(drawto_field, drawto_field,
6752 FX + TILEX * (dx == -1) - softscroll_offset,
6753 FY + TILEY * (dy == -1) - softscroll_offset,
6754 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6755 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6756 FX + TILEX * (dx == 1) - softscroll_offset,
6757 FY + TILEY * (dy == 1) - softscroll_offset);
6761 x = (dx == 1 ? BX1 : BX2);
6762 for (y=BY1; y <= BY2; y++)
6763 DrawScreenField(x, y);
6768 y = (dy == 1 ? BY1 : BY2);
6769 for (x=BX1; x <= BX2; x++)
6770 DrawScreenField(x, y);
6773 redraw_mask |= REDRAW_FIELD;
6776 static void CheckGravityMovement(struct PlayerInfo *player)
6778 if (game.gravity && !player->programmed_action)
6780 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6781 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6783 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6784 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6785 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6786 int jx = player->jx, jy = player->jy;
6787 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6788 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6789 int new_jx = jx + dx, new_jy = jy + dy;
6790 boolean field_under_player_is_free =
6791 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6792 boolean player_is_moving_to_valid_field =
6793 (IN_LEV_FIELD(new_jx, new_jy) &&
6794 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6795 Feld[new_jx][new_jy] == EL_SAND));
6796 /* !!! extend EL_SAND to anything diggable !!! */
6798 if (field_under_player_is_free &&
6799 !player_is_moving_to_valid_field &&
6800 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6801 player->programmed_action = MV_DOWN;
6807 -----------------------------------------------------------------------------
6808 dx, dy: direction (non-diagonal) to try to move the player to
6809 real_dx, real_dy: direction as read from input device (can be diagonal)
6812 boolean MovePlayerOneStep(struct PlayerInfo *player,
6813 int dx, int dy, int real_dx, int real_dy)
6816 static int change_sides[4][2] =
6818 /* enter side leave side */
6819 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6820 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6821 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6822 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6824 int move_direction = (dx == -1 ? MV_LEFT :
6825 dx == +1 ? MV_RIGHT :
6827 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6828 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6829 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6831 int jx = player->jx, jy = player->jy;
6832 int new_jx = jx + dx, new_jy = jy + dy;
6836 if (!player->active || (!dx && !dy))
6837 return MF_NO_ACTION;
6839 player->MovDir = (dx < 0 ? MV_LEFT :
6842 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6844 if (!IN_LEV_FIELD(new_jx, new_jy))
6845 return MF_NO_ACTION;
6847 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6848 return MF_NO_ACTION;
6851 element = MovingOrBlocked2Element(new_jx, new_jy);
6853 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6856 if (DONT_RUN_INTO(element))
6858 if (element == EL_ACID && dx == 0 && dy == 1)
6861 Feld[jx][jy] = EL_PLAYER_1;
6862 InitMovingField(jx, jy, MV_DOWN);
6863 Store[jx][jy] = EL_ACID;
6864 ContinueMoving(jx, jy);
6868 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6873 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6874 if (can_move != MF_MOVING)
6877 /* check if DigField() has caused relocation of the player */
6878 if (player->jx != jx || player->jy != jy)
6879 return MF_NO_ACTION;
6881 StorePlayer[jx][jy] = 0;
6882 player->last_jx = jx;
6883 player->last_jy = jy;
6884 player->jx = new_jx;
6885 player->jy = new_jy;
6886 StorePlayer[new_jx][new_jy] = player->element_nr;
6889 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6891 ScrollPlayer(player, SCROLL_INIT);
6894 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6896 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6897 CE_OTHER_GETS_LEFT);
6898 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6899 CE_LEFT_BY_PLAYER, -1);
6902 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6904 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6905 enter_side, CE_OTHER_GETS_ENTERED);
6906 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6907 CE_ENTERED_BY_PLAYER, -1);
6914 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6916 int jx = player->jx, jy = player->jy;
6917 int old_jx = jx, old_jy = jy;
6918 int moved = MF_NO_ACTION;
6920 if (!player->active || (!dx && !dy))
6924 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6928 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6929 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6933 /* remove the last programmed player action */
6934 player->programmed_action = 0;
6938 /* should only happen if pre-1.2 tape recordings are played */
6939 /* this is only for backward compatibility */
6941 int original_move_delay_value = player->move_delay_value;
6944 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6948 /* scroll remaining steps with finest movement resolution */
6949 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6951 while (player->MovPos)
6953 ScrollPlayer(player, SCROLL_GO_ON);
6954 ScrollScreen(NULL, SCROLL_GO_ON);
6960 player->move_delay_value = original_move_delay_value;
6963 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6965 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6966 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6970 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6971 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6977 if (moved & MF_MOVING && !ScreenMovPos &&
6978 (player == local_player || !options.network))
6980 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6981 int offset = (setup.scroll_delay ? 3 : 0);
6983 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6985 /* actual player has left the screen -- scroll in that direction */
6986 if (jx != old_jx) /* player has moved horizontally */
6987 scroll_x += (jx - old_jx);
6988 else /* player has moved vertically */
6989 scroll_y += (jy - old_jy);
6993 if (jx != old_jx) /* player has moved horizontally */
6995 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6996 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6997 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6999 /* don't scroll over playfield boundaries */
7000 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7001 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7003 /* don't scroll more than one field at a time */
7004 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7006 /* don't scroll against the player's moving direction */
7007 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7008 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7009 scroll_x = old_scroll_x;
7011 else /* player has moved vertically */
7013 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7014 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7015 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7017 /* don't scroll over playfield boundaries */
7018 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7019 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7021 /* don't scroll more than one field at a time */
7022 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7024 /* don't scroll against the player's moving direction */
7025 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7026 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7027 scroll_y = old_scroll_y;
7031 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7033 if (!options.network && !AllPlayersInVisibleScreen())
7035 scroll_x = old_scroll_x;
7036 scroll_y = old_scroll_y;
7040 ScrollScreen(player, SCROLL_INIT);
7041 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7048 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7050 if (!(moved & MF_MOVING) && !player->is_pushing)
7055 player->StepFrame = 0;
7057 if (moved & MF_MOVING)
7059 if (old_jx != jx && old_jy == jy)
7060 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7061 else if (old_jx == jx && old_jy != jy)
7062 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7064 DrawLevelField(jx, jy); /* for "crumbled sand" */
7066 player->last_move_dir = player->MovDir;
7067 player->is_moving = TRUE;
7069 player->is_snapping = FALSE;
7073 player->is_switching = FALSE;
7079 static int change_sides[4][2] =
7081 /* enter side leave side */
7082 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7083 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7084 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7085 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7087 int move_direction = player->MovDir;
7088 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7089 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7092 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7094 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7095 leave_side, CE_OTHER_GETS_LEFT);
7096 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7097 leave_side, CE_LEFT_BY_PLAYER, -1);
7100 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7102 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7103 enter_side, CE_OTHER_GETS_ENTERED);
7104 CheckElementSideChange(jx, jy, Feld[jx][jy],
7105 enter_side, CE_ENTERED_BY_PLAYER, -1);
7116 CheckGravityMovement(player);
7119 player->last_move_dir = MV_NO_MOVING;
7121 player->is_moving = FALSE;
7124 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7126 TestIfHeroTouchesBadThing(jx, jy);
7127 TestIfPlayerTouchesCustomElement(jx, jy);
7130 if (!player->active)
7136 void ScrollPlayer(struct PlayerInfo *player, int mode)
7138 int jx = player->jx, jy = player->jy;
7139 int last_jx = player->last_jx, last_jy = player->last_jy;
7140 int move_stepsize = TILEX / player->move_delay_value;
7142 if (!player->active || !player->MovPos)
7145 if (mode == SCROLL_INIT)
7147 player->actual_frame_counter = FrameCounter;
7148 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7150 if (Feld[last_jx][last_jy] == EL_EMPTY)
7151 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7158 else if (!FrameReached(&player->actual_frame_counter, 1))
7161 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7162 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7164 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7165 Feld[last_jx][last_jy] = EL_EMPTY;
7167 /* before DrawPlayer() to draw correct player graphic for this case */
7168 if (player->MovPos == 0)
7169 CheckGravityMovement(player);
7172 DrawPlayer(player); /* needed here only to cleanup last field */
7175 if (player->MovPos == 0) /* player reached destination field */
7177 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7179 /* continue with normal speed after quickly moving through gate */
7180 HALVE_PLAYER_SPEED(player);
7182 /* be able to make the next move without delay */
7183 player->move_delay = 0;
7186 player->last_jx = jx;
7187 player->last_jy = jy;
7189 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7190 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7191 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7193 DrawPlayer(player); /* needed here only to cleanup last field */
7196 if (local_player->friends_still_needed == 0 ||
7197 IS_SP_ELEMENT(Feld[jx][jy]))
7198 player->LevelSolved = player->GameOver = TRUE;
7201 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7203 TestIfHeroTouchesBadThing(jx, jy);
7204 TestIfPlayerTouchesCustomElement(jx, jy);
7206 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7209 if (!player->active)
7213 if (tape.single_step && tape.recording && !tape.pausing &&
7214 !player->programmed_action)
7215 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7219 void ScrollScreen(struct PlayerInfo *player, int mode)
7221 static unsigned long screen_frame_counter = 0;
7223 if (mode == SCROLL_INIT)
7225 /* set scrolling step size according to actual player's moving speed */
7226 ScrollStepSize = TILEX / player->move_delay_value;
7228 screen_frame_counter = FrameCounter;
7229 ScreenMovDir = player->MovDir;
7230 ScreenMovPos = player->MovPos;
7231 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7234 else if (!FrameReached(&screen_frame_counter, 1))
7239 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7240 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7241 redraw_mask |= REDRAW_FIELD;
7244 ScreenMovDir = MV_NO_MOVING;
7247 void TestIfPlayerTouchesCustomElement(int x, int y)
7249 static int xy[4][2] =
7256 static int change_sides[4][2] =
7258 /* center side border side */
7259 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7260 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7261 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7262 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7264 static int touch_dir[4] =
7271 int center_element = Feld[x][y]; /* should always be non-moving! */
7276 int xx = x + xy[i][0];
7277 int yy = y + xy[i][1];
7278 int center_side = change_sides[i][0];
7279 int border_side = change_sides[i][1];
7282 if (!IN_LEV_FIELD(xx, yy))
7285 if (IS_PLAYER(x, y))
7287 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7288 border_element = Feld[xx][yy]; /* may be moving! */
7289 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7290 border_element = Feld[xx][yy];
7291 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7292 border_element = MovingOrBlocked2Element(xx, yy);
7294 continue; /* center and border element do not touch */
7296 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7297 CE_OTHER_GETS_TOUCHED);
7298 CheckElementSideChange(xx, yy, border_element, border_side,
7299 CE_TOUCHED_BY_PLAYER, -1);
7301 else if (IS_PLAYER(xx, yy))
7303 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7305 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7307 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7308 continue; /* center and border element do not touch */
7311 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7312 CE_OTHER_GETS_TOUCHED);
7313 CheckElementSideChange(x, y, center_element, center_side,
7314 CE_TOUCHED_BY_PLAYER, -1);
7321 void TestIfElementTouchesCustomElement(int x, int y)
7323 static int xy[4][2] =
7330 static int change_sides[4][2] =
7332 /* center side border side */
7333 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7334 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7335 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7336 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7338 static int touch_dir[4] =
7345 boolean change_center_element = FALSE;
7346 int center_element_change_page = 0;
7347 int center_element = Feld[x][y]; /* should always be non-moving! */
7352 int xx = x + xy[i][0];
7353 int yy = y + xy[i][1];
7354 int center_side = change_sides[i][0];
7355 int border_side = change_sides[i][1];
7358 if (!IN_LEV_FIELD(xx, yy))
7361 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7362 border_element = Feld[xx][yy]; /* may be moving! */
7363 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7364 border_element = Feld[xx][yy];
7365 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7366 border_element = MovingOrBlocked2Element(xx, yy);
7368 continue; /* center and border element do not touch */
7370 /* check for change of center element (but change it only once) */
7371 if (IS_CUSTOM_ELEMENT(center_element) &&
7372 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7373 !change_center_element)
7375 for (j=0; j < element_info[center_element].num_change_pages; j++)
7377 struct ElementChangeInfo *change =
7378 &element_info[center_element].change_page[j];
7380 if (change->can_change &&
7381 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7382 change->sides & border_side &&
7383 change->trigger_element == border_element)
7385 change_center_element = TRUE;
7386 center_element_change_page = j;
7393 /* check for change of border element */
7394 if (IS_CUSTOM_ELEMENT(border_element) &&
7395 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7397 for (j=0; j < element_info[border_element].num_change_pages; j++)
7399 struct ElementChangeInfo *change =
7400 &element_info[border_element].change_page[j];
7402 if (change->can_change &&
7403 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7404 change->sides & center_side &&
7405 change->trigger_element == center_element)
7407 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7408 CE_OTHER_IS_TOUCHING, j);
7415 if (change_center_element)
7416 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7417 CE_OTHER_IS_TOUCHING, center_element_change_page);
7420 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7422 int i, kill_x = -1, kill_y = -1;
7423 static int test_xy[4][2] =
7430 static int test_dir[4] =
7440 int test_x, test_y, test_move_dir, test_element;
7442 test_x = good_x + test_xy[i][0];
7443 test_y = good_y + test_xy[i][1];
7444 if (!IN_LEV_FIELD(test_x, test_y))
7448 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7451 test_element = Feld[test_x][test_y];
7453 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7456 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7457 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7459 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7460 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7468 if (kill_x != -1 || kill_y != -1)
7470 if (IS_PLAYER(good_x, good_y))
7472 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7474 if (player->shield_deadly_time_left > 0)
7475 Bang(kill_x, kill_y);
7476 else if (!PLAYER_PROTECTED(good_x, good_y))
7480 Bang(good_x, good_y);
7484 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7486 int i, kill_x = -1, kill_y = -1;
7487 int bad_element = Feld[bad_x][bad_y];
7488 static int test_xy[4][2] =
7495 static int touch_dir[4] =
7502 static int test_dir[4] =
7510 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7515 int test_x, test_y, test_move_dir, test_element;
7517 test_x = bad_x + test_xy[i][0];
7518 test_y = bad_y + test_xy[i][1];
7519 if (!IN_LEV_FIELD(test_x, test_y))
7523 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7525 test_element = Feld[test_x][test_y];
7527 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7528 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7530 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7531 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7533 /* good thing is player or penguin that does not move away */
7534 if (IS_PLAYER(test_x, test_y))
7536 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7538 if (bad_element == EL_ROBOT && player->is_moving)
7539 continue; /* robot does not kill player if he is moving */
7541 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7543 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7544 continue; /* center and border element do not touch */
7551 else if (test_element == EL_PENGUIN)
7560 if (kill_x != -1 || kill_y != -1)
7562 if (IS_PLAYER(kill_x, kill_y))
7564 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7566 if (player->shield_deadly_time_left > 0)
7568 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7572 Bang(kill_x, kill_y);
7576 void TestIfHeroTouchesBadThing(int x, int y)
7578 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7581 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7583 TestIfGoodThingHitsBadThing(x, y, move_dir);
7586 void TestIfBadThingTouchesHero(int x, int y)
7588 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7591 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7593 TestIfBadThingHitsGoodThing(x, y, move_dir);
7596 void TestIfFriendTouchesBadThing(int x, int y)
7598 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7601 void TestIfBadThingTouchesFriend(int x, int y)
7603 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7606 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7608 int i, kill_x = bad_x, kill_y = bad_y;
7609 static int xy[4][2] =
7621 x = bad_x + xy[i][0];
7622 y = bad_y + xy[i][1];
7623 if (!IN_LEV_FIELD(x, y))
7626 element = Feld[x][y];
7627 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7628 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7636 if (kill_x != bad_x || kill_y != bad_y)
7640 void KillHero(struct PlayerInfo *player)
7642 int jx = player->jx, jy = player->jy;
7644 if (!player->active)
7647 /* remove accessible field at the player's position */
7648 Feld[jx][jy] = EL_EMPTY;
7650 /* deactivate shield (else Bang()/Explode() would not work right) */
7651 player->shield_normal_time_left = 0;
7652 player->shield_deadly_time_left = 0;
7658 static void KillHeroUnlessProtected(int x, int y)
7660 if (!PLAYER_PROTECTED(x, y))
7661 KillHero(PLAYERINFO(x, y));
7664 void BuryHero(struct PlayerInfo *player)
7666 int jx = player->jx, jy = player->jy;
7668 if (!player->active)
7672 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
7674 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
7676 PlayLevelSound(jx, jy, SND_GAME_LOSING);
7678 player->GameOver = TRUE;
7682 void RemoveHero(struct PlayerInfo *player)
7684 int jx = player->jx, jy = player->jy;
7685 int i, found = FALSE;
7687 player->present = FALSE;
7688 player->active = FALSE;
7690 if (!ExplodeField[jx][jy])
7691 StorePlayer[jx][jy] = 0;
7693 for (i=0; i<MAX_PLAYERS; i++)
7694 if (stored_player[i].active)
7698 AllPlayersGone = TRUE;
7705 =============================================================================
7706 checkDiagonalPushing()
7707 -----------------------------------------------------------------------------
7708 check if diagonal input device direction results in pushing of object
7709 (by checking if the alternative direction is walkable, diggable, ...)
7710 =============================================================================
7713 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7714 int x, int y, int real_dx, int real_dy)
7716 int jx, jy, dx, dy, xx, yy;
7718 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7721 /* diagonal direction: check alternative direction */
7726 xx = jx + (dx == 0 ? real_dx : 0);
7727 yy = jy + (dy == 0 ? real_dy : 0);
7729 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7733 =============================================================================
7735 -----------------------------------------------------------------------------
7736 x, y: field next to player (non-diagonal) to try to dig to
7737 real_dx, real_dy: direction as read from input device (can be diagonal)
7738 =============================================================================
7741 int DigField(struct PlayerInfo *player,
7742 int x, int y, int real_dx, int real_dy, int mode)
7744 static int change_sides[4] =
7746 CH_SIDE_RIGHT, /* moving left */
7747 CH_SIDE_LEFT, /* moving right */
7748 CH_SIDE_BOTTOM, /* moving up */
7749 CH_SIDE_TOP, /* moving down */
7751 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7752 int jx = player->jx, jy = player->jy;
7753 int dx = x - jx, dy = y - jy;
7754 int nextx = x + dx, nexty = y + dy;
7755 int move_direction = (dx == -1 ? MV_LEFT :
7756 dx == +1 ? MV_RIGHT :
7758 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7759 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7762 if (player->MovPos == 0)
7764 player->is_digging = FALSE;
7765 player->is_collecting = FALSE;
7768 if (player->MovPos == 0) /* last pushing move finished */
7769 player->is_pushing = FALSE;
7771 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7773 player->is_switching = FALSE;
7774 player->push_delay = 0;
7776 return MF_NO_ACTION;
7779 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7780 return MF_NO_ACTION;
7783 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7785 if (IS_TUBE(Feld[jx][jy]) ||
7786 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7790 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7791 int tube_leave_directions[][2] =
7793 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7794 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7795 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7796 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7797 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7798 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7799 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7800 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7801 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7802 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7803 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7804 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7807 while (tube_leave_directions[i][0] != tube_element)
7810 if (tube_leave_directions[i][0] == -1) /* should not happen */
7814 if (!(tube_leave_directions[i][1] & move_direction))
7815 return MF_NO_ACTION; /* tube has no opening in this direction */
7818 element = Feld[x][y];
7820 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7821 game.engine_version >= VERSION_IDENT(2,2,0,0))
7822 return MF_NO_ACTION;
7826 case EL_SP_PORT_LEFT:
7827 case EL_SP_PORT_RIGHT:
7829 case EL_SP_PORT_DOWN:
7830 case EL_SP_PORT_HORIZONTAL:
7831 case EL_SP_PORT_VERTICAL:
7832 case EL_SP_PORT_ANY:
7833 case EL_SP_GRAVITY_PORT_LEFT:
7834 case EL_SP_GRAVITY_PORT_RIGHT:
7835 case EL_SP_GRAVITY_PORT_UP:
7836 case EL_SP_GRAVITY_PORT_DOWN:
7838 element != EL_SP_PORT_LEFT &&
7839 element != EL_SP_GRAVITY_PORT_LEFT &&
7840 element != EL_SP_PORT_HORIZONTAL &&
7841 element != EL_SP_PORT_ANY) ||
7843 element != EL_SP_PORT_RIGHT &&
7844 element != EL_SP_GRAVITY_PORT_RIGHT &&
7845 element != EL_SP_PORT_HORIZONTAL &&
7846 element != EL_SP_PORT_ANY) ||
7848 element != EL_SP_PORT_UP &&
7849 element != EL_SP_GRAVITY_PORT_UP &&
7850 element != EL_SP_PORT_VERTICAL &&
7851 element != EL_SP_PORT_ANY) ||
7853 element != EL_SP_PORT_DOWN &&
7854 element != EL_SP_GRAVITY_PORT_DOWN &&
7855 element != EL_SP_PORT_VERTICAL &&
7856 element != EL_SP_PORT_ANY) ||
7857 !IN_LEV_FIELD(nextx, nexty) ||
7858 !IS_FREE(nextx, nexty))
7859 return MF_NO_ACTION;
7861 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7862 element == EL_SP_GRAVITY_PORT_RIGHT ||
7863 element == EL_SP_GRAVITY_PORT_UP ||
7864 element == EL_SP_GRAVITY_PORT_DOWN)
7865 game.gravity = !game.gravity;
7867 /* automatically move to the next field with double speed */
7868 player->programmed_action = move_direction;
7869 DOUBLE_PLAYER_SPEED(player);
7871 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
7875 case EL_TUBE_VERTICAL:
7876 case EL_TUBE_HORIZONTAL:
7877 case EL_TUBE_VERTICAL_LEFT:
7878 case EL_TUBE_VERTICAL_RIGHT:
7879 case EL_TUBE_HORIZONTAL_UP:
7880 case EL_TUBE_HORIZONTAL_DOWN:
7881 case EL_TUBE_LEFT_UP:
7882 case EL_TUBE_LEFT_DOWN:
7883 case EL_TUBE_RIGHT_UP:
7884 case EL_TUBE_RIGHT_DOWN:
7887 int tube_enter_directions[][2] =
7889 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7890 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7891 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7892 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7893 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7894 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7895 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7896 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7897 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7898 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7899 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7900 { -1, MV_NO_MOVING }
7903 while (tube_enter_directions[i][0] != element)
7906 if (tube_enter_directions[i][0] == -1) /* should not happen */
7910 if (!(tube_enter_directions[i][1] & move_direction))
7911 return MF_NO_ACTION; /* tube has no opening in this direction */
7913 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
7919 if (IS_WALKABLE(element))
7921 int sound_action = ACTION_WALKING;
7923 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7925 if (!player->key[element - EL_GATE_1])
7926 return MF_NO_ACTION;
7928 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7930 if (!player->key[element - EL_GATE_1_GRAY])
7931 return MF_NO_ACTION;
7933 else if (element == EL_EXIT_OPEN ||
7934 element == EL_SP_EXIT_OPEN ||
7935 element == EL_SP_EXIT_OPENING)
7937 sound_action = ACTION_PASSING; /* player is passing exit */
7939 else if (element == EL_EMPTY)
7941 sound_action = ACTION_MOVING; /* nothing to walk on */
7944 /* play sound from background or player, whatever is available */
7945 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7946 PlayLevelSoundElementAction(x, y, element, sound_action);
7948 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
7952 else if (IS_PASSABLE(element))
7954 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7955 return MF_NO_ACTION;
7958 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7959 return MF_NO_ACTION;
7962 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7964 if (!player->key[element - EL_EM_GATE_1])
7965 return MF_NO_ACTION;
7967 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7969 if (!player->key[element - EL_EM_GATE_1_GRAY])
7970 return MF_NO_ACTION;
7973 /* automatically move to the next field with double speed */
7974 player->programmed_action = move_direction;
7975 DOUBLE_PLAYER_SPEED(player);
7977 PlayLevelSoundAction(x, y, ACTION_PASSING);
7981 else if (IS_DIGGABLE(element))
7985 if (mode != DF_SNAP)
7988 GfxElement[x][y] = GFX_ELEMENT(element);
7991 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7993 player->is_digging = TRUE;
7996 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
7998 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8001 if (mode == DF_SNAP)
8002 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8007 else if (IS_COLLECTIBLE(element))
8011 if (mode != DF_SNAP)
8013 GfxElement[x][y] = element;
8014 player->is_collecting = TRUE;
8017 if (element == EL_SPEED_PILL)
8018 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8019 else if (element == EL_EXTRA_TIME && level.time > 0)
8022 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8024 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8026 player->shield_normal_time_left += 10;
8027 if (element == EL_SHIELD_DEADLY)
8028 player->shield_deadly_time_left += 10;
8030 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8032 if (player->inventory_size < MAX_INVENTORY_SIZE)
8033 player->inventory_element[player->inventory_size++] = element;
8035 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8036 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8038 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8040 player->dynabomb_count++;
8041 player->dynabombs_left++;
8043 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8045 player->dynabomb_size++;
8047 else if (element == EL_DYNABOMB_INCREASE_POWER)
8049 player->dynabomb_xl = TRUE;
8051 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8052 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8054 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8055 element - EL_KEY_1 : element - EL_EM_KEY_1);
8057 player->key[key_nr] = TRUE;
8059 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8060 el2edimg(EL_KEY_1 + key_nr));
8061 redraw_mask |= REDRAW_DOOR_1;
8063 else if (IS_ENVELOPE(element))
8066 player->show_envelope = element;
8068 ShowEnvelope(element - EL_ENVELOPE_1);
8071 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8075 for (i=0; i < element_info[element].collect_count; i++)
8076 if (player->inventory_size < MAX_INVENTORY_SIZE)
8077 player->inventory_element[player->inventory_size++] = element;
8079 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8080 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8082 else if (element_info[element].collect_count > 0)
8084 local_player->gems_still_needed -=
8085 element_info[element].collect_count;
8086 if (local_player->gems_still_needed < 0)
8087 local_player->gems_still_needed = 0;
8089 DrawText(DX_EMERALDS, DY_EMERALDS,
8090 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8093 RaiseScoreElement(element);
8094 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8096 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8099 if (mode == DF_SNAP)
8100 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8105 else if (IS_PUSHABLE(element))
8107 if (mode == DF_SNAP && element != EL_BD_ROCK)
8108 return MF_NO_ACTION;
8110 if (CAN_FALL(element) && dy)
8111 return MF_NO_ACTION;
8113 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8114 !(element == EL_SPRING && use_spring_bug))
8115 return MF_NO_ACTION;
8118 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8119 ((move_direction & MV_VERTICAL &&
8120 ((element_info[element].move_pattern & MV_LEFT &&
8121 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8122 (element_info[element].move_pattern & MV_RIGHT &&
8123 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8124 (move_direction & MV_HORIZONTAL &&
8125 ((element_info[element].move_pattern & MV_UP &&
8126 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8127 (element_info[element].move_pattern & MV_DOWN &&
8128 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8129 return MF_NO_ACTION;
8133 /* do not push elements already moving away faster than player */
8134 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8135 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8136 return MF_NO_ACTION;
8138 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8139 return MF_NO_ACTION;
8143 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8145 if (player->push_delay_value == -1)
8146 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8148 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8150 if (!player->is_pushing)
8151 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8155 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8156 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8157 !player_is_pushing))
8158 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8161 if (!player->is_pushing &&
8162 game.engine_version >= VERSION_IDENT(2,2,0,7))
8163 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8167 printf("::: push delay: %ld [%d, %d] [%d]\n",
8168 player->push_delay_value, FrameCounter, game.engine_version,
8169 player->is_pushing);
8172 player->is_pushing = TRUE;
8174 if (!(IN_LEV_FIELD(nextx, nexty) &&
8175 (IS_FREE(nextx, nexty) ||
8176 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8177 IS_SB_ELEMENT(element)))))
8178 return MF_NO_ACTION;
8180 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8181 return MF_NO_ACTION;
8183 if (player->push_delay == 0) /* new pushing; restart delay */
8184 player->push_delay = FrameCounter;
8186 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8187 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8188 element != EL_SPRING && element != EL_BALLOON)
8190 /* make sure that there is no move delay before next try to push */
8191 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8192 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8194 return MF_NO_ACTION;
8198 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8201 if (IS_SB_ELEMENT(element))
8203 if (element == EL_SOKOBAN_FIELD_FULL)
8205 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8206 local_player->sokobanfields_still_needed++;
8209 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8211 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8212 local_player->sokobanfields_still_needed--;
8215 Feld[x][y] = EL_SOKOBAN_OBJECT;
8217 if (Back[x][y] == Back[nextx][nexty])
8218 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8219 else if (Back[x][y] != 0)
8220 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8223 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8226 if (local_player->sokobanfields_still_needed == 0 &&
8227 game.emulation == EMU_SOKOBAN)
8229 player->LevelSolved = player->GameOver = TRUE;
8230 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8234 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8236 InitMovingField(x, y, move_direction);
8237 GfxAction[x][y] = ACTION_PUSHING;
8239 if (mode == DF_SNAP)
8240 ContinueMoving(x, y);
8242 MovPos[x][y] = (dx != 0 ? dx : dy);
8244 Pushed[x][y] = TRUE;
8245 Pushed[nextx][nexty] = TRUE;
8247 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8248 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8250 player->push_delay_value = -1; /* get new value later */
8252 CheckTriggeredElementSideChange(x, y, element, dig_side,
8253 CE_OTHER_GETS_PUSHED);
8254 CheckElementSideChange(x, y, element, dig_side,
8255 CE_PUSHED_BY_PLAYER, -1);
8259 else if (IS_SWITCHABLE(element))
8261 if (PLAYER_SWITCHING(player, x, y))
8264 player->is_switching = TRUE;
8265 player->switch_x = x;
8266 player->switch_y = y;
8268 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8270 if (element == EL_ROBOT_WHEEL)
8272 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8276 DrawLevelField(x, y);
8278 else if (element == EL_SP_TERMINAL)
8282 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8284 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8286 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8287 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8290 else if (IS_BELT_SWITCH(element))
8292 ToggleBeltSwitch(x, y);
8294 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8295 element == EL_SWITCHGATE_SWITCH_DOWN)
8297 ToggleSwitchgateSwitch(x, y);
8299 else if (element == EL_LIGHT_SWITCH ||
8300 element == EL_LIGHT_SWITCH_ACTIVE)
8302 ToggleLightSwitch(x, y);
8305 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8306 SND_LIGHT_SWITCH_ACTIVATING :
8307 SND_LIGHT_SWITCH_DEACTIVATING);
8310 else if (element == EL_TIMEGATE_SWITCH)
8312 ActivateTimegateSwitch(x, y);
8314 else if (element == EL_BALLOON_SWITCH_LEFT ||
8315 element == EL_BALLOON_SWITCH_RIGHT ||
8316 element == EL_BALLOON_SWITCH_UP ||
8317 element == EL_BALLOON_SWITCH_DOWN ||
8318 element == EL_BALLOON_SWITCH_ANY)
8320 if (element == EL_BALLOON_SWITCH_ANY)
8321 game.balloon_dir = move_direction;
8323 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8324 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8325 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8326 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8329 else if (element == EL_LAMP)
8331 Feld[x][y] = EL_LAMP_ACTIVE;
8332 local_player->lights_still_needed--;
8334 DrawLevelField(x, y);
8336 else if (element == EL_TIME_ORB_FULL)
8338 Feld[x][y] = EL_TIME_ORB_EMPTY;
8340 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8342 DrawLevelField(x, y);
8345 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8353 if (!PLAYER_SWITCHING(player, x, y))
8355 player->is_switching = TRUE;
8356 player->switch_x = x;
8357 player->switch_y = y;
8359 CheckTriggeredElementSideChange(x, y, element, dig_side,
8360 CE_OTHER_IS_SWITCHING);
8361 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8364 CheckTriggeredElementSideChange(x, y, element, dig_side,
8365 CE_OTHER_GETS_PRESSED);
8366 CheckElementSideChange(x, y, element, dig_side,
8367 CE_PRESSED_BY_PLAYER, -1);
8370 return MF_NO_ACTION;
8373 player->push_delay = 0;
8375 if (Feld[x][y] != element) /* really digged/collected something */
8376 player->is_collecting = !player->is_digging;
8381 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8383 int jx = player->jx, jy = player->jy;
8384 int x = jx + dx, y = jy + dy;
8385 int snap_direction = (dx == -1 ? MV_LEFT :
8386 dx == +1 ? MV_RIGHT :
8388 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8390 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8393 if (!player->active || !IN_LEV_FIELD(x, y))
8401 if (player->MovPos == 0)
8402 player->is_pushing = FALSE;
8404 player->is_snapping = FALSE;
8406 if (player->MovPos == 0)
8408 player->is_moving = FALSE;
8409 player->is_digging = FALSE;
8410 player->is_collecting = FALSE;
8416 if (player->is_snapping)
8419 player->MovDir = snap_direction;
8421 player->is_moving = FALSE;
8422 player->is_digging = FALSE;
8423 player->is_collecting = FALSE;
8425 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8428 player->is_snapping = TRUE;
8430 player->is_moving = FALSE;
8431 player->is_digging = FALSE;
8432 player->is_collecting = FALSE;
8434 DrawLevelField(x, y);
8440 boolean DropElement(struct PlayerInfo *player)
8442 int jx = player->jx, jy = player->jy;
8445 if (!player->active || player->MovPos)
8448 old_element = Feld[jx][jy];
8450 /* check if player has anything that can be dropped */
8451 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8454 /* check if anything can be dropped at the current position */
8455 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8458 /* collected custom elements can only be dropped on empty fields */
8459 if (player->inventory_size > 0 &&
8460 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8461 && old_element != EL_EMPTY)
8464 if (old_element != EL_EMPTY)
8465 Back[jx][jy] = old_element; /* store old element on this field */
8467 MovDelay[jx][jy] = 96;
8469 ResetGfxAnimation(jx, jy);
8470 ResetRandomAnimationValue(jx, jy);
8472 if (player->inventory_size > 0)
8474 int new_element = player->inventory_element[--player->inventory_size];
8476 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8477 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8480 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8481 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8483 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8484 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8486 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8488 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8489 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8491 TestIfElementTouchesCustomElement(jx, jy);
8493 else /* player is dropping a dyna bomb */
8495 player->dynabombs_left--;
8498 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8500 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8501 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8503 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8509 /* ------------------------------------------------------------------------- */
8510 /* game sound playing functions */
8511 /* ------------------------------------------------------------------------- */
8513 static int *loop_sound_frame = NULL;
8514 static int *loop_sound_volume = NULL;
8516 void InitPlayLevelSound()
8518 int num_sounds = getSoundListSize();
8520 if (loop_sound_frame != NULL)
8521 free(loop_sound_frame);
8523 if (loop_sound_volume != NULL)
8524 free(loop_sound_volume);
8526 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8527 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8530 static void PlayLevelSound(int x, int y, int nr)
8532 int sx = SCREENX(x), sy = SCREENY(y);
8533 int volume, stereo_position;
8534 int max_distance = 8;
8535 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8537 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8538 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8541 if (!IN_LEV_FIELD(x, y) ||
8542 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8543 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8546 volume = SOUND_MAX_VOLUME;
8548 if (!IN_SCR_FIELD(sx, sy))
8550 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8551 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8553 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8556 stereo_position = (SOUND_MAX_LEFT +
8557 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8558 (SCR_FIELDX + 2 * max_distance));
8560 if (IS_LOOP_SOUND(nr))
8562 /* This assures that quieter loop sounds do not overwrite louder ones,
8563 while restarting sound volume comparison with each new game frame. */
8565 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8568 loop_sound_volume[nr] = volume;
8569 loop_sound_frame[nr] = FrameCounter;
8572 PlaySoundExt(nr, volume, stereo_position, type);
8575 static void PlayLevelSoundNearest(int x, int y, int sound_action)
8577 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
8578 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8579 y < LEVELY(BY1) ? LEVELY(BY1) :
8580 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8584 static void PlayLevelSoundAction(int x, int y, int action)
8586 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
8589 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
8591 int sound_effect = element_info[element].sound[action];
8593 if (sound_effect != SND_UNDEFINED)
8594 PlayLevelSound(x, y, sound_effect);
8597 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
8599 int sound_effect = element_info[Feld[x][y]].sound[action];
8601 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8602 PlayLevelSound(x, y, sound_effect);
8605 static void StopLevelSoundActionIfLoop(int x, int y, int action)
8607 int sound_effect = element_info[Feld[x][y]].sound[action];
8609 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8610 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8613 static void PlayLevelMusic()
8616 if (levelset.music[level_nr] != MUS_UNDEFINED)
8617 PlayMusic(levelset.music[level_nr]); /* from config file */
8619 PlayMusic(-(level_nr + 1)); /* from music dir */
8621 PlayMusic(level_nr);
8625 void RaiseScore(int value)
8627 local_player->score += value;
8628 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8631 void RaiseScoreElement(int element)
8637 case EL_EMERALD_YELLOW:
8638 case EL_EMERALD_RED:
8639 case EL_EMERALD_PURPLE:
8640 case EL_SP_INFOTRON:
8641 RaiseScore(level.score[SC_EMERALD]);
8644 RaiseScore(level.score[SC_DIAMOND]);
8647 RaiseScore(level.score[SC_CRYSTAL]);
8650 RaiseScore(level.score[SC_PEARL]);
8653 case EL_BD_BUTTERFLY:
8654 case EL_SP_ELECTRON:
8655 RaiseScore(level.score[SC_BUG]);
8659 case EL_SP_SNIKSNAK:
8660 RaiseScore(level.score[SC_SPACESHIP]);
8663 case EL_DARK_YAMYAM:
8664 RaiseScore(level.score[SC_YAMYAM]);
8667 RaiseScore(level.score[SC_ROBOT]);
8670 RaiseScore(level.score[SC_PACMAN]);
8673 RaiseScore(level.score[SC_NUT]);
8676 case EL_SP_DISK_RED:
8677 case EL_DYNABOMB_INCREASE_NUMBER:
8678 case EL_DYNABOMB_INCREASE_SIZE:
8679 case EL_DYNABOMB_INCREASE_POWER:
8680 RaiseScore(level.score[SC_DYNAMITE]);
8682 case EL_SHIELD_NORMAL:
8683 case EL_SHIELD_DEADLY:
8684 RaiseScore(level.score[SC_SHIELD]);
8687 RaiseScore(level.score[SC_TIME_BONUS]);
8693 RaiseScore(level.score[SC_KEY]);
8696 RaiseScore(element_info[element].collect_score);
8701 void RequestQuitGame(boolean ask_if_really_quit)
8703 if (AllPlayersGone ||
8704 !ask_if_really_quit ||
8705 level_editor_test_game ||
8706 Request("Do you really want to quit the game ?",
8707 REQ_ASK | REQ_STAY_CLOSED))
8709 #if defined(PLATFORM_UNIX)
8710 if (options.network)
8711 SendToServer_StopPlaying();
8715 game_status = GAME_MODE_MAIN;
8721 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8726 /* ---------- new game button stuff ---------------------------------------- */
8728 /* graphic position values for game buttons */
8729 #define GAME_BUTTON_XSIZE 30
8730 #define GAME_BUTTON_YSIZE 30
8731 #define GAME_BUTTON_XPOS 5
8732 #define GAME_BUTTON_YPOS 215
8733 #define SOUND_BUTTON_XPOS 5
8734 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8736 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8737 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8738 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8739 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8740 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8741 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8748 } gamebutton_info[NUM_GAME_BUTTONS] =
8751 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8756 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8761 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8766 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8767 SOUND_CTRL_ID_MUSIC,
8768 "background music on/off"
8771 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8772 SOUND_CTRL_ID_LOOPS,
8773 "sound loops on/off"
8776 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8777 SOUND_CTRL_ID_SIMPLE,
8778 "normal sounds on/off"
8782 void CreateGameButtons()
8786 for (i=0; i<NUM_GAME_BUTTONS; i++)
8788 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8789 struct GadgetInfo *gi;
8792 unsigned long event_mask;
8793 int gd_xoffset, gd_yoffset;
8794 int gd_x1, gd_x2, gd_y1, gd_y2;
8797 gd_xoffset = gamebutton_info[i].x;
8798 gd_yoffset = gamebutton_info[i].y;
8799 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8800 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8802 if (id == GAME_CTRL_ID_STOP ||
8803 id == GAME_CTRL_ID_PAUSE ||
8804 id == GAME_CTRL_ID_PLAY)
8806 button_type = GD_TYPE_NORMAL_BUTTON;
8808 event_mask = GD_EVENT_RELEASED;
8809 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8810 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8814 button_type = GD_TYPE_CHECK_BUTTON;
8816 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8817 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8818 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8819 event_mask = GD_EVENT_PRESSED;
8820 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8821 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8824 gi = CreateGadget(GDI_CUSTOM_ID, id,
8825 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8826 GDI_X, DX + gd_xoffset,
8827 GDI_Y, DY + gd_yoffset,
8828 GDI_WIDTH, GAME_BUTTON_XSIZE,
8829 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8830 GDI_TYPE, button_type,
8831 GDI_STATE, GD_BUTTON_UNPRESSED,
8832 GDI_CHECKED, checked,
8833 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8834 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8835 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8836 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8837 GDI_EVENT_MASK, event_mask,
8838 GDI_CALLBACK_ACTION, HandleGameButtons,
8842 Error(ERR_EXIT, "cannot create gadget");
8844 game_gadget[id] = gi;
8848 void FreeGameButtons()
8852 for (i=0; i<NUM_GAME_BUTTONS; i++)
8853 FreeGadget(game_gadget[i]);
8856 static void MapGameButtons()
8860 for (i=0; i<NUM_GAME_BUTTONS; i++)
8861 MapGadget(game_gadget[i]);
8864 void UnmapGameButtons()
8868 for (i=0; i<NUM_GAME_BUTTONS; i++)
8869 UnmapGadget(game_gadget[i]);
8872 static void HandleGameButtons(struct GadgetInfo *gi)
8874 int id = gi->custom_id;
8876 if (game_status != GAME_MODE_PLAYING)
8881 case GAME_CTRL_ID_STOP:
8882 RequestQuitGame(TRUE);
8885 case GAME_CTRL_ID_PAUSE:
8886 if (options.network)
8888 #if defined(PLATFORM_UNIX)
8890 SendToServer_ContinuePlaying();
8892 SendToServer_PausePlaying();
8896 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8899 case GAME_CTRL_ID_PLAY:
8902 #if defined(PLATFORM_UNIX)
8903 if (options.network)
8904 SendToServer_ContinuePlaying();
8908 tape.pausing = FALSE;
8909 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8914 case SOUND_CTRL_ID_MUSIC:
8915 if (setup.sound_music)
8917 setup.sound_music = FALSE;
8920 else if (audio.music_available)
8922 setup.sound = setup.sound_music = TRUE;
8924 SetAudioMode(setup.sound);
8930 case SOUND_CTRL_ID_LOOPS:
8931 if (setup.sound_loops)
8932 setup.sound_loops = FALSE;
8933 else if (audio.loops_available)
8935 setup.sound = setup.sound_loops = TRUE;
8936 SetAudioMode(setup.sound);
8940 case SOUND_CTRL_ID_SIMPLE:
8941 if (setup.sound_simple)
8942 setup.sound_simple = FALSE;
8943 else if (audio.sound_available)
8945 setup.sound = setup.sound_simple = TRUE;
8946 SetAudioMode(setup.sound);