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 PlayLevelSoundElementActionIfLoop(int, int, int, int);
190 static void PlayLevelSoundActionIfLoop(int, int, int);
191 static void StopLevelSoundActionIfLoop(int, int, int);
192 static void PlayLevelMusic();
194 static void MapGameButtons();
195 static void HandleGameButtons(struct GadgetInfo *);
197 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
200 /* ------------------------------------------------------------------------- */
201 /* definition of elements that automatically change to other elements after */
202 /* a specified time, eventually calling a function when changing */
203 /* ------------------------------------------------------------------------- */
205 /* forward declaration for changer functions */
206 static void InitBuggyBase(int x, int y);
207 static void WarnBuggyBase(int x, int y);
209 static void InitTrap(int x, int y);
210 static void ActivateTrap(int x, int y);
211 static void ChangeActiveTrap(int x, int y);
213 static void InitRobotWheel(int x, int y);
214 static void RunRobotWheel(int x, int y);
215 static void StopRobotWheel(int x, int y);
217 static void InitTimegateWheel(int x, int y);
218 static void RunTimegateWheel(int x, int y);
220 struct ChangingElementInfo
225 void (*pre_change_function)(int x, int y);
226 void (*change_function)(int x, int y);
227 void (*post_change_function)(int x, int y);
230 static struct ChangingElementInfo change_delay_list[] =
281 EL_SWITCHGATE_OPENING,
289 EL_SWITCHGATE_CLOSING,
290 EL_SWITCHGATE_CLOSED,
322 EL_ACID_SPLASH_RIGHT,
331 EL_SP_BUGGY_BASE_ACTIVATING,
338 EL_SP_BUGGY_BASE_ACTIVATING,
339 EL_SP_BUGGY_BASE_ACTIVE,
346 EL_SP_BUGGY_BASE_ACTIVE,
370 EL_ROBOT_WHEEL_ACTIVE,
378 EL_TIMEGATE_SWITCH_ACTIVE,
399 int push_delay_fixed, push_delay_random;
404 { EL_BALLOON, 0, 0 },
406 { EL_SOKOBAN_OBJECT, 2, 0 },
407 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
408 { EL_SATELLITE, 2, 0 },
409 { EL_SP_DISK_YELLOW, 2, 0 },
411 { EL_UNDEFINED, 0, 0 },
419 move_stepsize_list[] =
421 { EL_AMOEBA_DROP, 2 },
422 { EL_AMOEBA_DROPPING, 2 },
423 { EL_QUICKSAND_FILLING, 1 },
424 { EL_QUICKSAND_EMPTYING, 1 },
425 { EL_MAGIC_WALL_FILLING, 2 },
426 { EL_BD_MAGIC_WALL_FILLING, 2 },
427 { EL_MAGIC_WALL_EMPTYING, 2 },
428 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
438 collect_count_list[] =
441 { EL_BD_DIAMOND, 1 },
442 { EL_EMERALD_YELLOW, 1 },
443 { EL_EMERALD_RED, 1 },
444 { EL_EMERALD_PURPLE, 1 },
446 { EL_SP_INFOTRON, 1 },
453 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
455 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
456 CH_EVENT_BIT(CE_DELAY))
457 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
458 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
459 IS_JUST_CHANGING(x, y))
461 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
464 void GetPlayerConfig()
466 if (!audio.sound_available)
467 setup.sound_simple = FALSE;
469 if (!audio.loops_available)
470 setup.sound_loops = FALSE;
472 if (!audio.music_available)
473 setup.sound_music = FALSE;
475 if (!video.fullscreen_available)
476 setup.fullscreen = FALSE;
478 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
480 SetAudioMode(setup.sound);
484 static int getBeltNrFromBeltElement(int element)
486 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
487 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
488 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
491 static int getBeltNrFromBeltActiveElement(int element)
493 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
494 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
495 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
498 static int getBeltNrFromBeltSwitchElement(int element)
500 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
501 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
502 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
505 static int getBeltDirNrFromBeltSwitchElement(int element)
507 static int belt_base_element[4] =
509 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
510 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
511 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
512 EL_CONVEYOR_BELT_4_SWITCH_LEFT
515 int belt_nr = getBeltNrFromBeltSwitchElement(element);
516 int belt_dir_nr = element - belt_base_element[belt_nr];
518 return (belt_dir_nr % 3);
521 static int getBeltDirFromBeltSwitchElement(int element)
523 static int belt_move_dir[3] =
530 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
532 return belt_move_dir[belt_dir_nr];
535 static void InitPlayerField(int x, int y, int element, boolean init_game)
537 if (element == EL_SP_MURPHY)
541 if (stored_player[0].present)
543 Feld[x][y] = EL_SP_MURPHY_CLONE;
549 stored_player[0].use_murphy_graphic = TRUE;
552 Feld[x][y] = EL_PLAYER_1;
558 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
559 int jx = player->jx, jy = player->jy;
561 player->present = TRUE;
563 if (!options.network || player->connected)
565 player->active = TRUE;
567 /* remove potentially duplicate players */
568 if (StorePlayer[jx][jy] == Feld[x][y])
569 StorePlayer[jx][jy] = 0;
571 StorePlayer[x][y] = Feld[x][y];
575 printf("Player %d activated.\n", player->element_nr);
576 printf("[Local player is %d and currently %s.]\n",
577 local_player->element_nr,
578 local_player->active ? "active" : "not active");
582 Feld[x][y] = EL_EMPTY;
583 player->jx = player->last_jx = x;
584 player->jy = player->last_jy = y;
588 static void InitField(int x, int y, boolean init_game)
590 int element = Feld[x][y];
599 InitPlayerField(x, y, element, init_game);
603 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
604 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
605 else if (x > 0 && Feld[x-1][y] == EL_ACID)
606 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
607 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
608 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
609 else if (y > 0 && Feld[x][y-1] == EL_ACID)
610 Feld[x][y] = EL_ACID_POOL_BOTTOM;
611 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
612 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
620 case EL_SPACESHIP_RIGHT:
621 case EL_SPACESHIP_UP:
622 case EL_SPACESHIP_LEFT:
623 case EL_SPACESHIP_DOWN:
625 case EL_BD_BUTTERFLY_RIGHT:
626 case EL_BD_BUTTERFLY_UP:
627 case EL_BD_BUTTERFLY_LEFT:
628 case EL_BD_BUTTERFLY_DOWN:
629 case EL_BD_BUTTERFLY:
630 case EL_BD_FIREFLY_RIGHT:
631 case EL_BD_FIREFLY_UP:
632 case EL_BD_FIREFLY_LEFT:
633 case EL_BD_FIREFLY_DOWN:
635 case EL_PACMAN_RIGHT:
659 if (y == lev_fieldy - 1)
661 Feld[x][y] = EL_AMOEBA_GROWING;
662 Store[x][y] = EL_AMOEBA_WET;
666 case EL_DYNAMITE_ACTIVE:
671 local_player->lights_still_needed++;
674 case EL_SOKOBAN_FIELD_EMPTY:
675 local_player->sokobanfields_still_needed++;
679 local_player->friends_still_needed++;
684 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
689 Feld[x][y] = EL_EMPTY;
694 case EL_EM_KEY_1_FILE:
695 Feld[x][y] = EL_EM_KEY_1;
697 case EL_EM_KEY_2_FILE:
698 Feld[x][y] = EL_EM_KEY_2;
700 case EL_EM_KEY_3_FILE:
701 Feld[x][y] = EL_EM_KEY_3;
703 case EL_EM_KEY_4_FILE:
704 Feld[x][y] = EL_EM_KEY_4;
708 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
709 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
710 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
711 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
712 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
713 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
714 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
715 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
716 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
717 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
718 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
719 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
722 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
723 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
724 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
726 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
728 game.belt_dir[belt_nr] = belt_dir;
729 game.belt_dir_nr[belt_nr] = belt_dir_nr;
731 else /* more than one switch -- set it like the first switch */
733 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
738 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
740 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
743 case EL_LIGHT_SWITCH_ACTIVE:
745 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
749 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
755 void DrawGameDoorValues()
759 for (i=0; i<MAX_PLAYERS; i++)
761 if (stored_player[i].key[j])
762 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
763 el2edimg(EL_KEY_1 + j));
765 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
766 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
767 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
768 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
769 DrawText(DX + XX_SCORE, DY + YY_SCORE,
770 int2str(local_player->score, 5), FONT_TEXT_2);
771 DrawText(DX + XX_TIME, DY + YY_TIME,
772 int2str(TimeLeft, 3), FONT_TEXT_2);
777 =============================================================================
779 -----------------------------------------------------------------------------
780 initialize game engine due to level / tape version number
781 =============================================================================
784 static void InitGameEngine()
788 /* set game engine from tape file when re-playing, else from level file */
789 game.engine_version = (tape.playing ? tape.engine_version :
792 /* dynamically adjust element properties according to game engine version */
793 InitElementPropertiesEngine(game.engine_version);
796 printf("level %d: level version == %06d\n", level_nr, level.game_version);
797 printf(" tape version == %06d [%s] [file: %06d]\n",
798 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
800 printf(" => game.engine_version == %06d\n", game.engine_version);
803 /* ---------- initialize player's initial move delay --------------------- */
805 /* dynamically adjust player properties according to game engine version */
806 game.initial_move_delay =
807 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
808 INITIAL_MOVE_DELAY_OFF);
810 /* dynamically adjust player properties according to level information */
811 game.initial_move_delay_value =
812 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
814 /* ---------- initialize player's initial push delay --------------------- */
816 /* dynamically adjust player properties according to game engine version */
817 game.initial_push_delay_value =
818 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
820 /* ---------- initialize changing elements ------------------------------- */
822 /* initialize changing elements information */
823 for (i=0; i < MAX_NUM_ELEMENTS; i++)
825 struct ElementInfo *ei = &element_info[i];
827 /* this pointer might have been changed in the level editor */
828 ei->change = &ei->change_page[0];
830 if (!IS_CUSTOM_ELEMENT(i))
832 ei->change->target_element = EL_EMPTY_SPACE;
833 ei->change->delay_fixed = 0;
834 ei->change->delay_random = 0;
835 ei->change->delay_frames = 1;
838 ei->change_events = CE_BITMASK_DEFAULT;
839 for (j=0; j < NUM_CHANGE_EVENTS; j++)
841 ei->event_page_nr[j] = 0;
842 ei->event_page[j] = &ei->change_page[0];
846 /* add changing elements from pre-defined list */
847 for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
849 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
850 struct ElementInfo *ei = &element_info[ch_delay->element];
852 ei->change->target_element = ch_delay->target_element;
853 ei->change->delay_fixed = ch_delay->change_delay;
855 ei->change->pre_change_function = ch_delay->pre_change_function;
856 ei->change->change_function = ch_delay->change_function;
857 ei->change->post_change_function = ch_delay->post_change_function;
859 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
863 /* add change events from custom element configuration */
864 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
866 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
868 for (j=0; j < ei->num_change_pages; j++)
870 if (!ei->change_page[j].can_change)
873 for (k=0; k < NUM_CHANGE_EVENTS; k++)
875 /* only add event page for the first page found with this event */
876 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
877 !(ei->change_events & CH_EVENT_BIT(k)))
879 ei->change_events |= CH_EVENT_BIT(k);
880 ei->event_page_nr[k] = j;
881 ei->event_page[k] = &ei->change_page[j];
889 /* add change events from custom element configuration */
890 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
892 int element = EL_CUSTOM_START + i;
894 /* only add custom elements that change after fixed/random frame delay */
895 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
896 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
900 /* ---------- initialize trigger events ---------------------------------- */
902 /* initialize trigger events information */
903 for (i=0; i<MAX_NUM_ELEMENTS; i++)
904 trigger_events[i] = EP_BITMASK_DEFAULT;
907 /* add trigger events from element change event properties */
908 for (i=0; i<MAX_NUM_ELEMENTS; i++)
910 struct ElementInfo *ei = &element_info[i];
912 for (j=0; j < ei->num_change_pages; j++)
914 if (!ei->change_page[j].can_change)
917 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
919 int trigger_element = ei->change_page[j].trigger_element;
921 trigger_events[trigger_element] |= ei->change_page[j].events;
926 /* add trigger events from element change event properties */
927 for (i=0; i<MAX_NUM_ELEMENTS; i++)
928 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
929 trigger_events[element_info[i].change->trigger_element] |=
930 element_info[i].change->events;
933 /* ---------- initialize push delay -------------------------------------- */
935 /* initialize push delay values to default */
936 for (i=0; i<MAX_NUM_ELEMENTS; i++)
938 if (!IS_CUSTOM_ELEMENT(i))
940 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
941 element_info[i].push_delay_random = game.default_push_delay_random;
945 /* set push delay value for certain elements from pre-defined list */
946 for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
948 int e = push_delay_list[i].element;
950 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
951 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
954 /* ---------- initialize move stepsize ----------------------------------- */
956 /* initialize move stepsize values to default */
957 for (i=0; i<MAX_NUM_ELEMENTS; i++)
958 if (!IS_CUSTOM_ELEMENT(i))
959 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
961 /* set move stepsize value for certain elements from pre-defined list */
962 for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
964 int e = move_stepsize_list[i].element;
966 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
969 /* ---------- initialize gem count --------------------------------------- */
971 /* initialize gem count values for each element */
972 for (i=0; i<MAX_NUM_ELEMENTS; i++)
973 if (!IS_CUSTOM_ELEMENT(i))
974 element_info[i].collect_count = 0;
976 /* add gem count values for all elements from pre-defined list */
977 for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
978 element_info[collect_count_list[i].element].collect_count =
979 collect_count_list[i].count;
984 =============================================================================
986 -----------------------------------------------------------------------------
987 initialize and start new game
988 =============================================================================
993 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
994 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
995 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1002 #if USE_NEW_AMOEBA_CODE
1003 printf("Using new amoeba code.\n");
1005 printf("Using old amoeba code.\n");
1010 /* don't play tapes over network */
1011 network_playing = (options.network && !tape.playing);
1013 for (i=0; i<MAX_PLAYERS; i++)
1015 struct PlayerInfo *player = &stored_player[i];
1017 player->index_nr = i;
1018 player->element_nr = EL_PLAYER_1 + i;
1020 player->present = FALSE;
1021 player->active = FALSE;
1024 player->effective_action = 0;
1025 player->programmed_action = 0;
1028 player->gems_still_needed = level.gems_needed;
1029 player->sokobanfields_still_needed = 0;
1030 player->lights_still_needed = 0;
1031 player->friends_still_needed = 0;
1034 player->key[j] = FALSE;
1036 player->dynabomb_count = 0;
1037 player->dynabomb_size = 1;
1038 player->dynabombs_left = 0;
1039 player->dynabomb_xl = FALSE;
1041 player->MovDir = MV_NO_MOVING;
1044 player->GfxDir = MV_NO_MOVING;
1045 player->GfxAction = ACTION_DEFAULT;
1047 player->StepFrame = 0;
1049 player->use_murphy_graphic = FALSE;
1051 player->actual_frame_counter = 0;
1053 player->last_move_dir = MV_NO_MOVING;
1055 player->is_waiting = FALSE;
1056 player->is_moving = FALSE;
1057 player->is_digging = FALSE;
1058 player->is_snapping = FALSE;
1059 player->is_collecting = FALSE;
1060 player->is_pushing = FALSE;
1061 player->is_switching = FALSE;
1063 player->is_bored = FALSE;
1064 player->is_sleeping = FALSE;
1066 player->frame_counter_bored = -1;
1067 player->frame_counter_sleeping = -1;
1069 player->switch_x = -1;
1070 player->switch_y = -1;
1072 player->show_envelope = 0;
1074 player->move_delay = game.initial_move_delay;
1075 player->move_delay_value = game.initial_move_delay_value;
1077 player->push_delay = 0;
1078 player->push_delay_value = game.initial_push_delay_value;
1080 player->last_jx = player->last_jy = 0;
1081 player->jx = player->jy = 0;
1083 player->shield_normal_time_left = 0;
1084 player->shield_deadly_time_left = 0;
1086 player->inventory_size = 0;
1088 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1089 SnapField(player, 0, 0);
1091 player->LevelSolved = FALSE;
1092 player->GameOver = FALSE;
1095 network_player_action_received = FALSE;
1097 #if defined(PLATFORM_UNIX)
1098 /* initial null action */
1099 if (network_playing)
1100 SendToServer_MovePlayer(MV_NO_MOVING);
1108 TimeLeft = level.time;
1110 ScreenMovDir = MV_NO_MOVING;
1114 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1116 AllPlayersGone = FALSE;
1118 game.yamyam_content_nr = 0;
1119 game.magic_wall_active = FALSE;
1120 game.magic_wall_time_left = 0;
1121 game.light_time_left = 0;
1122 game.timegate_time_left = 0;
1123 game.switchgate_pos = 0;
1124 game.balloon_dir = MV_NO_MOVING;
1125 game.gravity = level.initial_gravity;
1126 game.explosions_delayed = TRUE;
1128 game.envelope_active = FALSE;
1132 game.belt_dir[i] = MV_NO_MOVING;
1133 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1136 for (i=0; i<MAX_NUM_AMOEBA; i++)
1137 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1139 for (x=0; x<lev_fieldx; x++)
1141 for (y=0; y<lev_fieldy; y++)
1143 Feld[x][y] = level.field[x][y];
1144 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1145 ChangeDelay[x][y] = 0;
1146 ChangePage[x][y] = -1;
1147 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1149 WasJustMoving[x][y] = 0;
1150 WasJustFalling[x][y] = 0;
1152 Pushed[x][y] = FALSE;
1154 Changed[x][y] = CE_BITMASK_DEFAULT;
1155 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1157 ExplodePhase[x][y] = 0;
1158 ExplodeField[x][y] = EX_NO_EXPLOSION;
1161 GfxRandom[x][y] = INIT_GFX_RANDOM();
1162 GfxElement[x][y] = EL_UNDEFINED;
1163 GfxAction[x][y] = ACTION_DEFAULT;
1164 GfxDir[x][y] = MV_NO_MOVING;
1168 for(y=0; y<lev_fieldy; y++)
1170 for(x=0; x<lev_fieldx; x++)
1172 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1174 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1176 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1179 InitField(x, y, TRUE);
1185 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1186 emulate_sb ? EMU_SOKOBAN :
1187 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1189 /* correct non-moving belts to start moving left */
1191 if (game.belt_dir[i] == MV_NO_MOVING)
1192 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1194 /* check if any connected player was not found in playfield */
1195 for (i=0; i<MAX_PLAYERS; i++)
1197 struct PlayerInfo *player = &stored_player[i];
1199 if (player->connected && !player->present)
1201 for (j=0; j<MAX_PLAYERS; j++)
1203 struct PlayerInfo *some_player = &stored_player[j];
1204 int jx = some_player->jx, jy = some_player->jy;
1206 /* assign first free player found that is present in the playfield */
1207 if (some_player->present && !some_player->connected)
1209 player->present = TRUE;
1210 player->active = TRUE;
1211 some_player->present = FALSE;
1213 StorePlayer[jx][jy] = player->element_nr;
1214 player->jx = player->last_jx = jx;
1215 player->jy = player->last_jy = jy;
1225 /* when playing a tape, eliminate all players who do not participate */
1227 for (i=0; i<MAX_PLAYERS; i++)
1229 if (stored_player[i].active && !tape.player_participates[i])
1231 struct PlayerInfo *player = &stored_player[i];
1232 int jx = player->jx, jy = player->jy;
1234 player->active = FALSE;
1235 StorePlayer[jx][jy] = 0;
1236 Feld[jx][jy] = EL_EMPTY;
1240 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1242 /* when in single player mode, eliminate all but the first active player */
1244 for (i=0; i<MAX_PLAYERS; i++)
1246 if (stored_player[i].active)
1248 for (j=i+1; j<MAX_PLAYERS; j++)
1250 if (stored_player[j].active)
1252 struct PlayerInfo *player = &stored_player[j];
1253 int jx = player->jx, jy = player->jy;
1255 player->active = FALSE;
1256 StorePlayer[jx][jy] = 0;
1257 Feld[jx][jy] = EL_EMPTY;
1264 /* when recording the game, store which players take part in the game */
1267 for (i=0; i<MAX_PLAYERS; i++)
1268 if (stored_player[i].active)
1269 tape.player_participates[i] = TRUE;
1274 for (i=0; i<MAX_PLAYERS; i++)
1276 struct PlayerInfo *player = &stored_player[i];
1278 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1283 if (local_player == player)
1284 printf("Player %d is local player.\n", i+1);
1288 if (BorderElement == EL_EMPTY)
1291 SBX_Right = lev_fieldx - SCR_FIELDX;
1293 SBY_Lower = lev_fieldy - SCR_FIELDY;
1298 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1300 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1303 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1304 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1306 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1307 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1309 /* if local player not found, look for custom element that might create
1310 the player (make some assumptions about the right custom element) */
1311 if (!local_player->present)
1313 int start_x = 0, start_y = 0;
1314 int found_rating = 0;
1315 int found_element = EL_UNDEFINED;
1317 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1319 int element = Feld[x][y];
1324 if (!IS_CUSTOM_ELEMENT(element))
1327 if (CAN_CHANGE(element))
1329 for (i=0; i < element_info[element].num_change_pages; i++)
1331 content = element_info[element].change_page[i].target_element;
1332 is_player = ELEM_IS_PLAYER(content);
1334 if (is_player && (found_rating < 3 || element < found_element))
1340 found_element = element;
1345 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1347 content = element_info[element].content[xx][yy];
1348 is_player = ELEM_IS_PLAYER(content);
1350 if (is_player && (found_rating < 2 || element < found_element))
1352 start_x = x + xx - 1;
1353 start_y = y + yy - 1;
1356 found_element = element;
1359 if (!CAN_CHANGE(element))
1362 for (i=0; i < element_info[element].num_change_pages; i++)
1364 content = element_info[element].change_page[i].content[xx][yy];
1365 is_player = ELEM_IS_PLAYER(content);
1367 if (is_player && (found_rating < 1 || element < found_element))
1369 start_x = x + xx - 1;
1370 start_y = y + yy - 1;
1373 found_element = element;
1379 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1380 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1383 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1384 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1390 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1391 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1392 local_player->jx - MIDPOSX);
1394 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1395 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1396 local_player->jy - MIDPOSY);
1398 scroll_x = SBX_Left;
1399 scroll_y = SBY_Upper;
1400 if (local_player->jx >= SBX_Left + MIDPOSX)
1401 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1402 local_player->jx - MIDPOSX :
1404 if (local_player->jy >= SBY_Upper + MIDPOSY)
1405 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1406 local_player->jy - MIDPOSY :
1411 CloseDoor(DOOR_CLOSE_1);
1416 /* after drawing the level, correct some elements */
1417 if (game.timegate_time_left == 0)
1418 CloseAllOpenTimegates();
1420 if (setup.soft_scrolling)
1421 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1423 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1426 /* copy default game door content to main double buffer */
1427 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1428 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1431 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1434 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1435 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1436 BlitBitmap(drawto, drawto,
1437 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1438 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1439 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1440 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1443 DrawGameDoorValues();
1447 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1448 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1449 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1453 /* copy actual game door content to door double buffer for OpenDoor() */
1454 BlitBitmap(drawto, bitmap_db_door,
1455 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1457 OpenDoor(DOOR_OPEN_ALL);
1459 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1461 if (setup.sound_music)
1464 KeyboardAutoRepeatOffUnlessAutoplay();
1469 printf("Player %d %sactive.\n",
1470 i + 1, (stored_player[i].active ? "" : "not "));
1474 printf("::: starting game [%d]\n", FrameCounter);
1478 void InitMovDir(int x, int y)
1480 int i, element = Feld[x][y];
1481 static int xy[4][2] =
1488 static int direction[3][4] =
1490 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1491 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1492 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1501 Feld[x][y] = EL_BUG;
1502 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1505 case EL_SPACESHIP_RIGHT:
1506 case EL_SPACESHIP_UP:
1507 case EL_SPACESHIP_LEFT:
1508 case EL_SPACESHIP_DOWN:
1509 Feld[x][y] = EL_SPACESHIP;
1510 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1513 case EL_BD_BUTTERFLY_RIGHT:
1514 case EL_BD_BUTTERFLY_UP:
1515 case EL_BD_BUTTERFLY_LEFT:
1516 case EL_BD_BUTTERFLY_DOWN:
1517 Feld[x][y] = EL_BD_BUTTERFLY;
1518 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1521 case EL_BD_FIREFLY_RIGHT:
1522 case EL_BD_FIREFLY_UP:
1523 case EL_BD_FIREFLY_LEFT:
1524 case EL_BD_FIREFLY_DOWN:
1525 Feld[x][y] = EL_BD_FIREFLY;
1526 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1529 case EL_PACMAN_RIGHT:
1531 case EL_PACMAN_LEFT:
1532 case EL_PACMAN_DOWN:
1533 Feld[x][y] = EL_PACMAN;
1534 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1537 case EL_SP_SNIKSNAK:
1538 MovDir[x][y] = MV_UP;
1541 case EL_SP_ELECTRON:
1542 MovDir[x][y] = MV_LEFT;
1549 Feld[x][y] = EL_MOLE;
1550 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1554 if (IS_CUSTOM_ELEMENT(element))
1556 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1557 MovDir[x][y] = element_info[element].move_direction_initial;
1558 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1559 element_info[element].move_pattern == MV_TURNING_LEFT ||
1560 element_info[element].move_pattern == MV_TURNING_RIGHT)
1561 MovDir[x][y] = 1 << RND(4);
1562 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1563 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1564 else if (element_info[element].move_pattern == MV_VERTICAL)
1565 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1566 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1567 MovDir[x][y] = element_info[element].move_pattern;
1568 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1569 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1573 int x1 = x + xy[i][0];
1574 int y1 = y + xy[i][1];
1576 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1578 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1579 MovDir[x][y] = direction[0][i];
1581 MovDir[x][y] = direction[1][i];
1590 MovDir[x][y] = 1 << RND(4);
1592 if (element != EL_BUG &&
1593 element != EL_SPACESHIP &&
1594 element != EL_BD_BUTTERFLY &&
1595 element != EL_BD_FIREFLY)
1600 int x1 = x + xy[i][0];
1601 int y1 = y + xy[i][1];
1603 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1605 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1607 MovDir[x][y] = direction[0][i];
1610 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1611 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1613 MovDir[x][y] = direction[1][i];
1622 GfxDir[x][y] = MovDir[x][y];
1625 void InitAmoebaNr(int x, int y)
1628 int group_nr = AmoebeNachbarNr(x, y);
1632 for (i=1; i<MAX_NUM_AMOEBA; i++)
1634 if (AmoebaCnt[i] == 0)
1642 AmoebaNr[x][y] = group_nr;
1643 AmoebaCnt[group_nr]++;
1644 AmoebaCnt2[group_nr]++;
1650 boolean raise_level = FALSE;
1652 if (local_player->MovPos)
1656 if (tape.auto_play) /* tape might already be stopped here */
1657 tape.auto_play_level_solved = TRUE;
1659 if (tape.playing && tape.auto_play)
1660 tape.auto_play_level_solved = TRUE;
1663 local_player->LevelSolved = FALSE;
1665 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1669 if (!tape.playing && setup.sound_loops)
1670 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1671 SND_CTRL_PLAY_LOOP);
1673 while (TimeLeft > 0)
1675 if (!tape.playing && !setup.sound_loops)
1676 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1677 if (TimeLeft > 0 && !(TimeLeft % 10))
1678 RaiseScore(level.score[SC_TIME_BONUS]);
1679 if (TimeLeft > 100 && !(TimeLeft % 10))
1683 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1690 if (!tape.playing && setup.sound_loops)
1691 StopSound(SND_GAME_LEVELTIME_BONUS);
1693 else if (level.time == 0) /* level without time limit */
1695 if (!tape.playing && setup.sound_loops)
1696 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1697 SND_CTRL_PLAY_LOOP);
1699 while (TimePlayed < 999)
1701 if (!tape.playing && !setup.sound_loops)
1702 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1703 if (TimePlayed < 999 && !(TimePlayed % 10))
1704 RaiseScore(level.score[SC_TIME_BONUS]);
1705 if (TimePlayed < 900 && !(TimePlayed % 10))
1709 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1716 if (!tape.playing && setup.sound_loops)
1717 StopSound(SND_GAME_LEVELTIME_BONUS);
1720 /* close exit door after last player */
1721 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1722 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1724 int element = Feld[ExitX][ExitY];
1726 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1727 EL_SP_EXIT_CLOSING);
1729 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1732 /* Hero disappears */
1733 DrawLevelField(ExitX, ExitY);
1739 CloseDoor(DOOR_CLOSE_1);
1744 SaveTape(tape.level_nr); /* Ask to save tape */
1747 if (level_nr == leveldir_current->handicap_level)
1749 leveldir_current->handicap_level++;
1750 SaveLevelSetup_SeriesInfo();
1753 if (level_editor_test_game)
1754 local_player->score = -1; /* no highscore when playing from editor */
1755 else if (level_nr < leveldir_current->last_level)
1756 raise_level = TRUE; /* advance to next level */
1758 if ((hi_pos = NewHiScore()) >= 0)
1760 game_status = GAME_MODE_SCORES;
1761 DrawHallOfFame(hi_pos);
1770 game_status = GAME_MODE_MAIN;
1787 LoadScore(level_nr);
1789 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1790 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1793 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1795 if (local_player->score > highscore[k].Score)
1797 /* player has made it to the hall of fame */
1799 if (k < MAX_SCORE_ENTRIES - 1)
1801 int m = MAX_SCORE_ENTRIES - 1;
1804 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1805 if (!strcmp(setup.player_name, highscore[l].Name))
1807 if (m == k) /* player's new highscore overwrites his old one */
1813 strcpy(highscore[l].Name, highscore[l - 1].Name);
1814 highscore[l].Score = highscore[l - 1].Score;
1821 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1822 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1823 highscore[k].Score = local_player->score;
1829 else if (!strncmp(setup.player_name, highscore[k].Name,
1830 MAX_PLAYER_NAME_LEN))
1831 break; /* player already there with a higher score */
1837 SaveScore(level_nr);
1842 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1844 if (player->GfxAction != action || player->GfxDir != dir)
1847 printf("Player frame reset! (%d => %d, %d => %d)\n",
1848 player->GfxAction, action, player->GfxDir, dir);
1851 player->GfxAction = action;
1852 player->GfxDir = dir;
1854 player->StepFrame = 0;
1858 static void ResetRandomAnimationValue(int x, int y)
1860 GfxRandom[x][y] = INIT_GFX_RANDOM();
1863 static void ResetGfxAnimation(int x, int y)
1866 GfxAction[x][y] = ACTION_DEFAULT;
1867 GfxDir[x][y] = MovDir[x][y];
1870 void InitMovingField(int x, int y, int direction)
1872 int element = Feld[x][y];
1873 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1874 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1878 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1879 ResetGfxAnimation(x, y);
1881 MovDir[newx][newy] = MovDir[x][y] = direction;
1882 GfxDir[x][y] = direction;
1884 if (Feld[newx][newy] == EL_EMPTY)
1885 Feld[newx][newy] = EL_BLOCKED;
1887 if (direction == MV_DOWN && CAN_FALL(element))
1888 GfxAction[x][y] = ACTION_FALLING;
1890 GfxAction[x][y] = ACTION_MOVING;
1892 GfxFrame[newx][newy] = GfxFrame[x][y];
1893 GfxRandom[newx][newy] = GfxRandom[x][y];
1894 GfxAction[newx][newy] = GfxAction[x][y];
1895 GfxDir[newx][newy] = GfxDir[x][y];
1898 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1900 int direction = MovDir[x][y];
1901 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1902 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1908 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1910 int oldx = x, oldy = y;
1911 int direction = MovDir[x][y];
1913 if (direction == MV_LEFT)
1915 else if (direction == MV_RIGHT)
1917 else if (direction == MV_UP)
1919 else if (direction == MV_DOWN)
1922 *comes_from_x = oldx;
1923 *comes_from_y = oldy;
1926 int MovingOrBlocked2Element(int x, int y)
1928 int element = Feld[x][y];
1930 if (element == EL_BLOCKED)
1934 Blocked2Moving(x, y, &oldx, &oldy);
1935 return Feld[oldx][oldy];
1941 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1943 /* like MovingOrBlocked2Element(), but if element is moving
1944 and (x,y) is the field the moving element is just leaving,
1945 return EL_BLOCKED instead of the element value */
1946 int element = Feld[x][y];
1948 if (IS_MOVING(x, y))
1950 if (element == EL_BLOCKED)
1954 Blocked2Moving(x, y, &oldx, &oldy);
1955 return Feld[oldx][oldy];
1964 static void RemoveField(int x, int y)
1966 Feld[x][y] = EL_EMPTY;
1973 ChangeDelay[x][y] = 0;
1974 ChangePage[x][y] = -1;
1975 Pushed[x][y] = FALSE;
1977 GfxElement[x][y] = EL_UNDEFINED;
1978 GfxAction[x][y] = ACTION_DEFAULT;
1979 GfxDir[x][y] = MV_NO_MOVING;
1982 void RemoveMovingField(int x, int y)
1984 int oldx = x, oldy = y, newx = x, newy = y;
1985 int element = Feld[x][y];
1986 int next_element = EL_UNDEFINED;
1988 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1991 if (IS_MOVING(x, y))
1993 Moving2Blocked(x, y, &newx, &newy);
1994 if (Feld[newx][newy] != EL_BLOCKED)
1997 else if (element == EL_BLOCKED)
1999 Blocked2Moving(x, y, &oldx, &oldy);
2000 if (!IS_MOVING(oldx, oldy))
2004 if (element == EL_BLOCKED &&
2005 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2006 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2007 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2008 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2009 next_element = get_next_element(Feld[oldx][oldy]);
2011 RemoveField(oldx, oldy);
2012 RemoveField(newx, newy);
2014 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2016 if (next_element != EL_UNDEFINED)
2017 Feld[oldx][oldy] = next_element;
2019 DrawLevelField(oldx, oldy);
2020 DrawLevelField(newx, newy);
2023 void DrawDynamite(int x, int y)
2025 int sx = SCREENX(x), sy = SCREENY(y);
2026 int graphic = el2img(Feld[x][y]);
2029 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2032 if (IS_WALKABLE_INSIDE(Back[x][y]))
2036 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2037 else if (Store[x][y])
2038 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2040 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2043 if (Back[x][y] || Store[x][y])
2044 DrawGraphicThruMask(sx, sy, graphic, frame);
2046 DrawGraphic(sx, sy, graphic, frame);
2048 if (game.emulation == EMU_SUPAPLEX)
2049 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2050 else if (Store[x][y])
2051 DrawGraphicThruMask(sx, sy, graphic, frame);
2053 DrawGraphic(sx, sy, graphic, frame);
2057 void CheckDynamite(int x, int y)
2059 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2063 if (MovDelay[x][y] != 0)
2066 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2073 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2075 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2076 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2077 StopSound(SND_DYNAMITE_ACTIVE);
2079 StopSound(SND_DYNABOMB_ACTIVE);
2085 void RelocatePlayer(int x, int y, int element)
2087 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2090 RemoveField(x, y); /* temporarily remove newly placed player */
2091 DrawLevelField(x, y);
2094 if (player->present)
2096 while (player->MovPos)
2098 ScrollPlayer(player, SCROLL_GO_ON);
2099 ScrollScreen(NULL, SCROLL_GO_ON);
2105 Delay(GAME_FRAME_DELAY);
2108 DrawPlayer(player); /* needed here only to cleanup last field */
2109 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2111 player->is_moving = FALSE;
2114 Feld[x][y] = element;
2115 InitPlayerField(x, y, element, TRUE);
2117 if (player == local_player)
2119 int scroll_xx = -999, scroll_yy = -999;
2121 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2124 int fx = FX, fy = FY;
2126 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2127 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2128 local_player->jx - MIDPOSX);
2130 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2131 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2132 local_player->jy - MIDPOSY);
2134 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2135 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2140 fx += dx * TILEX / 2;
2141 fy += dy * TILEY / 2;
2143 ScrollLevel(dx, dy);
2146 /* scroll in two steps of half tile size to make things smoother */
2147 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2149 Delay(GAME_FRAME_DELAY);
2151 /* scroll second step to align at full tile size */
2153 Delay(GAME_FRAME_DELAY);
2158 void Explode(int ex, int ey, int phase, int mode)
2162 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2163 int last_phase = num_phase * delay;
2164 int half_phase = (num_phase / 2) * delay;
2165 int first_phase_after_start = EX_PHASE_START + 1;
2167 if (game.explosions_delayed)
2169 ExplodeField[ex][ey] = mode;
2173 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2175 int center_element = Feld[ex][ey];
2178 /* --- This is only really needed (and now handled) in "Impact()". --- */
2179 /* do not explode moving elements that left the explode field in time */
2180 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2181 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2185 if (mode == EX_NORMAL || mode == EX_CENTER)
2186 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2188 /* remove things displayed in background while burning dynamite */
2189 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2192 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2194 /* put moving element to center field (and let it explode there) */
2195 center_element = MovingOrBlocked2Element(ex, ey);
2196 RemoveMovingField(ex, ey);
2197 Feld[ex][ey] = center_element;
2200 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2202 int xx = x - ex + 1;
2203 int yy = y - ey + 1;
2206 if (!IN_LEV_FIELD(x, y) ||
2207 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2208 (x != ex || y != ey)))
2211 element = Feld[x][y];
2213 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2215 element = MovingOrBlocked2Element(x, y);
2217 if (!IS_EXPLOSION_PROOF(element))
2218 RemoveMovingField(x, y);
2224 if (IS_EXPLOSION_PROOF(element))
2227 /* indestructible elements can only explode in center (but not flames) */
2228 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2229 element == EL_FLAMES)
2234 if ((IS_INDESTRUCTIBLE(element) &&
2235 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2236 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2237 element == EL_FLAMES)
2241 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2243 if (IS_ACTIVE_BOMB(element))
2245 /* re-activate things under the bomb like gate or penguin */
2246 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2253 /* save walkable background elements while explosion on same tile */
2255 if (IS_INDESTRUCTIBLE(element))
2256 Back[x][y] = element;
2258 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2259 Back[x][y] = element;
2262 /* ignite explodable elements reached by other explosion */
2263 if (element == EL_EXPLOSION)
2264 element = Store2[x][y];
2267 if (AmoebaNr[x][y] &&
2268 (element == EL_AMOEBA_FULL ||
2269 element == EL_BD_AMOEBA ||
2270 element == EL_AMOEBA_GROWING))
2272 AmoebaCnt[AmoebaNr[x][y]]--;
2273 AmoebaCnt2[AmoebaNr[x][y]]--;
2279 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2281 switch(StorePlayer[ex][ey])
2284 Store[x][y] = EL_EMERALD_RED;
2287 Store[x][y] = EL_EMERALD;
2290 Store[x][y] = EL_EMERALD_PURPLE;
2294 Store[x][y] = EL_EMERALD_YELLOW;
2298 if (game.emulation == EMU_SUPAPLEX)
2299 Store[x][y] = EL_EMPTY;
2301 else if (center_element == EL_MOLE)
2302 Store[x][y] = EL_EMERALD_RED;
2303 else if (center_element == EL_PENGUIN)
2304 Store[x][y] = EL_EMERALD_PURPLE;
2305 else if (center_element == EL_BUG)
2306 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2307 else if (center_element == EL_BD_BUTTERFLY)
2308 Store[x][y] = EL_BD_DIAMOND;
2309 else if (center_element == EL_SP_ELECTRON)
2310 Store[x][y] = EL_SP_INFOTRON;
2311 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2312 Store[x][y] = level.amoeba_content;
2313 else if (center_element == EL_YAMYAM)
2314 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2315 else if (IS_CUSTOM_ELEMENT(center_element) &&
2316 element_info[center_element].content[xx][yy] != EL_EMPTY)
2317 Store[x][y] = element_info[center_element].content[xx][yy];
2318 else if (element == EL_WALL_EMERALD)
2319 Store[x][y] = EL_EMERALD;
2320 else if (element == EL_WALL_DIAMOND)
2321 Store[x][y] = EL_DIAMOND;
2322 else if (element == EL_WALL_BD_DIAMOND)
2323 Store[x][y] = EL_BD_DIAMOND;
2324 else if (element == EL_WALL_EMERALD_YELLOW)
2325 Store[x][y] = EL_EMERALD_YELLOW;
2326 else if (element == EL_WALL_EMERALD_RED)
2327 Store[x][y] = EL_EMERALD_RED;
2328 else if (element == EL_WALL_EMERALD_PURPLE)
2329 Store[x][y] = EL_EMERALD_PURPLE;
2330 else if (element == EL_WALL_PEARL)
2331 Store[x][y] = EL_PEARL;
2332 else if (element == EL_WALL_CRYSTAL)
2333 Store[x][y] = EL_CRYSTAL;
2334 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2335 Store[x][y] = element_info[element].content[1][1];
2337 Store[x][y] = EL_EMPTY;
2339 if (x != ex || y != ey ||
2340 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2341 Store2[x][y] = element;
2344 if (AmoebaNr[x][y] &&
2345 (element == EL_AMOEBA_FULL ||
2346 element == EL_BD_AMOEBA ||
2347 element == EL_AMOEBA_GROWING))
2349 AmoebaCnt[AmoebaNr[x][y]]--;
2350 AmoebaCnt2[AmoebaNr[x][y]]--;
2356 MovDir[x][y] = MovPos[x][y] = 0;
2357 GfxDir[x][y] = MovDir[x][y];
2362 Feld[x][y] = EL_EXPLOSION;
2364 GfxElement[x][y] = center_element;
2366 GfxElement[x][y] = EL_UNDEFINED;
2369 ExplodePhase[x][y] = 1;
2373 if (center_element == EL_YAMYAM)
2374 game.yamyam_content_nr =
2375 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2386 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2390 /* activate this even in non-DEBUG version until cause for crash in
2391 getGraphicAnimationFrame() (see below) is found and eliminated */
2395 if (GfxElement[x][y] == EL_UNDEFINED)
2398 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2399 printf("Explode(): This should never happen!\n");
2402 GfxElement[x][y] = EL_EMPTY;
2406 if (phase == first_phase_after_start)
2408 int element = Store2[x][y];
2410 if (element == EL_BLACK_ORB)
2412 Feld[x][y] = Store2[x][y];
2417 else if (phase == half_phase)
2419 int element = Store2[x][y];
2421 if (IS_PLAYER(x, y))
2422 KillHeroUnlessProtected(x, y);
2423 else if (CAN_EXPLODE_BY_FIRE(element))
2425 Feld[x][y] = Store2[x][y];
2429 else if (element == EL_AMOEBA_TO_DIAMOND)
2430 AmoebeUmwandeln(x, y);
2433 if (phase == last_phase)
2437 element = Feld[x][y] = Store[x][y];
2438 Store[x][y] = Store2[x][y] = 0;
2439 GfxElement[x][y] = EL_UNDEFINED;
2441 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2442 element = Feld[x][y] = Back[x][y];
2445 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2446 GfxDir[x][y] = MV_NO_MOVING;
2447 ChangeDelay[x][y] = 0;
2448 ChangePage[x][y] = -1;
2450 InitField(x, y, FALSE);
2451 if (CAN_MOVE(element))
2453 DrawLevelField(x, y);
2455 TestIfElementTouchesCustomElement(x, y);
2457 if (GFX_CRUMBLED(element))
2458 DrawLevelFieldCrumbledSandNeighbours(x, y);
2460 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2461 StorePlayer[x][y] = 0;
2463 if (ELEM_IS_PLAYER(element))
2464 RelocatePlayer(x, y, element);
2466 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2469 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2471 int stored = Store[x][y];
2472 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2473 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2476 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2479 DrawLevelFieldCrumbledSand(x, y);
2481 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2483 DrawLevelElement(x, y, Back[x][y]);
2484 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2486 else if (IS_WALKABLE_UNDER(Back[x][y]))
2488 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2489 DrawLevelElementThruMask(x, y, Back[x][y]);
2491 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2492 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2496 void DynaExplode(int ex, int ey)
2499 int dynabomb_size = 1;
2500 boolean dynabomb_xl = FALSE;
2501 struct PlayerInfo *player;
2502 static int xy[4][2] =
2510 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2512 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2513 dynabomb_size = player->dynabomb_size;
2514 dynabomb_xl = player->dynabomb_xl;
2515 player->dynabombs_left++;
2518 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2522 for (j=1; j<=dynabomb_size; j++)
2524 int x = ex + j * xy[i % 4][0];
2525 int y = ey + j * xy[i % 4][1];
2528 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2531 element = Feld[x][y];
2533 /* do not restart explosions of fields with active bombs */
2534 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2537 Explode(x, y, EX_PHASE_START, EX_BORDER);
2539 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2540 if (element != EL_EMPTY &&
2541 element != EL_SAND &&
2542 element != EL_EXPLOSION &&
2549 void Bang(int x, int y)
2552 int element = MovingOrBlocked2Element(x, y);
2554 int element = Feld[x][y];
2558 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2560 if (IS_PLAYER(x, y))
2563 struct PlayerInfo *player = PLAYERINFO(x, y);
2565 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2566 player->element_nr);
2571 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2573 if (game.emulation == EMU_SUPAPLEX)
2574 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2576 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2581 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2589 case EL_BD_BUTTERFLY:
2592 case EL_DARK_YAMYAM:
2596 RaiseScoreElement(element);
2597 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2599 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2600 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2601 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2602 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2603 case EL_DYNABOMB_INCREASE_NUMBER:
2604 case EL_DYNABOMB_INCREASE_SIZE:
2605 case EL_DYNABOMB_INCREASE_POWER:
2610 case EL_LAMP_ACTIVE:
2611 if (IS_PLAYER(x, y))
2612 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2614 Explode(x, y, EX_PHASE_START, EX_CENTER);
2617 if (CAN_EXPLODE_1X1(element))
2618 Explode(x, y, EX_PHASE_START, EX_CENTER);
2620 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2624 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2627 void SplashAcid(int x, int y)
2629 int element = Feld[x][y];
2631 if (element != EL_ACID_SPLASH_LEFT &&
2632 element != EL_ACID_SPLASH_RIGHT)
2634 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2636 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2637 (!IN_LEV_FIELD(x-1, y-1) ||
2638 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2639 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2641 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2642 (!IN_LEV_FIELD(x+1, y-1) ||
2643 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2644 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2648 static void InitBeltMovement()
2650 static int belt_base_element[4] =
2652 EL_CONVEYOR_BELT_1_LEFT,
2653 EL_CONVEYOR_BELT_2_LEFT,
2654 EL_CONVEYOR_BELT_3_LEFT,
2655 EL_CONVEYOR_BELT_4_LEFT
2657 static int belt_base_active_element[4] =
2659 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2660 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2661 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2662 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2667 /* set frame order for belt animation graphic according to belt direction */
2674 int element = belt_base_active_element[belt_nr] + j;
2675 int graphic = el2img(element);
2677 if (game.belt_dir[i] == MV_LEFT)
2678 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2680 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2684 for(y=0; y<lev_fieldy; y++)
2686 for(x=0; x<lev_fieldx; x++)
2688 int element = Feld[x][y];
2692 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2694 int e_belt_nr = getBeltNrFromBeltElement(element);
2697 if (e_belt_nr == belt_nr)
2699 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2701 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2709 static void ToggleBeltSwitch(int x, int y)
2711 static int belt_base_element[4] =
2713 EL_CONVEYOR_BELT_1_LEFT,
2714 EL_CONVEYOR_BELT_2_LEFT,
2715 EL_CONVEYOR_BELT_3_LEFT,
2716 EL_CONVEYOR_BELT_4_LEFT
2718 static int belt_base_active_element[4] =
2720 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2721 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2722 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2723 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2725 static int belt_base_switch_element[4] =
2727 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2728 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2729 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2730 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2732 static int belt_move_dir[4] =
2740 int element = Feld[x][y];
2741 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2742 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2743 int belt_dir = belt_move_dir[belt_dir_nr];
2746 if (!IS_BELT_SWITCH(element))
2749 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2750 game.belt_dir[belt_nr] = belt_dir;
2752 if (belt_dir_nr == 3)
2755 /* set frame order for belt animation graphic according to belt direction */
2758 int element = belt_base_active_element[belt_nr] + i;
2759 int graphic = el2img(element);
2761 if (belt_dir == MV_LEFT)
2762 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2764 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2767 for (yy=0; yy<lev_fieldy; yy++)
2769 for (xx=0; xx<lev_fieldx; xx++)
2771 int element = Feld[xx][yy];
2773 if (IS_BELT_SWITCH(element))
2775 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2777 if (e_belt_nr == belt_nr)
2779 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2780 DrawLevelField(xx, yy);
2783 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2785 int e_belt_nr = getBeltNrFromBeltElement(element);
2787 if (e_belt_nr == belt_nr)
2789 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2791 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2792 DrawLevelField(xx, yy);
2795 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2797 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2799 if (e_belt_nr == belt_nr)
2801 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2803 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2804 DrawLevelField(xx, yy);
2811 static void ToggleSwitchgateSwitch(int x, int y)
2815 game.switchgate_pos = !game.switchgate_pos;
2817 for (yy=0; yy<lev_fieldy; yy++)
2819 for (xx=0; xx<lev_fieldx; xx++)
2821 int element = Feld[xx][yy];
2823 if (element == EL_SWITCHGATE_SWITCH_UP ||
2824 element == EL_SWITCHGATE_SWITCH_DOWN)
2826 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2827 DrawLevelField(xx, yy);
2829 else if (element == EL_SWITCHGATE_OPEN ||
2830 element == EL_SWITCHGATE_OPENING)
2832 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2834 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2836 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2839 else if (element == EL_SWITCHGATE_CLOSED ||
2840 element == EL_SWITCHGATE_CLOSING)
2842 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2844 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2846 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2853 static int getInvisibleActiveFromInvisibleElement(int element)
2855 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2856 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2857 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2861 static int getInvisibleFromInvisibleActiveElement(int element)
2863 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2864 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2865 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2869 static void RedrawAllLightSwitchesAndInvisibleElements()
2873 for (y=0; y<lev_fieldy; y++)
2875 for (x=0; x<lev_fieldx; x++)
2877 int element = Feld[x][y];
2879 if (element == EL_LIGHT_SWITCH &&
2880 game.light_time_left > 0)
2882 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2883 DrawLevelField(x, y);
2885 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2886 game.light_time_left == 0)
2888 Feld[x][y] = EL_LIGHT_SWITCH;
2889 DrawLevelField(x, y);
2891 else if (element == EL_INVISIBLE_STEELWALL ||
2892 element == EL_INVISIBLE_WALL ||
2893 element == EL_INVISIBLE_SAND)
2895 if (game.light_time_left > 0)
2896 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2898 DrawLevelField(x, y);
2900 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2901 element == EL_INVISIBLE_WALL_ACTIVE ||
2902 element == EL_INVISIBLE_SAND_ACTIVE)
2904 if (game.light_time_left == 0)
2905 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2907 DrawLevelField(x, y);
2913 static void ToggleLightSwitch(int x, int y)
2915 int element = Feld[x][y];
2917 game.light_time_left =
2918 (element == EL_LIGHT_SWITCH ?
2919 level.time_light * FRAMES_PER_SECOND : 0);
2921 RedrawAllLightSwitchesAndInvisibleElements();
2924 static void ActivateTimegateSwitch(int x, int y)
2928 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2930 for (yy=0; yy<lev_fieldy; yy++)
2932 for (xx=0; xx<lev_fieldx; xx++)
2934 int element = Feld[xx][yy];
2936 if (element == EL_TIMEGATE_CLOSED ||
2937 element == EL_TIMEGATE_CLOSING)
2939 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2940 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
2944 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2946 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2947 DrawLevelField(xx, yy);
2954 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2957 inline static int getElementMoveStepsize(int x, int y)
2959 int element = Feld[x][y];
2960 int direction = MovDir[x][y];
2961 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2962 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2963 int horiz_move = (dx != 0);
2964 int sign = (horiz_move ? dx : dy);
2965 int step = sign * element_info[element].move_stepsize;
2967 /* special values for move stepsize for spring and things on conveyor belt */
2970 if (CAN_FALL(element) &&
2971 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2972 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2973 else if (element == EL_SPRING)
2974 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2980 void Impact(int x, int y)
2982 boolean lastline = (y == lev_fieldy-1);
2983 boolean object_hit = FALSE;
2984 boolean impact = (lastline || object_hit);
2985 int element = Feld[x][y];
2986 int smashed = EL_UNDEFINED;
2988 if (!lastline) /* check if element below was hit */
2990 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2993 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2994 MovDir[x][y + 1] != MV_DOWN ||
2995 MovPos[x][y + 1] <= TILEY / 2));
2997 /* do not smash moving elements that left the smashed field in time */
2998 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2999 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3003 smashed = MovingOrBlocked2Element(x, y + 1);
3005 impact = (lastline || object_hit);
3008 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3014 /* only reset graphic animation if graphic really changes after impact */
3016 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3018 ResetGfxAnimation(x, y);
3019 DrawLevelField(x, y);
3022 if (impact && CAN_EXPLODE_IMPACT(element))
3027 else if (impact && element == EL_PEARL)
3029 Feld[x][y] = EL_PEARL_BREAKING;
3030 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3033 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3035 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3040 if (impact && element == EL_AMOEBA_DROP)
3042 if (object_hit && IS_PLAYER(x, y + 1))
3043 KillHeroUnlessProtected(x, y + 1);
3044 else if (object_hit && smashed == EL_PENGUIN)
3048 Feld[x][y] = EL_AMOEBA_GROWING;
3049 Store[x][y] = EL_AMOEBA_WET;
3051 ResetRandomAnimationValue(x, y);
3056 if (object_hit) /* check which object was hit */
3058 if (CAN_PASS_MAGIC_WALL(element) &&
3059 (smashed == EL_MAGIC_WALL ||
3060 smashed == EL_BD_MAGIC_WALL))
3063 int activated_magic_wall =
3064 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3065 EL_BD_MAGIC_WALL_ACTIVE);
3067 /* activate magic wall / mill */
3068 for (yy=0; yy<lev_fieldy; yy++)
3069 for (xx=0; xx<lev_fieldx; xx++)
3070 if (Feld[xx][yy] == smashed)
3071 Feld[xx][yy] = activated_magic_wall;
3073 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3074 game.magic_wall_active = TRUE;
3076 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3077 SND_MAGIC_WALL_ACTIVATING :
3078 SND_BD_MAGIC_WALL_ACTIVATING));
3081 if (IS_PLAYER(x, y + 1))
3083 if (CAN_SMASH_PLAYER(element))
3085 KillHeroUnlessProtected(x, y + 1);
3089 else if (smashed == EL_PENGUIN)
3091 if (CAN_SMASH_PLAYER(element))
3097 else if (element == EL_BD_DIAMOND)
3099 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3105 else if ((element == EL_SP_INFOTRON ||
3106 element == EL_SP_ZONK) &&
3107 (smashed == EL_SP_SNIKSNAK ||
3108 smashed == EL_SP_ELECTRON ||
3109 smashed == EL_SP_DISK_ORANGE))
3115 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3121 else if (CAN_SMASH_EVERYTHING(element))
3123 if (IS_CLASSIC_ENEMY(smashed) ||
3124 CAN_EXPLODE_SMASHED(smashed))
3129 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3131 if (smashed == EL_LAMP ||
3132 smashed == EL_LAMP_ACTIVE)
3137 else if (smashed == EL_NUT)
3139 Feld[x][y + 1] = EL_NUT_BREAKING;
3140 PlayLevelSound(x, y, SND_NUT_BREAKING);
3141 RaiseScoreElement(EL_NUT);
3144 else if (smashed == EL_PEARL)
3146 Feld[x][y + 1] = EL_PEARL_BREAKING;
3147 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3150 else if (smashed == EL_DIAMOND)
3152 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3153 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3156 else if (IS_BELT_SWITCH(smashed))
3158 ToggleBeltSwitch(x, y + 1);
3160 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3161 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3163 ToggleSwitchgateSwitch(x, y + 1);
3165 else if (smashed == EL_LIGHT_SWITCH ||
3166 smashed == EL_LIGHT_SWITCH_ACTIVE)
3168 ToggleLightSwitch(x, y + 1);
3172 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3174 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3175 CE_OTHER_IS_SWITCHING);
3176 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3182 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3187 /* play sound of magic wall / mill */
3189 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3190 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3192 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3193 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3194 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3195 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3200 /* play sound of object that hits the ground */
3201 if (lastline || object_hit)
3202 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3205 inline static void TurnRoundExt(int x, int y)
3217 { 0, 0 }, { 0, 0 }, { 0, 0 },
3222 int left, right, back;
3226 { MV_DOWN, MV_UP, MV_RIGHT },
3227 { MV_UP, MV_DOWN, MV_LEFT },
3229 { MV_LEFT, MV_RIGHT, MV_DOWN },
3233 { MV_RIGHT, MV_LEFT, MV_UP }
3236 int element = Feld[x][y];
3237 int old_move_dir = MovDir[x][y];
3238 int left_dir = turn[old_move_dir].left;
3239 int right_dir = turn[old_move_dir].right;
3240 int back_dir = turn[old_move_dir].back;
3242 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3243 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3244 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3245 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3247 int left_x = x + left_dx, left_y = y + left_dy;
3248 int right_x = x + right_dx, right_y = y + right_dy;
3249 int move_x = x + move_dx, move_y = y + move_dy;
3253 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3255 TestIfBadThingTouchesOtherBadThing(x, y);
3257 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3258 MovDir[x][y] = right_dir;
3259 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3260 MovDir[x][y] = left_dir;
3262 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3264 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3267 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3268 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3270 TestIfBadThingTouchesOtherBadThing(x, y);
3272 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3273 MovDir[x][y] = left_dir;
3274 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3275 MovDir[x][y] = right_dir;
3277 if ((element == EL_SPACESHIP ||
3278 element == EL_SP_SNIKSNAK ||
3279 element == EL_SP_ELECTRON)
3280 && MovDir[x][y] != old_move_dir)
3282 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3285 else if (element == EL_YAMYAM)
3287 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3288 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3290 if (can_turn_left && can_turn_right)
3291 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3292 else if (can_turn_left)
3293 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3294 else if (can_turn_right)
3295 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3297 MovDir[x][y] = back_dir;
3299 MovDelay[x][y] = 16 + 16 * RND(3);
3301 else if (element == EL_DARK_YAMYAM)
3303 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3304 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3306 if (can_turn_left && can_turn_right)
3307 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3308 else if (can_turn_left)
3309 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3310 else if (can_turn_right)
3311 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3313 MovDir[x][y] = back_dir;
3315 MovDelay[x][y] = 16 + 16 * RND(3);
3317 else if (element == EL_PACMAN)
3319 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3320 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3322 if (can_turn_left && can_turn_right)
3323 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3324 else if (can_turn_left)
3325 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3326 else if (can_turn_right)
3327 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3329 MovDir[x][y] = back_dir;
3331 MovDelay[x][y] = 6 + RND(40);
3333 else if (element == EL_PIG)
3335 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3336 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3337 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3338 boolean should_turn_left, should_turn_right, should_move_on;
3340 int rnd = RND(rnd_value);
3342 should_turn_left = (can_turn_left &&
3344 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3345 y + back_dy + left_dy)));
3346 should_turn_right = (can_turn_right &&
3348 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3349 y + back_dy + right_dy)));
3350 should_move_on = (can_move_on &&
3353 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3354 y + move_dy + left_dy) ||
3355 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3356 y + move_dy + right_dy)));
3358 if (should_turn_left || should_turn_right || should_move_on)
3360 if (should_turn_left && should_turn_right && should_move_on)
3361 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3362 rnd < 2 * rnd_value / 3 ? right_dir :
3364 else if (should_turn_left && should_turn_right)
3365 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3366 else if (should_turn_left && should_move_on)
3367 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3368 else if (should_turn_right && should_move_on)
3369 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3370 else if (should_turn_left)
3371 MovDir[x][y] = left_dir;
3372 else if (should_turn_right)
3373 MovDir[x][y] = right_dir;
3374 else if (should_move_on)
3375 MovDir[x][y] = old_move_dir;
3377 else if (can_move_on && rnd > rnd_value / 8)
3378 MovDir[x][y] = old_move_dir;
3379 else if (can_turn_left && can_turn_right)
3380 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3381 else if (can_turn_left && rnd > rnd_value / 8)
3382 MovDir[x][y] = left_dir;
3383 else if (can_turn_right && rnd > rnd_value/8)
3384 MovDir[x][y] = right_dir;
3386 MovDir[x][y] = back_dir;
3388 xx = x + move_xy[MovDir[x][y]].x;
3389 yy = y + move_xy[MovDir[x][y]].y;
3391 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3392 MovDir[x][y] = old_move_dir;
3396 else if (element == EL_DRAGON)
3398 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3399 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3400 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3402 int rnd = RND(rnd_value);
3405 if (FrameCounter < 1 && x == 0 && y == 29)
3406 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3409 if (can_move_on && rnd > rnd_value / 8)
3410 MovDir[x][y] = old_move_dir;
3411 else if (can_turn_left && can_turn_right)
3412 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3413 else if (can_turn_left && rnd > rnd_value / 8)
3414 MovDir[x][y] = left_dir;
3415 else if (can_turn_right && rnd > rnd_value / 8)
3416 MovDir[x][y] = right_dir;
3418 MovDir[x][y] = back_dir;
3420 xx = x + move_xy[MovDir[x][y]].x;
3421 yy = y + move_xy[MovDir[x][y]].y;
3424 if (FrameCounter < 1 && x == 0 && y == 29)
3425 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3426 xx, yy, Feld[xx][yy],
3431 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3432 MovDir[x][y] = old_move_dir;
3434 if (!IS_FREE(xx, yy))
3435 MovDir[x][y] = old_move_dir;
3439 if (FrameCounter < 1 && x == 0 && y == 29)
3440 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3445 else if (element == EL_MOLE)
3447 boolean can_move_on =
3448 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3449 IS_AMOEBOID(Feld[move_x][move_y]) ||
3450 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3453 boolean can_turn_left =
3454 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3455 IS_AMOEBOID(Feld[left_x][left_y])));
3457 boolean can_turn_right =
3458 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3459 IS_AMOEBOID(Feld[right_x][right_y])));
3461 if (can_turn_left && can_turn_right)
3462 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3463 else if (can_turn_left)
3464 MovDir[x][y] = left_dir;
3466 MovDir[x][y] = right_dir;
3469 if (MovDir[x][y] != old_move_dir)
3472 else if (element == EL_BALLOON)
3474 MovDir[x][y] = game.balloon_dir;
3477 else if (element == EL_SPRING)
3479 if (MovDir[x][y] & MV_HORIZONTAL &&
3480 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3481 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3482 MovDir[x][y] = MV_NO_MOVING;
3486 else if (element == EL_ROBOT ||
3487 element == EL_SATELLITE ||
3488 element == EL_PENGUIN)
3490 int attr_x = -1, attr_y = -1;
3501 for (i=0; i<MAX_PLAYERS; i++)
3503 struct PlayerInfo *player = &stored_player[i];
3504 int jx = player->jx, jy = player->jy;
3506 if (!player->active)
3510 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3518 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3524 if (element == EL_PENGUIN)
3527 static int xy[4][2] =
3537 int ex = x + xy[i % 4][0];
3538 int ey = y + xy[i % 4][1];
3540 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3549 MovDir[x][y] = MV_NO_MOVING;
3551 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3552 else if (attr_x > x)
3553 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3555 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3556 else if (attr_y > y)
3557 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3559 if (element == EL_ROBOT)
3563 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3564 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3565 Moving2Blocked(x, y, &newx, &newy);
3567 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3568 MovDelay[x][y] = 8 + 8 * !RND(3);
3570 MovDelay[x][y] = 16;
3572 else if (element == EL_PENGUIN)
3578 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3580 boolean first_horiz = RND(2);
3581 int new_move_dir = MovDir[x][y];
3584 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3585 Moving2Blocked(x, y, &newx, &newy);
3587 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3591 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3592 Moving2Blocked(x, y, &newx, &newy);
3594 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3597 MovDir[x][y] = old_move_dir;
3601 else /* (element == EL_SATELLITE) */
3607 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3609 boolean first_horiz = RND(2);
3610 int new_move_dir = MovDir[x][y];
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))
3620 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3621 Moving2Blocked(x, y, &newx, &newy);
3623 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3626 MovDir[x][y] = old_move_dir;
3631 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3632 element_info[element].move_pattern == MV_TURNING_LEFT ||
3633 element_info[element].move_pattern == MV_TURNING_RIGHT)
3635 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3636 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3638 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3639 MovDir[x][y] = left_dir;
3640 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3641 MovDir[x][y] = right_dir;
3642 else if (can_turn_left && can_turn_right)
3643 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3644 else if (can_turn_left)
3645 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3646 else if (can_turn_right)
3647 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3649 MovDir[x][y] = back_dir;
3651 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3653 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3654 element_info[element].move_pattern == MV_VERTICAL)
3656 if (element_info[element].move_pattern & old_move_dir)
3657 MovDir[x][y] = back_dir;
3658 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3659 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3660 else if (element_info[element].move_pattern == MV_VERTICAL)
3661 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3663 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3665 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3667 MovDir[x][y] = element_info[element].move_pattern;
3668 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3670 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3672 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3673 MovDir[x][y] = left_dir;
3674 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3675 MovDir[x][y] = right_dir;
3677 if (MovDir[x][y] != old_move_dir)
3678 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3680 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3682 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3683 MovDir[x][y] = right_dir;
3684 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3685 MovDir[x][y] = left_dir;
3687 if (MovDir[x][y] != old_move_dir)
3688 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3690 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3691 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3693 int attr_x = -1, attr_y = -1;
3696 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3707 for (i=0; i<MAX_PLAYERS; i++)
3709 struct PlayerInfo *player = &stored_player[i];
3710 int jx = player->jx, jy = player->jy;
3712 if (!player->active)
3716 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3724 MovDir[x][y] = MV_NO_MOVING;
3726 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3727 else if (attr_x > x)
3728 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3730 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3731 else if (attr_y > y)
3732 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3734 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3736 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3738 boolean first_horiz = RND(2);
3739 int new_move_dir = MovDir[x][y];
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))
3749 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3750 Moving2Blocked(x, y, &newx, &newy);
3752 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3755 MovDir[x][y] = old_move_dir;
3758 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3760 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3761 MovDir[x][y] = MV_NO_MOVING;
3767 static void TurnRound(int x, int y)
3769 int direction = MovDir[x][y];
3772 GfxDir[x][y] = MovDir[x][y];
3778 GfxDir[x][y] = MovDir[x][y];
3781 if (direction != MovDir[x][y])
3786 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3789 GfxAction[x][y] = ACTION_WAITING;
3793 static boolean JustBeingPushed(int x, int y)
3797 for (i=0; i<MAX_PLAYERS; i++)
3799 struct PlayerInfo *player = &stored_player[i];
3801 if (player->active && player->is_pushing && player->MovPos)
3803 int next_jx = player->jx + (player->jx - player->last_jx);
3804 int next_jy = player->jy + (player->jy - player->last_jy);
3806 if (x == next_jx && y == next_jy)
3814 void StartMoving(int x, int y)
3816 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3817 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3818 int element = Feld[x][y];
3824 if (MovDelay[x][y] == 0)
3825 GfxAction[x][y] = ACTION_DEFAULT;
3827 /* !!! this should be handled more generic (not only for mole) !!! */
3828 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3829 GfxAction[x][y] = ACTION_DEFAULT;
3832 if (CAN_FALL(element) && y < lev_fieldy - 1)
3834 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3835 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3836 if (JustBeingPushed(x, y))
3839 if (element == EL_QUICKSAND_FULL)
3841 if (IS_FREE(x, y + 1))
3843 InitMovingField(x, y, MV_DOWN);
3844 started_moving = TRUE;
3846 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3847 Store[x][y] = EL_ROCK;
3849 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
3851 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
3854 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3856 if (!MovDelay[x][y])
3857 MovDelay[x][y] = TILEY + 1;
3866 Feld[x][y] = EL_QUICKSAND_EMPTY;
3867 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3868 Store[x][y + 1] = Store[x][y];
3871 PlayLevelSoundAction(x, y, ACTION_FILLING);
3873 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
3877 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3878 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3880 InitMovingField(x, y, MV_DOWN);
3881 started_moving = TRUE;
3883 Feld[x][y] = EL_QUICKSAND_FILLING;
3884 Store[x][y] = element;
3886 PlayLevelSoundAction(x, y, ACTION_FILLING);
3888 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
3891 else if (element == EL_MAGIC_WALL_FULL)
3893 if (IS_FREE(x, y + 1))
3895 InitMovingField(x, y, MV_DOWN);
3896 started_moving = TRUE;
3898 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3899 Store[x][y] = EL_CHANGED(Store[x][y]);
3901 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3903 if (!MovDelay[x][y])
3904 MovDelay[x][y] = TILEY/4 + 1;
3913 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3914 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3915 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3919 else if (element == EL_BD_MAGIC_WALL_FULL)
3921 if (IS_FREE(x, y + 1))
3923 InitMovingField(x, y, MV_DOWN);
3924 started_moving = TRUE;
3926 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3927 Store[x][y] = EL_CHANGED2(Store[x][y]);
3929 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3931 if (!MovDelay[x][y])
3932 MovDelay[x][y] = TILEY/4 + 1;
3941 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3942 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3943 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3947 else if (CAN_PASS_MAGIC_WALL(element) &&
3948 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3949 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3951 InitMovingField(x, y, MV_DOWN);
3952 started_moving = TRUE;
3955 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3956 EL_BD_MAGIC_WALL_FILLING);
3957 Store[x][y] = element;
3960 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3962 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3967 InitMovingField(x, y, MV_DOWN);
3968 started_moving = TRUE;
3970 Store[x][y] = EL_ACID;
3972 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3973 GfxAction[x][y + 1] = ACTION_ACTIVE;
3977 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3978 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3979 (Feld[x][y + 1] == EL_BLOCKED)) ||
3980 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3981 CAN_SMASH(element) && WasJustFalling[x][y] &&
3982 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3986 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3987 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3988 WasJustMoving[x][y] && !Pushed[x][y + 1])
3990 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3991 WasJustMoving[x][y])
3996 /* this is needed for a special case not covered by calling "Impact()"
3997 from "ContinueMoving()": if an element moves to a tile directly below
3998 another element which was just falling on that tile (which was empty
3999 in the previous frame), the falling element above would just stop
4000 instead of smashing the element below (in previous version, the above
4001 element was just checked for "moving" instead of "falling", resulting
4002 in incorrect smashes caused by horizontal movement of the above
4003 element; also, the case of the player being the element to smash was
4004 simply not covered here... :-/ ) */
4008 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4010 if (MovDir[x][y] == MV_NO_MOVING)
4012 InitMovingField(x, y, MV_DOWN);
4013 started_moving = TRUE;
4016 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4018 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4019 MovDir[x][y] = MV_DOWN;
4021 InitMovingField(x, y, MV_DOWN);
4022 started_moving = TRUE;
4024 else if (element == EL_AMOEBA_DROP)
4026 Feld[x][y] = EL_AMOEBA_GROWING;
4027 Store[x][y] = EL_AMOEBA_WET;
4029 /* Store[x][y + 1] must be zero, because:
4030 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4033 #if OLD_GAME_BEHAVIOUR
4034 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4036 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4037 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4038 element != EL_DX_SUPABOMB)
4041 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4042 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4043 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4044 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4047 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4048 (IS_FREE(x - 1, y + 1) ||
4049 Feld[x - 1][y + 1] == EL_ACID));
4050 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4051 (IS_FREE(x + 1, y + 1) ||
4052 Feld[x + 1][y + 1] == EL_ACID));
4053 boolean can_fall_any = (can_fall_left || can_fall_right);
4054 boolean can_fall_both = (can_fall_left && can_fall_right);
4056 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4058 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4060 if (slippery_type == SLIPPERY_ONLY_LEFT)
4061 can_fall_right = FALSE;
4062 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4063 can_fall_left = FALSE;
4064 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4065 can_fall_right = FALSE;
4066 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4067 can_fall_left = FALSE;
4069 can_fall_any = (can_fall_left || can_fall_right);
4070 can_fall_both = (can_fall_left && can_fall_right);
4075 if (can_fall_both &&
4076 (game.emulation != EMU_BOULDERDASH &&
4077 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4078 can_fall_left = !(can_fall_right = RND(2));
4080 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4081 started_moving = TRUE;
4084 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4086 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4087 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4088 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4089 int belt_dir = game.belt_dir[belt_nr];
4091 if ((belt_dir == MV_LEFT && left_is_free) ||
4092 (belt_dir == MV_RIGHT && right_is_free))
4094 InitMovingField(x, y, belt_dir);
4095 started_moving = TRUE;
4097 GfxAction[x][y] = ACTION_DEFAULT;
4102 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4103 if (CAN_MOVE(element) && !started_moving)
4108 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4111 if ((element == EL_SATELLITE ||
4112 element == EL_BALLOON ||
4113 element == EL_SPRING)
4114 && JustBeingPushed(x, y))
4120 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4121 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4123 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4125 Moving2Blocked(x, y, &newx, &newy);
4126 if (Feld[newx][newy] == EL_BLOCKED)
4127 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4133 if (FrameCounter < 1 && x == 0 && y == 29)
4134 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4137 if (!MovDelay[x][y]) /* start new movement phase */
4139 /* all objects that can change their move direction after each step
4140 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4142 if (element != EL_YAMYAM &&
4143 element != EL_DARK_YAMYAM &&
4144 element != EL_PACMAN &&
4145 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4146 element_info[element].move_pattern != MV_TURNING_LEFT &&
4147 element_info[element].move_pattern != MV_TURNING_RIGHT)
4152 if (FrameCounter < 1 && x == 0 && y == 29)
4153 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4156 if (MovDelay[x][y] && (element == EL_BUG ||
4157 element == EL_SPACESHIP ||
4158 element == EL_SP_SNIKSNAK ||
4159 element == EL_SP_ELECTRON ||
4160 element == EL_MOLE))
4161 DrawLevelField(x, y);
4165 if (MovDelay[x][y]) /* wait some time before next movement */
4170 if (element == EL_YAMYAM)
4173 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4174 DrawLevelElementAnimation(x, y, element);
4178 if (MovDelay[x][y]) /* element still has to wait some time */
4181 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4182 ResetGfxAnimation(x, y);
4186 if (GfxAction[x][y] != ACTION_WAITING)
4187 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4189 GfxAction[x][y] = ACTION_WAITING;
4193 if (element == EL_ROBOT ||
4195 element == EL_PACMAN ||
4197 element == EL_YAMYAM ||
4198 element == EL_DARK_YAMYAM)
4201 DrawLevelElementAnimation(x, y, element);
4203 DrawLevelElementAnimationIfNeeded(x, y, element);
4205 PlayLevelSoundAction(x, y, ACTION_WAITING);
4207 else if (element == EL_SP_ELECTRON)
4208 DrawLevelElementAnimationIfNeeded(x, y, element);
4209 else if (element == EL_DRAGON)
4212 int dir = MovDir[x][y];
4213 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4214 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4215 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4216 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4217 dir == MV_UP ? IMG_FLAMES_1_UP :
4218 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4219 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4222 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4225 GfxAction[x][y] = ACTION_ATTACKING;
4227 if (IS_PLAYER(x, y))
4228 DrawPlayerField(x, y);
4230 DrawLevelField(x, y);
4232 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4234 for (i=1; i <= 3; i++)
4236 int xx = x + i * dx;
4237 int yy = y + i * dy;
4238 int sx = SCREENX(xx);
4239 int sy = SCREENY(yy);
4240 int flame_graphic = graphic + (i - 1);
4242 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4247 int flamed = MovingOrBlocked2Element(xx, yy);
4249 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4252 RemoveMovingField(xx, yy);
4254 Feld[xx][yy] = EL_FLAMES;
4255 if (IN_SCR_FIELD(sx, sy))
4257 DrawLevelFieldCrumbledSand(xx, yy);
4258 DrawGraphic(sx, sy, flame_graphic, frame);
4263 if (Feld[xx][yy] == EL_FLAMES)
4264 Feld[xx][yy] = EL_EMPTY;
4265 DrawLevelField(xx, yy);
4270 if (MovDelay[x][y]) /* element still has to wait some time */
4272 PlayLevelSoundAction(x, y, ACTION_WAITING);
4278 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4279 for all other elements GfxAction will be set by InitMovingField() */
4280 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4281 GfxAction[x][y] = ACTION_MOVING;
4285 /* now make next step */
4287 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4289 if (DONT_COLLIDE_WITH(element) &&
4290 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4291 !PLAYER_PROTECTED(newx, newy))
4294 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4297 /* player killed by element which is deadly when colliding with */
4299 KillHero(PLAYERINFO(newx, newy));
4304 else if ((element == EL_PENGUIN ||
4305 element == EL_ROBOT ||
4306 element == EL_SATELLITE ||
4307 element == EL_BALLOON ||
4308 IS_CUSTOM_ELEMENT(element)) &&
4309 IN_LEV_FIELD(newx, newy) &&
4310 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4313 Store[x][y] = EL_ACID;
4315 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4317 if (Feld[newx][newy] == EL_EXIT_OPEN)
4319 Feld[x][y] = EL_EMPTY;
4320 DrawLevelField(x, y);
4322 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4323 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4324 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4326 local_player->friends_still_needed--;
4327 if (!local_player->friends_still_needed &&
4328 !local_player->GameOver && AllPlayersGone)
4329 local_player->LevelSolved = local_player->GameOver = TRUE;
4333 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4335 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4336 DrawLevelField(newx, newy);
4338 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4340 else if (!IS_FREE(newx, newy))
4342 GfxAction[x][y] = ACTION_WAITING;
4344 if (IS_PLAYER(x, y))
4345 DrawPlayerField(x, y);
4347 DrawLevelField(x, y);
4351 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4353 if (IS_FOOD_PIG(Feld[newx][newy]))
4355 if (IS_MOVING(newx, newy))
4356 RemoveMovingField(newx, newy);
4359 Feld[newx][newy] = EL_EMPTY;
4360 DrawLevelField(newx, newy);
4363 PlayLevelSound(x, y, SND_PIG_DIGGING);
4365 else if (!IS_FREE(newx, newy))
4367 if (IS_PLAYER(x, y))
4368 DrawPlayerField(x, y);
4370 DrawLevelField(x, y);
4374 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4376 if (!IS_FREE(newx, newy))
4378 if (IS_PLAYER(x, y))
4379 DrawPlayerField(x, y);
4381 DrawLevelField(x, y);
4387 boolean wanna_flame = !RND(10);
4388 int dx = newx - x, dy = newy - y;
4389 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4390 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4391 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4392 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4393 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4394 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4397 IS_CLASSIC_ENEMY(element1) ||
4398 IS_CLASSIC_ENEMY(element2)) &&
4399 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4400 element1 != EL_FLAMES && element2 != EL_FLAMES)
4403 ResetGfxAnimation(x, y);
4404 GfxAction[x][y] = ACTION_ATTACKING;
4407 if (IS_PLAYER(x, y))
4408 DrawPlayerField(x, y);
4410 DrawLevelField(x, y);
4412 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4414 MovDelay[x][y] = 50;
4416 Feld[newx][newy] = EL_FLAMES;
4417 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4418 Feld[newx1][newy1] = EL_FLAMES;
4419 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4420 Feld[newx2][newy2] = EL_FLAMES;
4426 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4427 Feld[newx][newy] == EL_DIAMOND)
4429 if (IS_MOVING(newx, newy))
4430 RemoveMovingField(newx, newy);
4433 Feld[newx][newy] = EL_EMPTY;
4434 DrawLevelField(newx, newy);
4437 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4439 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4440 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4442 if (AmoebaNr[newx][newy])
4444 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4445 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4446 Feld[newx][newy] == EL_BD_AMOEBA)
4447 AmoebaCnt[AmoebaNr[newx][newy]]--;
4450 if (IS_MOVING(newx, newy))
4451 RemoveMovingField(newx, newy);
4454 Feld[newx][newy] = EL_EMPTY;
4455 DrawLevelField(newx, newy);
4458 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4460 else if ((element == EL_PACMAN || element == EL_MOLE)
4461 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4463 if (AmoebaNr[newx][newy])
4465 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4466 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4467 Feld[newx][newy] == EL_BD_AMOEBA)
4468 AmoebaCnt[AmoebaNr[newx][newy]]--;
4471 if (element == EL_MOLE)
4473 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4474 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4476 ResetGfxAnimation(x, y);
4477 GfxAction[x][y] = ACTION_DIGGING;
4478 DrawLevelField(x, y);
4480 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4481 return; /* wait for shrinking amoeba */
4483 else /* element == EL_PACMAN */
4485 Feld[newx][newy] = EL_EMPTY;
4486 DrawLevelField(newx, newy);
4487 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4490 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4491 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4492 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4494 /* wait for shrinking amoeba to completely disappear */
4497 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4499 /* object was running against a wall */
4504 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4505 DrawLevelElementAnimation(x, y, element);
4507 if (element == EL_BUG ||
4508 element == EL_SPACESHIP ||
4509 element == EL_SP_SNIKSNAK)
4510 DrawLevelField(x, y);
4511 else if (element == EL_MOLE)
4512 DrawLevelField(x, y);
4513 else if (element == EL_BD_BUTTERFLY ||
4514 element == EL_BD_FIREFLY)
4515 DrawLevelElementAnimationIfNeeded(x, y, element);
4516 else if (element == EL_SATELLITE)
4517 DrawLevelElementAnimationIfNeeded(x, y, element);
4518 else if (element == EL_SP_ELECTRON)
4519 DrawLevelElementAnimationIfNeeded(x, y, element);
4522 if (DONT_TOUCH(element))
4523 TestIfBadThingTouchesHero(x, y);
4526 PlayLevelSoundAction(x, y, ACTION_WAITING);
4532 InitMovingField(x, y, MovDir[x][y]);
4534 PlayLevelSoundAction(x, y, ACTION_MOVING);
4538 ContinueMoving(x, y);
4541 void ContinueMoving(int x, int y)
4543 int element = Feld[x][y];
4544 int direction = MovDir[x][y];
4545 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4546 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4547 int newx = x + dx, newy = y + dy;
4548 int nextx = newx + dx, nexty = newy + dy;
4549 boolean pushed = Pushed[x][y];
4551 MovPos[x][y] += getElementMoveStepsize(x, y);
4553 if (pushed) /* special case: moving object pushed by player */
4554 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4556 if (ABS(MovPos[x][y]) < TILEX)
4558 DrawLevelField(x, y);
4560 return; /* element is still moving */
4563 /* element reached destination field */
4565 Feld[x][y] = EL_EMPTY;
4566 Feld[newx][newy] = element;
4567 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4569 if (element == EL_MOLE)
4571 Feld[x][y] = EL_SAND;
4573 DrawLevelFieldCrumbledSandNeighbours(x, y);
4575 else if (element == EL_QUICKSAND_FILLING)
4577 element = Feld[newx][newy] = get_next_element(element);
4578 Store[newx][newy] = Store[x][y];
4580 else if (element == EL_QUICKSAND_EMPTYING)
4582 Feld[x][y] = get_next_element(element);
4583 element = Feld[newx][newy] = Store[x][y];
4585 else if (element == EL_MAGIC_WALL_FILLING)
4587 element = Feld[newx][newy] = get_next_element(element);
4588 if (!game.magic_wall_active)
4589 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4590 Store[newx][newy] = Store[x][y];
4592 else if (element == EL_MAGIC_WALL_EMPTYING)
4594 Feld[x][y] = get_next_element(element);
4595 if (!game.magic_wall_active)
4596 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4597 element = Feld[newx][newy] = Store[x][y];
4599 else if (element == EL_BD_MAGIC_WALL_FILLING)
4601 element = Feld[newx][newy] = get_next_element(element);
4602 if (!game.magic_wall_active)
4603 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4604 Store[newx][newy] = Store[x][y];
4606 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4608 Feld[x][y] = get_next_element(element);
4609 if (!game.magic_wall_active)
4610 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4611 element = Feld[newx][newy] = Store[x][y];
4613 else if (element == EL_AMOEBA_DROPPING)
4615 Feld[x][y] = get_next_element(element);
4616 element = Feld[newx][newy] = Store[x][y];
4618 else if (element == EL_SOKOBAN_OBJECT)
4621 Feld[x][y] = Back[x][y];
4623 if (Back[newx][newy])
4624 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4626 Back[x][y] = Back[newx][newy] = 0;
4628 else if (Store[x][y] == EL_ACID)
4630 element = Feld[newx][newy] = EL_ACID;
4634 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4635 MovDelay[newx][newy] = 0;
4637 /* copy element change control values to new field */
4638 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4639 ChangePage[newx][newy] = ChangePage[x][y];
4640 Changed[newx][newy] = Changed[x][y];
4641 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4643 ChangeDelay[x][y] = 0;
4644 ChangePage[x][y] = -1;
4645 Changed[x][y] = CE_BITMASK_DEFAULT;
4646 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4648 /* copy animation control values to new field */
4649 GfxFrame[newx][newy] = GfxFrame[x][y];
4650 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4651 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4652 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4654 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4656 ResetGfxAnimation(x, y); /* reset animation values for old field */
4659 /* 2.1.1 (does not work correctly for spring) */
4660 if (!CAN_MOVE(element))
4661 MovDir[newx][newy] = 0;
4665 /* (does not work for falling objects that slide horizontally) */
4666 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4667 MovDir[newx][newy] = 0;
4670 if (!CAN_MOVE(element) ||
4671 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4672 MovDir[newx][newy] = 0;
4675 if (!CAN_MOVE(element) ||
4676 (CAN_FALL(element) && direction == MV_DOWN))
4677 GfxDir[x][y] = MovDir[newx][newy] = 0;
4682 DrawLevelField(x, y);
4683 DrawLevelField(newx, newy);
4685 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4687 /* prevent pushed element from moving on in pushed direction */
4688 if (pushed && CAN_MOVE(element) &&
4689 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4690 !(element_info[element].move_pattern & direction))
4691 TurnRound(newx, newy);
4693 if (!pushed) /* special case: moving object pushed by player */
4695 WasJustMoving[newx][newy] = 3;
4697 if (CAN_FALL(element) && direction == MV_DOWN)
4698 WasJustFalling[newx][newy] = 3;
4701 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4703 TestIfBadThingTouchesHero(newx, newy);
4704 TestIfBadThingTouchesFriend(newx, newy);
4706 if (!IS_CUSTOM_ELEMENT(element))
4707 TestIfBadThingTouchesOtherBadThing(newx, newy);
4709 else if (element == EL_PENGUIN)
4710 TestIfFriendTouchesBadThing(newx, newy);
4712 if (CAN_FALL(element) && direction == MV_DOWN &&
4713 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4717 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4721 if (ChangePage[newx][newy] != -1) /* delayed change */
4722 ChangeElement(newx, newy, ChangePage[newx][newy]);
4725 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4726 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4729 TestIfPlayerTouchesCustomElement(newx, newy);
4730 TestIfElementTouchesCustomElement(newx, newy);
4733 int AmoebeNachbarNr(int ax, int ay)
4736 int element = Feld[ax][ay];
4738 static int xy[4][2] =
4748 int x = ax + xy[i][0];
4749 int y = ay + xy[i][1];
4751 if (!IN_LEV_FIELD(x, y))
4754 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4755 group_nr = AmoebaNr[x][y];
4761 void AmoebenVereinigen(int ax, int ay)
4763 int i, x, y, xx, yy;
4764 int new_group_nr = AmoebaNr[ax][ay];
4765 static int xy[4][2] =
4773 if (new_group_nr == 0)
4781 if (!IN_LEV_FIELD(x, y))
4784 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4785 Feld[x][y] == EL_BD_AMOEBA ||
4786 Feld[x][y] == EL_AMOEBA_DEAD) &&
4787 AmoebaNr[x][y] != new_group_nr)
4789 int old_group_nr = AmoebaNr[x][y];
4791 if (old_group_nr == 0)
4794 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4795 AmoebaCnt[old_group_nr] = 0;
4796 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4797 AmoebaCnt2[old_group_nr] = 0;
4799 for (yy=0; yy<lev_fieldy; yy++)
4801 for (xx=0; xx<lev_fieldx; xx++)
4803 if (AmoebaNr[xx][yy] == old_group_nr)
4804 AmoebaNr[xx][yy] = new_group_nr;
4811 void AmoebeUmwandeln(int ax, int ay)
4815 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4817 int group_nr = AmoebaNr[ax][ay];
4822 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4823 printf("AmoebeUmwandeln(): This should never happen!\n");
4828 for (y=0; y<lev_fieldy; y++)
4830 for (x=0; x<lev_fieldx; x++)
4832 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4835 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4839 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
4840 SND_AMOEBA_TURNING_TO_GEM :
4841 SND_AMOEBA_TURNING_TO_ROCK));
4846 static int xy[4][2] =
4859 if (!IN_LEV_FIELD(x, y))
4862 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4864 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
4865 SND_AMOEBA_TURNING_TO_GEM :
4866 SND_AMOEBA_TURNING_TO_ROCK));
4873 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4876 int group_nr = AmoebaNr[ax][ay];
4877 boolean done = FALSE;
4882 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4883 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4888 for (y=0; y<lev_fieldy; y++)
4890 for (x=0; x<lev_fieldx; x++)
4892 if (AmoebaNr[x][y] == group_nr &&
4893 (Feld[x][y] == EL_AMOEBA_DEAD ||
4894 Feld[x][y] == EL_BD_AMOEBA ||
4895 Feld[x][y] == EL_AMOEBA_GROWING))
4898 Feld[x][y] = new_element;
4899 InitField(x, y, FALSE);
4900 DrawLevelField(x, y);
4907 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
4908 SND_BD_AMOEBA_TURNING_TO_ROCK :
4909 SND_BD_AMOEBA_TURNING_TO_GEM));
4912 void AmoebeWaechst(int x, int y)
4914 static unsigned long sound_delay = 0;
4915 static unsigned long sound_delay_value = 0;
4917 if (!MovDelay[x][y]) /* start new growing cycle */
4921 if (DelayReached(&sound_delay, sound_delay_value))
4924 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
4926 if (Store[x][y] == EL_BD_AMOEBA)
4927 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
4929 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
4931 sound_delay_value = 30;
4935 if (MovDelay[x][y]) /* wait some time before growing bigger */
4938 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4940 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4941 6 - MovDelay[x][y]);
4943 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4946 if (!MovDelay[x][y])
4948 Feld[x][y] = Store[x][y];
4950 DrawLevelField(x, y);
4955 void AmoebaDisappearing(int x, int y)
4957 static unsigned long sound_delay = 0;
4958 static unsigned long sound_delay_value = 0;
4960 if (!MovDelay[x][y]) /* start new shrinking cycle */
4964 if (DelayReached(&sound_delay, sound_delay_value))
4965 sound_delay_value = 30;
4968 if (MovDelay[x][y]) /* wait some time before shrinking */
4971 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4973 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4974 6 - MovDelay[x][y]);
4976 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4979 if (!MovDelay[x][y])
4981 Feld[x][y] = EL_EMPTY;
4982 DrawLevelField(x, y);
4984 /* don't let mole enter this field in this cycle;
4985 (give priority to objects falling to this field from above) */
4991 void AmoebeAbleger(int ax, int ay)
4994 int element = Feld[ax][ay];
4995 int graphic = el2img(element);
4996 int newax = ax, neway = ay;
4997 static int xy[4][2] =
5005 if (!level.amoeba_speed)
5007 Feld[ax][ay] = EL_AMOEBA_DEAD;
5008 DrawLevelField(ax, ay);
5012 if (IS_ANIMATED(graphic))
5013 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5015 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5016 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5018 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5021 if (MovDelay[ax][ay])
5025 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5028 int x = ax + xy[start][0];
5029 int y = ay + xy[start][1];
5031 if (!IN_LEV_FIELD(x, y))
5034 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5035 if (IS_FREE(x, y) ||
5036 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5042 if (newax == ax && neway == ay)
5045 else /* normal or "filled" (BD style) amoeba */
5048 boolean waiting_for_player = FALSE;
5052 int j = (start + i) % 4;
5053 int x = ax + xy[j][0];
5054 int y = ay + xy[j][1];
5056 if (!IN_LEV_FIELD(x, y))
5059 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5060 if (IS_FREE(x, y) ||
5061 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5067 else if (IS_PLAYER(x, y))
5068 waiting_for_player = TRUE;
5071 if (newax == ax && neway == ay) /* amoeba cannot grow */
5073 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5075 Feld[ax][ay] = EL_AMOEBA_DEAD;
5076 DrawLevelField(ax, ay);
5077 AmoebaCnt[AmoebaNr[ax][ay]]--;
5079 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5081 if (element == EL_AMOEBA_FULL)
5082 AmoebeUmwandeln(ax, ay);
5083 else if (element == EL_BD_AMOEBA)
5084 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5089 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5091 /* amoeba gets larger by growing in some direction */
5093 int new_group_nr = AmoebaNr[ax][ay];
5096 if (new_group_nr == 0)
5098 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5099 printf("AmoebeAbleger(): This should never happen!\n");
5104 AmoebaNr[newax][neway] = new_group_nr;
5105 AmoebaCnt[new_group_nr]++;
5106 AmoebaCnt2[new_group_nr]++;
5108 /* if amoeba touches other amoeba(s) after growing, unify them */
5109 AmoebenVereinigen(newax, neway);
5111 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5113 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5119 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5120 (neway == lev_fieldy - 1 && newax != ax))
5122 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5123 Store[newax][neway] = element;
5125 else if (neway == ay)
5127 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5129 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5131 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5136 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5137 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5138 Store[ax][ay] = EL_AMOEBA_DROP;
5139 ContinueMoving(ax, ay);
5143 DrawLevelField(newax, neway);
5146 void Life(int ax, int ay)
5149 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5151 int element = Feld[ax][ay];
5152 int graphic = el2img(element);
5153 boolean changed = FALSE;
5155 if (IS_ANIMATED(graphic))
5156 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5161 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5162 MovDelay[ax][ay] = life_time;
5164 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5167 if (MovDelay[ax][ay])
5171 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5173 int xx = ax+x1, yy = ay+y1;
5176 if (!IN_LEV_FIELD(xx, yy))
5179 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5181 int x = xx+x2, y = yy+y2;
5183 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5186 if (((Feld[x][y] == element ||
5187 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5189 (IS_FREE(x, y) && Stop[x][y]))
5193 if (xx == ax && yy == ay) /* field in the middle */
5195 if (nachbarn < life[0] || nachbarn > life[1])
5197 Feld[xx][yy] = EL_EMPTY;
5199 DrawLevelField(xx, yy);
5200 Stop[xx][yy] = TRUE;
5204 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5205 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5206 { /* free border field */
5207 if (nachbarn >= life[2] && nachbarn <= life[3])
5209 Feld[xx][yy] = element;
5210 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5212 DrawLevelField(xx, yy);
5213 Stop[xx][yy] = TRUE;
5220 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5221 SND_GAME_OF_LIFE_GROWING);
5224 static void InitRobotWheel(int x, int y)
5226 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5229 static void RunRobotWheel(int x, int y)
5231 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5234 static void StopRobotWheel(int x, int y)
5236 if (ZX == x && ZY == y)
5240 static void InitTimegateWheel(int x, int y)
5242 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5245 static void RunTimegateWheel(int x, int y)
5247 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5250 void CheckExit(int x, int y)
5252 if (local_player->gems_still_needed > 0 ||
5253 local_player->sokobanfields_still_needed > 0 ||
5254 local_player->lights_still_needed > 0)
5256 int element = Feld[x][y];
5257 int graphic = el2img(element);
5259 if (IS_ANIMATED(graphic))
5260 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5265 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5268 Feld[x][y] = EL_EXIT_OPENING;
5270 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5273 void CheckExitSP(int x, int y)
5275 if (local_player->gems_still_needed > 0)
5277 int element = Feld[x][y];
5278 int graphic = el2img(element);
5280 if (IS_ANIMATED(graphic))
5281 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5286 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5289 Feld[x][y] = EL_SP_EXIT_OPENING;
5291 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5294 static void CloseAllOpenTimegates()
5298 for (y=0; y<lev_fieldy; y++)
5300 for (x=0; x<lev_fieldx; x++)
5302 int element = Feld[x][y];
5304 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5306 Feld[x][y] = EL_TIMEGATE_CLOSING;
5308 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5310 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5317 void EdelsteinFunkeln(int x, int y)
5319 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5322 if (Feld[x][y] == EL_BD_DIAMOND)
5325 if (MovDelay[x][y] == 0) /* next animation frame */
5326 MovDelay[x][y] = 11 * !SimpleRND(500);
5328 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5332 if (setup.direct_draw && MovDelay[x][y])
5333 SetDrawtoField(DRAW_BUFFERED);
5335 DrawLevelElementAnimation(x, y, Feld[x][y]);
5337 if (MovDelay[x][y] != 0)
5339 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5340 10 - MovDelay[x][y]);
5342 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5344 if (setup.direct_draw)
5348 dest_x = FX + SCREENX(x) * TILEX;
5349 dest_y = FY + SCREENY(y) * TILEY;
5351 BlitBitmap(drawto_field, window,
5352 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5353 SetDrawtoField(DRAW_DIRECT);
5359 void MauerWaechst(int x, int y)
5363 if (!MovDelay[x][y]) /* next animation frame */
5364 MovDelay[x][y] = 3 * delay;
5366 if (MovDelay[x][y]) /* wait some time before next frame */
5370 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5372 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5373 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5375 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5378 if (!MovDelay[x][y])
5380 if (MovDir[x][y] == MV_LEFT)
5382 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5383 DrawLevelField(x - 1, y);
5385 else if (MovDir[x][y] == MV_RIGHT)
5387 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5388 DrawLevelField(x + 1, y);
5390 else if (MovDir[x][y] == MV_UP)
5392 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5393 DrawLevelField(x, y - 1);
5397 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5398 DrawLevelField(x, y + 1);
5401 Feld[x][y] = Store[x][y];
5403 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5404 DrawLevelField(x, y);
5409 void MauerAbleger(int ax, int ay)
5411 int element = Feld[ax][ay];
5412 int graphic = el2img(element);
5413 boolean oben_frei = FALSE, unten_frei = FALSE;
5414 boolean links_frei = FALSE, rechts_frei = FALSE;
5415 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5416 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5417 boolean new_wall = FALSE;
5419 if (IS_ANIMATED(graphic))
5420 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5422 if (!MovDelay[ax][ay]) /* start building new wall */
5423 MovDelay[ax][ay] = 6;
5425 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5428 if (MovDelay[ax][ay])
5432 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5434 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5436 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5438 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5441 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5442 element == EL_EXPANDABLE_WALL_ANY)
5446 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5447 Store[ax][ay-1] = element;
5448 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5449 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5450 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5451 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5456 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5457 Store[ax][ay+1] = element;
5458 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5459 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5460 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5461 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5466 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5467 element == EL_EXPANDABLE_WALL_ANY ||
5468 element == EL_EXPANDABLE_WALL)
5472 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5473 Store[ax-1][ay] = element;
5474 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5475 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5476 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5477 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5483 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5484 Store[ax+1][ay] = element;
5485 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5486 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5487 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5488 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5493 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5494 DrawLevelField(ax, ay);
5496 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5498 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5499 unten_massiv = TRUE;
5500 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5501 links_massiv = TRUE;
5502 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5503 rechts_massiv = TRUE;
5505 if (((oben_massiv && unten_massiv) ||
5506 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5507 element == EL_EXPANDABLE_WALL) &&
5508 ((links_massiv && rechts_massiv) ||
5509 element == EL_EXPANDABLE_WALL_VERTICAL))
5510 Feld[ax][ay] = EL_WALL;
5514 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5516 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5520 void CheckForDragon(int x, int y)
5523 boolean dragon_found = FALSE;
5524 static int xy[4][2] =
5536 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5538 if (IN_LEV_FIELD(xx, yy) &&
5539 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5541 if (Feld[xx][yy] == EL_DRAGON)
5542 dragon_found = TRUE;
5555 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5557 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5559 Feld[xx][yy] = EL_EMPTY;
5560 DrawLevelField(xx, yy);
5569 static void InitBuggyBase(int x, int y)
5571 int element = Feld[x][y];
5572 int activating_delay = FRAMES_PER_SECOND / 4;
5575 (element == EL_SP_BUGGY_BASE ?
5576 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5577 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5579 element == EL_SP_BUGGY_BASE_ACTIVE ?
5580 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5583 static void WarnBuggyBase(int x, int y)
5586 static int xy[4][2] =
5596 int xx = x + xy[i][0], yy = y + xy[i][1];
5598 if (IS_PLAYER(xx, yy))
5600 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5607 static void InitTrap(int x, int y)
5609 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5612 static void ActivateTrap(int x, int y)
5614 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5617 static void ChangeActiveTrap(int x, int y)
5619 int graphic = IMG_TRAP_ACTIVE;
5621 /* if new animation frame was drawn, correct crumbled sand border */
5622 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5623 DrawLevelFieldCrumbledSand(x, y);
5626 static void ChangeElementNowExt(int x, int y, int target_element)
5628 /* check if element under player changes from accessible to unaccessible
5629 (needed for special case of dropping element which then changes) */
5630 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5631 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5638 Feld[x][y] = target_element;
5640 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5642 ResetGfxAnimation(x, y);
5643 ResetRandomAnimationValue(x, y);
5645 InitField(x, y, FALSE);
5646 if (CAN_MOVE(Feld[x][y]))
5649 DrawLevelField(x, y);
5651 if (GFX_CRUMBLED(Feld[x][y]))
5652 DrawLevelFieldCrumbledSandNeighbours(x, y);
5654 TestIfBadThingTouchesHero(x, y);
5655 TestIfPlayerTouchesCustomElement(x, y);
5656 TestIfElementTouchesCustomElement(x, y);
5658 if (ELEM_IS_PLAYER(target_element))
5659 RelocatePlayer(x, y, target_element);
5662 static boolean ChangeElementNow(int x, int y, int element, int page)
5664 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5666 /* always use default change event to prevent running into a loop */
5667 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5668 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5670 /* do not change already changed elements with same change event */
5672 if (Changed[x][y] & ChangeEvent[x][y])
5679 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5681 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5683 if (change->explode)
5690 if (change->use_content)
5692 boolean complete_change = TRUE;
5693 boolean can_change[3][3];
5696 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5698 boolean half_destructible;
5699 int ex = x + xx - 1;
5700 int ey = y + yy - 1;
5703 can_change[xx][yy] = TRUE;
5705 if (ex == x && ey == y) /* do not check changing element itself */
5708 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5710 can_change[xx][yy] = FALSE; /* do not change empty borders */
5715 if (!IN_LEV_FIELD(ex, ey))
5717 can_change[xx][yy] = FALSE;
5718 complete_change = FALSE;
5725 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5726 e = MovingOrBlocked2Element(ex, ey);
5728 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5730 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5731 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5732 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5734 can_change[xx][yy] = FALSE;
5735 complete_change = FALSE;
5739 if (!change->only_complete || complete_change)
5741 boolean something_has_changed = FALSE;
5743 if (change->only_complete && change->use_random_change &&
5744 RND(100) < change->random)
5747 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5749 int ex = x + xx - 1;
5750 int ey = y + yy - 1;
5752 if (can_change[xx][yy] && (!change->use_random_change ||
5753 RND(100) < change->random))
5755 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5756 RemoveMovingField(ex, ey);
5758 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5760 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5762 something_has_changed = TRUE;
5764 /* for symmetry reasons, freeze newly created border elements */
5765 if (ex != x || ey != y)
5766 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5770 if (something_has_changed)
5771 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
5776 ChangeElementNowExt(x, y, change->target_element);
5778 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
5784 static void ChangeElement(int x, int y, int page)
5786 int element = MovingOrBlocked2Element(x, y);
5787 struct ElementInfo *ei = &element_info[element];
5788 struct ElementChangeInfo *change = &ei->change_page[page];
5792 if (!CAN_CHANGE(element))
5795 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5796 x, y, element, element_info[element].token_name);
5797 printf("ChangeElement(): This should never happen!\n");
5803 if (ChangeDelay[x][y] == 0) /* initialize element change */
5805 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5806 RND(change->delay_random * change->delay_frames)) + 1;
5808 ResetGfxAnimation(x, y);
5809 ResetRandomAnimationValue(x, y);
5811 if (change->pre_change_function)
5812 change->pre_change_function(x, y);
5815 ChangeDelay[x][y]--;
5817 if (ChangeDelay[x][y] != 0) /* continue element change */
5819 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5821 if (IS_ANIMATED(graphic))
5822 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5824 if (change->change_function)
5825 change->change_function(x, y);
5827 else /* finish element change */
5829 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5831 page = ChangePage[x][y];
5832 ChangePage[x][y] = -1;
5835 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5837 ChangeDelay[x][y] = 1; /* try change after next move step */
5838 ChangePage[x][y] = page; /* remember page to use for change */
5843 if (ChangeElementNow(x, y, element, page))
5845 if (change->post_change_function)
5846 change->post_change_function(x, y);
5851 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5852 int trigger_element,
5858 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5861 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5863 int element = EL_CUSTOM_START + i;
5865 boolean change_element = FALSE;
5868 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5871 for (j=0; j < element_info[element].num_change_pages; j++)
5873 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5875 if (change->can_change &&
5877 change->events & CH_EVENT_BIT(trigger_event) &&
5879 change->sides & trigger_side &&
5880 change->trigger_element == trigger_element)
5883 if (!(change->events & CH_EVENT_BIT(trigger_event)))
5884 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5885 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5888 change_element = TRUE;
5895 if (!change_element)
5898 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5901 if (x == lx && y == ly) /* do not change trigger element itself */
5905 if (Feld[x][y] == element)
5907 ChangeDelay[x][y] = 1;
5908 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5909 ChangeElement(x, y, page);
5917 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5920 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5924 static boolean CheckElementSideChange(int x, int y, int element, int side,
5925 int trigger_event, int page)
5927 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5930 if (Feld[x][y] == EL_BLOCKED)
5932 Blocked2Moving(x, y, &x, &y);
5933 element = Feld[x][y];
5937 page = element_info[element].event_page_nr[trigger_event];
5939 if (!(element_info[element].change_page[page].sides & side))
5942 ChangeDelay[x][y] = 1;
5943 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5944 ChangeElement(x, y, page);
5949 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5951 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5954 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
5956 boolean was_waiting = player->is_waiting;
5960 int jx = player->jx, jy = player->jy;
5961 int element = player->element_nr;
5964 if (!was_waiting) /* not waiting -> waiting */
5966 player->is_waiting = TRUE;
5967 player->is_bored = FALSE;
5968 player->is_sleeping = FALSE;
5970 player->frame_counter_bored =
5972 game.player_boring_delay_fixed +
5973 SimpleRND(game.player_boring_delay_random));
5974 player->frame_counter_sleeping =
5976 game.player_sleeping_delay_fixed +
5977 SimpleRND(game.player_sleeping_delay_random));
5979 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
5982 if (FrameCounter >= player->frame_counter_bored)
5983 player->is_bored = TRUE;
5984 if (FrameCounter >= player->frame_counter_sleeping)
5985 player->is_sleeping = TRUE;
5987 action = (player->is_sleeping ? ACTION_SLEEPING :
5988 player->is_bored ? ACTION_BORING : ACTION_WAITING);
5991 PlayLevelSoundElementAction(jx, jy, element, action);
5993 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
5995 else if (was_waiting) /* waiting -> not waiting */
5997 player->is_waiting = FALSE;
5998 player->is_bored = FALSE;
5999 player->is_sleeping = FALSE;
6001 player->frame_counter_bored = -1;
6002 player->frame_counter_sleeping = -1;
6007 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6010 static byte stored_player_action[MAX_PLAYERS];
6011 static int num_stored_actions = 0;
6013 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6014 int left = player_action & JOY_LEFT;
6015 int right = player_action & JOY_RIGHT;
6016 int up = player_action & JOY_UP;
6017 int down = player_action & JOY_DOWN;
6018 int button1 = player_action & JOY_BUTTON_1;
6019 int button2 = player_action & JOY_BUTTON_2;
6020 int dx = (left ? -1 : right ? 1 : 0);
6021 int dy = (up ? -1 : down ? 1 : 0);
6024 stored_player_action[player->index_nr] = 0;
6025 num_stored_actions++;
6029 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6032 if (!player->active || tape.pausing)
6038 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6042 snapped = SnapField(player, dx, dy);
6046 dropped = DropElement(player);
6048 moved = MovePlayer(player, dx, dy);
6051 if (tape.single_step && tape.recording && !tape.pausing)
6053 if (button1 || (dropped && !moved))
6055 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6056 SnapField(player, 0, 0); /* stop snapping */
6060 SetPlayerWaiting(player, FALSE);
6063 return player_action;
6065 stored_player_action[player->index_nr] = player_action;
6071 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6074 /* no actions for this player (no input at player's configured device) */
6076 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6077 SnapField(player, 0, 0);
6078 CheckGravityMovement(player);
6080 if (player->MovPos == 0)
6081 SetPlayerWaiting(player, TRUE);
6083 if (player->MovPos == 0) /* needed for tape.playing */
6084 player->is_moving = FALSE;
6090 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6092 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6094 TapeRecordAction(stored_player_action);
6095 num_stored_actions = 0;
6102 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6104 static byte stored_player_action[MAX_PLAYERS];
6105 static int num_stored_actions = 0;
6106 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6107 int left = player_action & JOY_LEFT;
6108 int right = player_action & JOY_RIGHT;
6109 int up = player_action & JOY_UP;
6110 int down = player_action & JOY_DOWN;
6111 int button1 = player_action & JOY_BUTTON_1;
6112 int button2 = player_action & JOY_BUTTON_2;
6113 int dx = (left ? -1 : right ? 1 : 0);
6114 int dy = (up ? -1 : down ? 1 : 0);
6116 stored_player_action[player->index_nr] = 0;
6117 num_stored_actions++;
6119 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6121 if (!player->active || tape.pausing)
6126 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6129 snapped = SnapField(player, dx, dy);
6133 dropped = DropElement(player);
6135 moved = MovePlayer(player, dx, dy);
6138 if (tape.single_step && tape.recording && !tape.pausing)
6140 if (button1 || (dropped && !moved))
6142 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6143 SnapField(player, 0, 0); /* stop snapping */
6147 stored_player_action[player->index_nr] = player_action;
6151 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6153 /* no actions for this player (no input at player's configured device) */
6155 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6156 SnapField(player, 0, 0);
6157 CheckGravityMovement(player);
6159 if (player->MovPos == 0)
6160 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6162 if (player->MovPos == 0) /* needed for tape.playing */
6163 player->is_moving = FALSE;
6166 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6168 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6170 TapeRecordAction(stored_player_action);
6171 num_stored_actions = 0;
6178 static unsigned long action_delay = 0;
6179 unsigned long action_delay_value;
6180 int magic_wall_x = 0, magic_wall_y = 0;
6181 int i, x, y, element, graphic;
6182 byte *recorded_player_action;
6183 byte summarized_player_action = 0;
6185 byte tape_action[MAX_PLAYERS];
6188 if (game_status != GAME_MODE_PLAYING)
6191 action_delay_value =
6192 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6194 if (tape.playing && tape.index_search && !tape.pausing)
6195 action_delay_value = 0;
6197 /* ---------- main game synchronization point ---------- */
6199 WaitUntilDelayReached(&action_delay, action_delay_value);
6201 if (network_playing && !network_player_action_received)
6205 printf("DEBUG: try to get network player actions in time\n");
6209 #if defined(PLATFORM_UNIX)
6210 /* last chance to get network player actions without main loop delay */
6214 if (game_status != GAME_MODE_PLAYING)
6217 if (!network_player_action_received)
6221 printf("DEBUG: failed to get network player actions in time\n");
6232 printf("::: getting new tape action [%d]\n", FrameCounter);
6235 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6237 for (i=0; i<MAX_PLAYERS; i++)
6239 summarized_player_action |= stored_player[i].action;
6241 if (!network_playing)
6242 stored_player[i].effective_action = stored_player[i].action;
6245 #if defined(PLATFORM_UNIX)
6246 if (network_playing)
6247 SendToServer_MovePlayer(summarized_player_action);
6250 if (!options.network && !setup.team_mode)
6251 local_player->effective_action = summarized_player_action;
6253 for (i=0; i < MAX_PLAYERS; i++)
6255 int actual_player_action = stored_player[i].effective_action;
6257 if (stored_player[i].programmed_action)
6258 actual_player_action = stored_player[i].programmed_action;
6260 if (recorded_player_action)
6261 actual_player_action = recorded_player_action[i];
6263 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6265 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6266 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6268 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6273 TapeRecordAction(tape_action);
6276 network_player_action_received = FALSE;
6278 ScrollScreen(NULL, SCROLL_GO_ON);
6284 for (i=0; i<MAX_PLAYERS; i++)
6285 stored_player[i].Frame++;
6289 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6291 for (i=0; i<MAX_PLAYERS; i++)
6293 struct PlayerInfo *player = &stored_player[i];
6297 if (player->active && player->is_pushing && player->is_moving &&
6300 ContinueMoving(x, y);
6302 /* continue moving after pushing (this is actually a bug) */
6303 if (!IS_MOVING(x, y))
6312 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6314 Changed[x][y] = CE_BITMASK_DEFAULT;
6315 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6318 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6320 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6321 printf("GameActions(): This should never happen!\n");
6323 ChangePage[x][y] = -1;
6328 if (WasJustMoving[x][y] > 0)
6329 WasJustMoving[x][y]--;
6330 if (WasJustFalling[x][y] > 0)
6331 WasJustFalling[x][y]--;
6336 /* reset finished pushing action (not done in ContinueMoving() to allow
6337 continous pushing animation for elements with zero push delay) */
6338 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6340 ResetGfxAnimation(x, y);
6341 DrawLevelField(x, y);
6346 if (IS_BLOCKED(x, y))
6350 Blocked2Moving(x, y, &oldx, &oldy);
6351 if (!IS_MOVING(oldx, oldy))
6353 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6354 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6355 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6356 printf("GameActions(): This should never happen!\n");
6362 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6364 element = Feld[x][y];
6366 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6368 graphic = el2img(element);
6374 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6376 element = graphic = 0;
6380 if (graphic_info[graphic].anim_global_sync)
6381 GfxFrame[x][y] = FrameCounter;
6383 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6384 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6385 ResetRandomAnimationValue(x, y);
6387 SetRandomAnimationValue(x, y);
6390 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6393 if (IS_INACTIVE(element))
6395 if (IS_ANIMATED(graphic))
6396 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6402 /* this may take place after moving, so 'element' may have changed */
6404 if (IS_CHANGING(x, y))
6406 if (IS_CHANGING(x, y) &&
6407 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6411 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6412 element_info[element].event_page_nr[CE_DELAY]);
6414 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6417 element = Feld[x][y];
6418 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6422 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6427 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6429 if (element == EL_MOLE)
6430 printf("::: %d, %d, %d [%d]\n",
6431 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6435 if (element == EL_YAMYAM)
6436 printf("::: %d, %d, %d\n",
6437 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6441 if (IS_ANIMATED(graphic) &&
6445 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6448 if (element == EL_BUG)
6449 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6453 if (element == EL_MOLE)
6454 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6458 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6459 EdelsteinFunkeln(x, y);
6461 else if ((element == EL_ACID ||
6462 element == EL_EXIT_OPEN ||
6463 element == EL_SP_EXIT_OPEN ||
6464 element == EL_SP_TERMINAL ||
6465 element == EL_SP_TERMINAL_ACTIVE ||
6466 element == EL_EXTRA_TIME ||
6467 element == EL_SHIELD_NORMAL ||
6468 element == EL_SHIELD_DEADLY) &&
6469 IS_ANIMATED(graphic))
6470 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6471 else if (IS_MOVING(x, y))
6472 ContinueMoving(x, y);
6473 else if (IS_ACTIVE_BOMB(element))
6474 CheckDynamite(x, y);
6476 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6477 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6479 else if (element == EL_AMOEBA_GROWING)
6480 AmoebeWaechst(x, y);
6481 else if (element == EL_AMOEBA_SHRINKING)
6482 AmoebaDisappearing(x, y);
6484 #if !USE_NEW_AMOEBA_CODE
6485 else if (IS_AMOEBALIVE(element))
6486 AmoebeAbleger(x, y);
6489 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6491 else if (element == EL_EXIT_CLOSED)
6493 else if (element == EL_SP_EXIT_CLOSED)
6495 else if (element == EL_EXPANDABLE_WALL_GROWING)
6497 else if (element == EL_EXPANDABLE_WALL ||
6498 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6499 element == EL_EXPANDABLE_WALL_VERTICAL ||
6500 element == EL_EXPANDABLE_WALL_ANY)
6502 else if (element == EL_FLAMES)
6503 CheckForDragon(x, y);
6505 else if (IS_AUTO_CHANGING(element))
6506 ChangeElement(x, y);
6508 else if (element == EL_EXPLOSION)
6509 ; /* drawing of correct explosion animation is handled separately */
6510 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6511 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6514 /* this may take place after moving, so 'element' may have changed */
6515 if (IS_AUTO_CHANGING(Feld[x][y]))
6516 ChangeElement(x, y);
6519 if (IS_BELT_ACTIVE(element))
6520 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6522 if (game.magic_wall_active)
6524 int jx = local_player->jx, jy = local_player->jy;
6526 /* play the element sound at the position nearest to the player */
6527 if ((element == EL_MAGIC_WALL_FULL ||
6528 element == EL_MAGIC_WALL_ACTIVE ||
6529 element == EL_MAGIC_WALL_EMPTYING ||
6530 element == EL_BD_MAGIC_WALL_FULL ||
6531 element == EL_BD_MAGIC_WALL_ACTIVE ||
6532 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6533 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6541 #if USE_NEW_AMOEBA_CODE
6542 /* new experimental amoeba growth stuff */
6544 if (!(FrameCounter % 8))
6547 static unsigned long random = 1684108901;
6549 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6552 x = (random >> 10) % lev_fieldx;
6553 y = (random >> 20) % lev_fieldy;
6555 x = RND(lev_fieldx);
6556 y = RND(lev_fieldy);
6558 element = Feld[x][y];
6560 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6561 if (!IS_PLAYER(x,y) &&
6562 (element == EL_EMPTY ||
6563 element == EL_SAND ||
6564 element == EL_QUICKSAND_EMPTY ||
6565 element == EL_ACID_SPLASH_LEFT ||
6566 element == EL_ACID_SPLASH_RIGHT))
6568 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6569 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6570 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6571 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6572 Feld[x][y] = EL_AMOEBA_DROP;
6575 random = random * 129 + 1;
6581 if (game.explosions_delayed)
6584 game.explosions_delayed = FALSE;
6586 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6588 element = Feld[x][y];
6590 if (ExplodeField[x][y])
6591 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6592 else if (element == EL_EXPLOSION)
6593 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6595 ExplodeField[x][y] = EX_NO_EXPLOSION;
6598 game.explosions_delayed = TRUE;
6601 if (game.magic_wall_active)
6603 if (!(game.magic_wall_time_left % 4))
6605 int element = Feld[magic_wall_x][magic_wall_y];
6607 if (element == EL_BD_MAGIC_WALL_FULL ||
6608 element == EL_BD_MAGIC_WALL_ACTIVE ||
6609 element == EL_BD_MAGIC_WALL_EMPTYING)
6610 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6612 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6615 if (game.magic_wall_time_left > 0)
6617 game.magic_wall_time_left--;
6618 if (!game.magic_wall_time_left)
6620 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6622 element = Feld[x][y];
6624 if (element == EL_MAGIC_WALL_ACTIVE ||
6625 element == EL_MAGIC_WALL_FULL)
6627 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6628 DrawLevelField(x, y);
6630 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6631 element == EL_BD_MAGIC_WALL_FULL)
6633 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6634 DrawLevelField(x, y);
6638 game.magic_wall_active = FALSE;
6643 if (game.light_time_left > 0)
6645 game.light_time_left--;
6647 if (game.light_time_left == 0)
6648 RedrawAllLightSwitchesAndInvisibleElements();
6651 if (game.timegate_time_left > 0)
6653 game.timegate_time_left--;
6655 if (game.timegate_time_left == 0)
6656 CloseAllOpenTimegates();
6659 for (i=0; i<MAX_PLAYERS; i++)
6661 struct PlayerInfo *player = &stored_player[i];
6663 if (SHIELD_ON(player))
6665 if (player->shield_deadly_time_left)
6666 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6667 else if (player->shield_normal_time_left)
6668 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6672 if (TimeFrames >= FRAMES_PER_SECOND)
6677 for (i=0; i<MAX_PLAYERS; i++)
6679 struct PlayerInfo *player = &stored_player[i];
6681 if (SHIELD_ON(player))
6683 player->shield_normal_time_left--;
6685 if (player->shield_deadly_time_left > 0)
6686 player->shield_deadly_time_left--;
6690 if (tape.recording || tape.playing)
6691 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6697 if (TimeLeft <= 10 && setup.time_limit)
6698 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6700 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6702 if (!TimeLeft && setup.time_limit)
6703 for (i=0; i<MAX_PLAYERS; i++)
6704 KillHero(&stored_player[i]);
6706 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6707 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6712 if (options.debug) /* calculate frames per second */
6714 static unsigned long fps_counter = 0;
6715 static int fps_frames = 0;
6716 unsigned long fps_delay_ms = Counter() - fps_counter;
6720 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6722 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6725 fps_counter = Counter();
6728 redraw_mask |= REDRAW_FPS;
6732 if (stored_player[0].jx != stored_player[0].last_jx ||
6733 stored_player[0].jy != stored_player[0].last_jy)
6734 printf("::: %d, %d, %d, %d, %d\n",
6735 stored_player[0].MovDir,
6736 stored_player[0].MovPos,
6737 stored_player[0].GfxPos,
6738 stored_player[0].Frame,
6739 stored_player[0].StepFrame);
6746 for (i=0; i<MAX_PLAYERS; i++)
6749 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6751 stored_player[i].Frame += move_frames;
6753 if (stored_player[i].MovPos != 0)
6754 stored_player[i].StepFrame += move_frames;
6759 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6761 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6763 local_player->show_envelope = 0;
6768 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6770 int min_x = x, min_y = y, max_x = x, max_y = y;
6773 for (i=0; i<MAX_PLAYERS; i++)
6775 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6777 if (!stored_player[i].active || &stored_player[i] == player)
6780 min_x = MIN(min_x, jx);
6781 min_y = MIN(min_y, jy);
6782 max_x = MAX(max_x, jx);
6783 max_y = MAX(max_y, jy);
6786 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6789 static boolean AllPlayersInVisibleScreen()
6793 for (i=0; i<MAX_PLAYERS; i++)
6795 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6797 if (!stored_player[i].active)
6800 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6807 void ScrollLevel(int dx, int dy)
6809 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6812 BlitBitmap(drawto_field, drawto_field,
6813 FX + TILEX * (dx == -1) - softscroll_offset,
6814 FY + TILEY * (dy == -1) - softscroll_offset,
6815 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6816 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6817 FX + TILEX * (dx == 1) - softscroll_offset,
6818 FY + TILEY * (dy == 1) - softscroll_offset);
6822 x = (dx == 1 ? BX1 : BX2);
6823 for (y=BY1; y <= BY2; y++)
6824 DrawScreenField(x, y);
6829 y = (dy == 1 ? BY1 : BY2);
6830 for (x=BX1; x <= BX2; x++)
6831 DrawScreenField(x, y);
6834 redraw_mask |= REDRAW_FIELD;
6837 static void CheckGravityMovement(struct PlayerInfo *player)
6839 if (game.gravity && !player->programmed_action)
6841 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6842 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6844 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6845 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6846 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6847 int jx = player->jx, jy = player->jy;
6848 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6849 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6850 int new_jx = jx + dx, new_jy = jy + dy;
6851 boolean field_under_player_is_free =
6852 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6853 boolean player_is_moving_to_valid_field =
6854 (IN_LEV_FIELD(new_jx, new_jy) &&
6855 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6856 Feld[new_jx][new_jy] == EL_SAND));
6857 /* !!! extend EL_SAND to anything diggable !!! */
6859 if (field_under_player_is_free &&
6860 !player_is_moving_to_valid_field &&
6861 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6862 player->programmed_action = MV_DOWN;
6868 -----------------------------------------------------------------------------
6869 dx, dy: direction (non-diagonal) to try to move the player to
6870 real_dx, real_dy: direction as read from input device (can be diagonal)
6873 boolean MovePlayerOneStep(struct PlayerInfo *player,
6874 int dx, int dy, int real_dx, int real_dy)
6877 static int change_sides[4][2] =
6879 /* enter side leave side */
6880 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6881 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6882 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6883 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6885 int move_direction = (dx == -1 ? MV_LEFT :
6886 dx == +1 ? MV_RIGHT :
6888 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6889 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6890 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6892 int jx = player->jx, jy = player->jy;
6893 int new_jx = jx + dx, new_jy = jy + dy;
6897 if (!player->active || (!dx && !dy))
6898 return MF_NO_ACTION;
6900 player->MovDir = (dx < 0 ? MV_LEFT :
6903 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6905 if (!IN_LEV_FIELD(new_jx, new_jy))
6906 return MF_NO_ACTION;
6908 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6909 return MF_NO_ACTION;
6912 element = MovingOrBlocked2Element(new_jx, new_jy);
6914 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6917 if (DONT_RUN_INTO(element))
6919 if (element == EL_ACID && dx == 0 && dy == 1)
6922 Feld[jx][jy] = EL_PLAYER_1;
6923 InitMovingField(jx, jy, MV_DOWN);
6924 Store[jx][jy] = EL_ACID;
6925 ContinueMoving(jx, jy);
6929 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6934 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6935 if (can_move != MF_MOVING)
6938 /* check if DigField() has caused relocation of the player */
6939 if (player->jx != jx || player->jy != jy)
6940 return MF_NO_ACTION;
6942 StorePlayer[jx][jy] = 0;
6943 player->last_jx = jx;
6944 player->last_jy = jy;
6945 player->jx = new_jx;
6946 player->jy = new_jy;
6947 StorePlayer[new_jx][new_jy] = player->element_nr;
6950 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6952 ScrollPlayer(player, SCROLL_INIT);
6955 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6957 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6958 CE_OTHER_GETS_LEFT);
6959 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6960 CE_LEFT_BY_PLAYER, -1);
6963 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6965 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6966 enter_side, CE_OTHER_GETS_ENTERED);
6967 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6968 CE_ENTERED_BY_PLAYER, -1);
6975 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6977 int jx = player->jx, jy = player->jy;
6978 int old_jx = jx, old_jy = jy;
6979 int moved = MF_NO_ACTION;
6981 if (!player->active || (!dx && !dy))
6985 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6989 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6990 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6994 /* remove the last programmed player action */
6995 player->programmed_action = 0;
6999 /* should only happen if pre-1.2 tape recordings are played */
7000 /* this is only for backward compatibility */
7002 int original_move_delay_value = player->move_delay_value;
7005 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7009 /* scroll remaining steps with finest movement resolution */
7010 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7012 while (player->MovPos)
7014 ScrollPlayer(player, SCROLL_GO_ON);
7015 ScrollScreen(NULL, SCROLL_GO_ON);
7021 player->move_delay_value = original_move_delay_value;
7024 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7026 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7027 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7031 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7032 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7038 if (moved & MF_MOVING && !ScreenMovPos &&
7039 (player == local_player || !options.network))
7041 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7042 int offset = (setup.scroll_delay ? 3 : 0);
7044 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7046 /* actual player has left the screen -- scroll in that direction */
7047 if (jx != old_jx) /* player has moved horizontally */
7048 scroll_x += (jx - old_jx);
7049 else /* player has moved vertically */
7050 scroll_y += (jy - old_jy);
7054 if (jx != old_jx) /* player has moved horizontally */
7056 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7057 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7058 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7060 /* don't scroll over playfield boundaries */
7061 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7062 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7064 /* don't scroll more than one field at a time */
7065 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7067 /* don't scroll against the player's moving direction */
7068 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7069 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7070 scroll_x = old_scroll_x;
7072 else /* player has moved vertically */
7074 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7075 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7076 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7078 /* don't scroll over playfield boundaries */
7079 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7080 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7082 /* don't scroll more than one field at a time */
7083 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7085 /* don't scroll against the player's moving direction */
7086 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7087 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7088 scroll_y = old_scroll_y;
7092 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7094 if (!options.network && !AllPlayersInVisibleScreen())
7096 scroll_x = old_scroll_x;
7097 scroll_y = old_scroll_y;
7101 ScrollScreen(player, SCROLL_INIT);
7102 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7109 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7111 if (!(moved & MF_MOVING) && !player->is_pushing)
7116 player->StepFrame = 0;
7118 if (moved & MF_MOVING)
7120 if (old_jx != jx && old_jy == jy)
7121 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7122 else if (old_jx == jx && old_jy != jy)
7123 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7125 DrawLevelField(jx, jy); /* for "crumbled sand" */
7127 player->last_move_dir = player->MovDir;
7128 player->is_moving = TRUE;
7130 player->is_snapping = FALSE;
7134 player->is_switching = FALSE;
7140 static int change_sides[4][2] =
7142 /* enter side leave side */
7143 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7144 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7145 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7146 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7148 int move_direction = player->MovDir;
7149 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7150 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7153 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7155 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7156 leave_side, CE_OTHER_GETS_LEFT);
7157 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7158 leave_side, CE_LEFT_BY_PLAYER, -1);
7161 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7163 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7164 enter_side, CE_OTHER_GETS_ENTERED);
7165 CheckElementSideChange(jx, jy, Feld[jx][jy],
7166 enter_side, CE_ENTERED_BY_PLAYER, -1);
7177 CheckGravityMovement(player);
7180 player->last_move_dir = MV_NO_MOVING;
7182 player->is_moving = FALSE;
7185 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7187 TestIfHeroTouchesBadThing(jx, jy);
7188 TestIfPlayerTouchesCustomElement(jx, jy);
7191 if (!player->active)
7197 void ScrollPlayer(struct PlayerInfo *player, int mode)
7199 int jx = player->jx, jy = player->jy;
7200 int last_jx = player->last_jx, last_jy = player->last_jy;
7201 int move_stepsize = TILEX / player->move_delay_value;
7203 if (!player->active || !player->MovPos)
7206 if (mode == SCROLL_INIT)
7208 player->actual_frame_counter = FrameCounter;
7209 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7211 if (Feld[last_jx][last_jy] == EL_EMPTY)
7212 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7219 else if (!FrameReached(&player->actual_frame_counter, 1))
7222 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7223 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7225 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7226 Feld[last_jx][last_jy] = EL_EMPTY;
7228 /* before DrawPlayer() to draw correct player graphic for this case */
7229 if (player->MovPos == 0)
7230 CheckGravityMovement(player);
7233 DrawPlayer(player); /* needed here only to cleanup last field */
7236 if (player->MovPos == 0) /* player reached destination field */
7238 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7240 /* continue with normal speed after quickly moving through gate */
7241 HALVE_PLAYER_SPEED(player);
7243 /* be able to make the next move without delay */
7244 player->move_delay = 0;
7247 player->last_jx = jx;
7248 player->last_jy = jy;
7250 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7251 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7252 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7254 DrawPlayer(player); /* needed here only to cleanup last field */
7257 if (local_player->friends_still_needed == 0 ||
7258 IS_SP_ELEMENT(Feld[jx][jy]))
7259 player->LevelSolved = player->GameOver = TRUE;
7262 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7264 TestIfHeroTouchesBadThing(jx, jy);
7265 TestIfPlayerTouchesCustomElement(jx, jy);
7267 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7270 if (!player->active)
7274 if (tape.single_step && tape.recording && !tape.pausing &&
7275 !player->programmed_action)
7276 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7280 void ScrollScreen(struct PlayerInfo *player, int mode)
7282 static unsigned long screen_frame_counter = 0;
7284 if (mode == SCROLL_INIT)
7286 /* set scrolling step size according to actual player's moving speed */
7287 ScrollStepSize = TILEX / player->move_delay_value;
7289 screen_frame_counter = FrameCounter;
7290 ScreenMovDir = player->MovDir;
7291 ScreenMovPos = player->MovPos;
7292 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7295 else if (!FrameReached(&screen_frame_counter, 1))
7300 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7301 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7302 redraw_mask |= REDRAW_FIELD;
7305 ScreenMovDir = MV_NO_MOVING;
7308 void TestIfPlayerTouchesCustomElement(int x, int y)
7310 static int xy[4][2] =
7317 static int change_sides[4][2] =
7319 /* center side border side */
7320 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7321 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7322 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7323 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7325 static int touch_dir[4] =
7332 int center_element = Feld[x][y]; /* should always be non-moving! */
7337 int xx = x + xy[i][0];
7338 int yy = y + xy[i][1];
7339 int center_side = change_sides[i][0];
7340 int border_side = change_sides[i][1];
7343 if (!IN_LEV_FIELD(xx, yy))
7346 if (IS_PLAYER(x, y))
7348 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7349 border_element = Feld[xx][yy]; /* may be moving! */
7350 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7351 border_element = Feld[xx][yy];
7352 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7353 border_element = MovingOrBlocked2Element(xx, yy);
7355 continue; /* center and border element do not touch */
7357 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7358 CE_OTHER_GETS_TOUCHED);
7359 CheckElementSideChange(xx, yy, border_element, border_side,
7360 CE_TOUCHED_BY_PLAYER, -1);
7362 else if (IS_PLAYER(xx, yy))
7364 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7366 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7368 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7369 continue; /* center and border element do not touch */
7372 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7373 CE_OTHER_GETS_TOUCHED);
7374 CheckElementSideChange(x, y, center_element, center_side,
7375 CE_TOUCHED_BY_PLAYER, -1);
7382 void TestIfElementTouchesCustomElement(int x, int y)
7384 static int xy[4][2] =
7391 static int change_sides[4][2] =
7393 /* center side border side */
7394 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7395 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7396 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7397 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7399 static int touch_dir[4] =
7406 boolean change_center_element = FALSE;
7407 int center_element_change_page = 0;
7408 int center_element = Feld[x][y]; /* should always be non-moving! */
7413 int xx = x + xy[i][0];
7414 int yy = y + xy[i][1];
7415 int center_side = change_sides[i][0];
7416 int border_side = change_sides[i][1];
7419 if (!IN_LEV_FIELD(xx, yy))
7422 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7423 border_element = Feld[xx][yy]; /* may be moving! */
7424 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7425 border_element = Feld[xx][yy];
7426 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7427 border_element = MovingOrBlocked2Element(xx, yy);
7429 continue; /* center and border element do not touch */
7431 /* check for change of center element (but change it only once) */
7432 if (IS_CUSTOM_ELEMENT(center_element) &&
7433 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7434 !change_center_element)
7436 for (j=0; j < element_info[center_element].num_change_pages; j++)
7438 struct ElementChangeInfo *change =
7439 &element_info[center_element].change_page[j];
7441 if (change->can_change &&
7442 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7443 change->sides & border_side &&
7444 change->trigger_element == border_element)
7446 change_center_element = TRUE;
7447 center_element_change_page = j;
7454 /* check for change of border element */
7455 if (IS_CUSTOM_ELEMENT(border_element) &&
7456 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7458 for (j=0; j < element_info[border_element].num_change_pages; j++)
7460 struct ElementChangeInfo *change =
7461 &element_info[border_element].change_page[j];
7463 if (change->can_change &&
7464 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7465 change->sides & center_side &&
7466 change->trigger_element == center_element)
7468 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7469 CE_OTHER_IS_TOUCHING, j);
7476 if (change_center_element)
7477 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7478 CE_OTHER_IS_TOUCHING, center_element_change_page);
7481 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7483 int i, kill_x = -1, kill_y = -1;
7484 static int test_xy[4][2] =
7491 static int test_dir[4] =
7501 int test_x, test_y, test_move_dir, test_element;
7503 test_x = good_x + test_xy[i][0];
7504 test_y = good_y + test_xy[i][1];
7505 if (!IN_LEV_FIELD(test_x, test_y))
7509 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7512 test_element = Feld[test_x][test_y];
7514 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7517 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7518 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7520 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7521 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7529 if (kill_x != -1 || kill_y != -1)
7531 if (IS_PLAYER(good_x, good_y))
7533 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7535 if (player->shield_deadly_time_left > 0)
7536 Bang(kill_x, kill_y);
7537 else if (!PLAYER_PROTECTED(good_x, good_y))
7541 Bang(good_x, good_y);
7545 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7547 int i, kill_x = -1, kill_y = -1;
7548 int bad_element = Feld[bad_x][bad_y];
7549 static int test_xy[4][2] =
7556 static int touch_dir[4] =
7563 static int test_dir[4] =
7571 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7576 int test_x, test_y, test_move_dir, test_element;
7578 test_x = bad_x + test_xy[i][0];
7579 test_y = bad_y + test_xy[i][1];
7580 if (!IN_LEV_FIELD(test_x, test_y))
7584 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7586 test_element = Feld[test_x][test_y];
7588 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7589 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7591 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7592 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7594 /* good thing is player or penguin that does not move away */
7595 if (IS_PLAYER(test_x, test_y))
7597 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7599 if (bad_element == EL_ROBOT && player->is_moving)
7600 continue; /* robot does not kill player if he is moving */
7602 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7604 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7605 continue; /* center and border element do not touch */
7612 else if (test_element == EL_PENGUIN)
7621 if (kill_x != -1 || kill_y != -1)
7623 if (IS_PLAYER(kill_x, kill_y))
7625 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7627 if (player->shield_deadly_time_left > 0)
7629 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7633 Bang(kill_x, kill_y);
7637 void TestIfHeroTouchesBadThing(int x, int y)
7639 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7642 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7644 TestIfGoodThingHitsBadThing(x, y, move_dir);
7647 void TestIfBadThingTouchesHero(int x, int y)
7649 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7652 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7654 TestIfBadThingHitsGoodThing(x, y, move_dir);
7657 void TestIfFriendTouchesBadThing(int x, int y)
7659 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7662 void TestIfBadThingTouchesFriend(int x, int y)
7664 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7667 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7669 int i, kill_x = bad_x, kill_y = bad_y;
7670 static int xy[4][2] =
7682 x = bad_x + xy[i][0];
7683 y = bad_y + xy[i][1];
7684 if (!IN_LEV_FIELD(x, y))
7687 element = Feld[x][y];
7688 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7689 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7697 if (kill_x != bad_x || kill_y != bad_y)
7701 void KillHero(struct PlayerInfo *player)
7703 int jx = player->jx, jy = player->jy;
7705 if (!player->active)
7708 /* remove accessible field at the player's position */
7709 Feld[jx][jy] = EL_EMPTY;
7711 /* deactivate shield (else Bang()/Explode() would not work right) */
7712 player->shield_normal_time_left = 0;
7713 player->shield_deadly_time_left = 0;
7719 static void KillHeroUnlessProtected(int x, int y)
7721 if (!PLAYER_PROTECTED(x, y))
7722 KillHero(PLAYERINFO(x, y));
7725 void BuryHero(struct PlayerInfo *player)
7727 int jx = player->jx, jy = player->jy;
7729 if (!player->active)
7733 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
7735 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
7737 PlayLevelSound(jx, jy, SND_GAME_LOSING);
7739 player->GameOver = TRUE;
7743 void RemoveHero(struct PlayerInfo *player)
7745 int jx = player->jx, jy = player->jy;
7746 int i, found = FALSE;
7748 player->present = FALSE;
7749 player->active = FALSE;
7751 if (!ExplodeField[jx][jy])
7752 StorePlayer[jx][jy] = 0;
7754 for (i=0; i<MAX_PLAYERS; i++)
7755 if (stored_player[i].active)
7759 AllPlayersGone = TRUE;
7766 =============================================================================
7767 checkDiagonalPushing()
7768 -----------------------------------------------------------------------------
7769 check if diagonal input device direction results in pushing of object
7770 (by checking if the alternative direction is walkable, diggable, ...)
7771 =============================================================================
7774 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7775 int x, int y, int real_dx, int real_dy)
7777 int jx, jy, dx, dy, xx, yy;
7779 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7782 /* diagonal direction: check alternative direction */
7787 xx = jx + (dx == 0 ? real_dx : 0);
7788 yy = jy + (dy == 0 ? real_dy : 0);
7790 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7794 =============================================================================
7796 -----------------------------------------------------------------------------
7797 x, y: field next to player (non-diagonal) to try to dig to
7798 real_dx, real_dy: direction as read from input device (can be diagonal)
7799 =============================================================================
7802 int DigField(struct PlayerInfo *player,
7803 int x, int y, int real_dx, int real_dy, int mode)
7805 static int change_sides[4] =
7807 CH_SIDE_RIGHT, /* moving left */
7808 CH_SIDE_LEFT, /* moving right */
7809 CH_SIDE_BOTTOM, /* moving up */
7810 CH_SIDE_TOP, /* moving down */
7812 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7813 int jx = player->jx, jy = player->jy;
7814 int dx = x - jx, dy = y - jy;
7815 int nextx = x + dx, nexty = y + dy;
7816 int move_direction = (dx == -1 ? MV_LEFT :
7817 dx == +1 ? MV_RIGHT :
7819 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7820 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7823 if (player->MovPos == 0)
7825 player->is_digging = FALSE;
7826 player->is_collecting = FALSE;
7829 if (player->MovPos == 0) /* last pushing move finished */
7830 player->is_pushing = FALSE;
7832 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7834 player->is_switching = FALSE;
7835 player->push_delay = 0;
7837 return MF_NO_ACTION;
7840 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7841 return MF_NO_ACTION;
7844 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7846 if (IS_TUBE(Feld[jx][jy]) ||
7847 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7851 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7852 int tube_leave_directions[][2] =
7854 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7855 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7856 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7857 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7858 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7859 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7860 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7861 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7862 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7863 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7864 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7865 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7868 while (tube_leave_directions[i][0] != tube_element)
7871 if (tube_leave_directions[i][0] == -1) /* should not happen */
7875 if (!(tube_leave_directions[i][1] & move_direction))
7876 return MF_NO_ACTION; /* tube has no opening in this direction */
7879 element = Feld[x][y];
7881 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7882 game.engine_version >= VERSION_IDENT(2,2,0,0))
7883 return MF_NO_ACTION;
7887 case EL_SP_PORT_LEFT:
7888 case EL_SP_PORT_RIGHT:
7890 case EL_SP_PORT_DOWN:
7891 case EL_SP_PORT_HORIZONTAL:
7892 case EL_SP_PORT_VERTICAL:
7893 case EL_SP_PORT_ANY:
7894 case EL_SP_GRAVITY_PORT_LEFT:
7895 case EL_SP_GRAVITY_PORT_RIGHT:
7896 case EL_SP_GRAVITY_PORT_UP:
7897 case EL_SP_GRAVITY_PORT_DOWN:
7899 element != EL_SP_PORT_LEFT &&
7900 element != EL_SP_GRAVITY_PORT_LEFT &&
7901 element != EL_SP_PORT_HORIZONTAL &&
7902 element != EL_SP_PORT_ANY) ||
7904 element != EL_SP_PORT_RIGHT &&
7905 element != EL_SP_GRAVITY_PORT_RIGHT &&
7906 element != EL_SP_PORT_HORIZONTAL &&
7907 element != EL_SP_PORT_ANY) ||
7909 element != EL_SP_PORT_UP &&
7910 element != EL_SP_GRAVITY_PORT_UP &&
7911 element != EL_SP_PORT_VERTICAL &&
7912 element != EL_SP_PORT_ANY) ||
7914 element != EL_SP_PORT_DOWN &&
7915 element != EL_SP_GRAVITY_PORT_DOWN &&
7916 element != EL_SP_PORT_VERTICAL &&
7917 element != EL_SP_PORT_ANY) ||
7918 !IN_LEV_FIELD(nextx, nexty) ||
7919 !IS_FREE(nextx, nexty))
7920 return MF_NO_ACTION;
7922 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7923 element == EL_SP_GRAVITY_PORT_RIGHT ||
7924 element == EL_SP_GRAVITY_PORT_UP ||
7925 element == EL_SP_GRAVITY_PORT_DOWN)
7926 game.gravity = !game.gravity;
7928 /* automatically move to the next field with double speed */
7929 player->programmed_action = move_direction;
7930 DOUBLE_PLAYER_SPEED(player);
7932 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
7936 case EL_TUBE_VERTICAL:
7937 case EL_TUBE_HORIZONTAL:
7938 case EL_TUBE_VERTICAL_LEFT:
7939 case EL_TUBE_VERTICAL_RIGHT:
7940 case EL_TUBE_HORIZONTAL_UP:
7941 case EL_TUBE_HORIZONTAL_DOWN:
7942 case EL_TUBE_LEFT_UP:
7943 case EL_TUBE_LEFT_DOWN:
7944 case EL_TUBE_RIGHT_UP:
7945 case EL_TUBE_RIGHT_DOWN:
7948 int tube_enter_directions[][2] =
7950 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7951 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7952 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7953 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7954 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7955 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7956 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7957 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7958 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7959 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7960 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7961 { -1, MV_NO_MOVING }
7964 while (tube_enter_directions[i][0] != element)
7967 if (tube_enter_directions[i][0] == -1) /* should not happen */
7971 if (!(tube_enter_directions[i][1] & move_direction))
7972 return MF_NO_ACTION; /* tube has no opening in this direction */
7974 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
7980 if (IS_WALKABLE(element))
7982 int sound_action = ACTION_WALKING;
7984 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7986 if (!player->key[element - EL_GATE_1])
7987 return MF_NO_ACTION;
7989 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7991 if (!player->key[element - EL_GATE_1_GRAY])
7992 return MF_NO_ACTION;
7994 else if (element == EL_EXIT_OPEN ||
7995 element == EL_SP_EXIT_OPEN ||
7996 element == EL_SP_EXIT_OPENING)
7998 sound_action = ACTION_PASSING; /* player is passing exit */
8000 else if (element == EL_EMPTY)
8002 sound_action = ACTION_MOVING; /* nothing to walk on */
8005 /* play sound from background or player, whatever is available */
8006 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8007 PlayLevelSoundElementAction(x, y, element, sound_action);
8009 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8013 else if (IS_PASSABLE(element))
8015 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8016 return MF_NO_ACTION;
8019 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8020 return MF_NO_ACTION;
8023 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8025 if (!player->key[element - EL_EM_GATE_1])
8026 return MF_NO_ACTION;
8028 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8030 if (!player->key[element - EL_EM_GATE_1_GRAY])
8031 return MF_NO_ACTION;
8034 /* automatically move to the next field with double speed */
8035 player->programmed_action = move_direction;
8036 DOUBLE_PLAYER_SPEED(player);
8038 PlayLevelSoundAction(x, y, ACTION_PASSING);
8042 else if (IS_DIGGABLE(element))
8046 if (mode != DF_SNAP)
8049 GfxElement[x][y] = GFX_ELEMENT(element);
8052 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8054 player->is_digging = TRUE;
8057 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8059 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8062 if (mode == DF_SNAP)
8063 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8068 else if (IS_COLLECTIBLE(element))
8072 if (mode != DF_SNAP)
8074 GfxElement[x][y] = element;
8075 player->is_collecting = TRUE;
8078 if (element == EL_SPEED_PILL)
8079 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8080 else if (element == EL_EXTRA_TIME && level.time > 0)
8083 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8085 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8087 player->shield_normal_time_left += 10;
8088 if (element == EL_SHIELD_DEADLY)
8089 player->shield_deadly_time_left += 10;
8091 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8093 if (player->inventory_size < MAX_INVENTORY_SIZE)
8094 player->inventory_element[player->inventory_size++] = element;
8096 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8097 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8099 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8101 player->dynabomb_count++;
8102 player->dynabombs_left++;
8104 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8106 player->dynabomb_size++;
8108 else if (element == EL_DYNABOMB_INCREASE_POWER)
8110 player->dynabomb_xl = TRUE;
8112 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8113 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8115 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8116 element - EL_KEY_1 : element - EL_EM_KEY_1);
8118 player->key[key_nr] = TRUE;
8120 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8121 el2edimg(EL_KEY_1 + key_nr));
8122 redraw_mask |= REDRAW_DOOR_1;
8124 else if (IS_ENVELOPE(element))
8127 player->show_envelope = element;
8129 ShowEnvelope(element - EL_ENVELOPE_1);
8132 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8136 for (i=0; i < element_info[element].collect_count; i++)
8137 if (player->inventory_size < MAX_INVENTORY_SIZE)
8138 player->inventory_element[player->inventory_size++] = element;
8140 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8141 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8143 else if (element_info[element].collect_count > 0)
8145 local_player->gems_still_needed -=
8146 element_info[element].collect_count;
8147 if (local_player->gems_still_needed < 0)
8148 local_player->gems_still_needed = 0;
8150 DrawText(DX_EMERALDS, DY_EMERALDS,
8151 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8154 RaiseScoreElement(element);
8155 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8157 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8160 if (mode == DF_SNAP)
8161 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8166 else if (IS_PUSHABLE(element))
8168 if (mode == DF_SNAP && element != EL_BD_ROCK)
8169 return MF_NO_ACTION;
8171 if (CAN_FALL(element) && dy)
8172 return MF_NO_ACTION;
8174 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8175 !(element == EL_SPRING && use_spring_bug))
8176 return MF_NO_ACTION;
8179 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8180 ((move_direction & MV_VERTICAL &&
8181 ((element_info[element].move_pattern & MV_LEFT &&
8182 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8183 (element_info[element].move_pattern & MV_RIGHT &&
8184 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8185 (move_direction & MV_HORIZONTAL &&
8186 ((element_info[element].move_pattern & MV_UP &&
8187 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8188 (element_info[element].move_pattern & MV_DOWN &&
8189 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8190 return MF_NO_ACTION;
8194 /* do not push elements already moving away faster than player */
8195 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8196 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8197 return MF_NO_ACTION;
8199 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8200 return MF_NO_ACTION;
8204 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8206 if (player->push_delay_value == -1)
8207 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8209 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8211 if (!player->is_pushing)
8212 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8216 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8217 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8218 !player_is_pushing))
8219 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8222 if (!player->is_pushing &&
8223 game.engine_version >= VERSION_IDENT(2,2,0,7))
8224 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8228 printf("::: push delay: %ld [%d, %d] [%d]\n",
8229 player->push_delay_value, FrameCounter, game.engine_version,
8230 player->is_pushing);
8233 player->is_pushing = TRUE;
8235 if (!(IN_LEV_FIELD(nextx, nexty) &&
8236 (IS_FREE(nextx, nexty) ||
8237 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8238 IS_SB_ELEMENT(element)))))
8239 return MF_NO_ACTION;
8241 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8242 return MF_NO_ACTION;
8244 if (player->push_delay == 0) /* new pushing; restart delay */
8245 player->push_delay = FrameCounter;
8247 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8248 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8249 element != EL_SPRING && element != EL_BALLOON)
8251 /* make sure that there is no move delay before next try to push */
8252 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8253 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8255 return MF_NO_ACTION;
8259 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8262 if (IS_SB_ELEMENT(element))
8264 if (element == EL_SOKOBAN_FIELD_FULL)
8266 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8267 local_player->sokobanfields_still_needed++;
8270 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8272 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8273 local_player->sokobanfields_still_needed--;
8276 Feld[x][y] = EL_SOKOBAN_OBJECT;
8278 if (Back[x][y] == Back[nextx][nexty])
8279 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8280 else if (Back[x][y] != 0)
8281 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8284 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8287 if (local_player->sokobanfields_still_needed == 0 &&
8288 game.emulation == EMU_SOKOBAN)
8290 player->LevelSolved = player->GameOver = TRUE;
8291 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8295 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8297 InitMovingField(x, y, move_direction);
8298 GfxAction[x][y] = ACTION_PUSHING;
8300 if (mode == DF_SNAP)
8301 ContinueMoving(x, y);
8303 MovPos[x][y] = (dx != 0 ? dx : dy);
8305 Pushed[x][y] = TRUE;
8306 Pushed[nextx][nexty] = TRUE;
8308 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8309 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8311 player->push_delay_value = -1; /* get new value later */
8313 CheckTriggeredElementSideChange(x, y, element, dig_side,
8314 CE_OTHER_GETS_PUSHED);
8315 CheckElementSideChange(x, y, element, dig_side,
8316 CE_PUSHED_BY_PLAYER, -1);
8320 else if (IS_SWITCHABLE(element))
8322 if (PLAYER_SWITCHING(player, x, y))
8325 player->is_switching = TRUE;
8326 player->switch_x = x;
8327 player->switch_y = y;
8329 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8331 if (element == EL_ROBOT_WHEEL)
8333 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8337 DrawLevelField(x, y);
8339 else if (element == EL_SP_TERMINAL)
8343 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8345 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8347 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8348 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8351 else if (IS_BELT_SWITCH(element))
8353 ToggleBeltSwitch(x, y);
8355 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8356 element == EL_SWITCHGATE_SWITCH_DOWN)
8358 ToggleSwitchgateSwitch(x, y);
8360 else if (element == EL_LIGHT_SWITCH ||
8361 element == EL_LIGHT_SWITCH_ACTIVE)
8363 ToggleLightSwitch(x, y);
8366 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8367 SND_LIGHT_SWITCH_ACTIVATING :
8368 SND_LIGHT_SWITCH_DEACTIVATING);
8371 else if (element == EL_TIMEGATE_SWITCH)
8373 ActivateTimegateSwitch(x, y);
8375 else if (element == EL_BALLOON_SWITCH_LEFT ||
8376 element == EL_BALLOON_SWITCH_RIGHT ||
8377 element == EL_BALLOON_SWITCH_UP ||
8378 element == EL_BALLOON_SWITCH_DOWN ||
8379 element == EL_BALLOON_SWITCH_ANY)
8381 if (element == EL_BALLOON_SWITCH_ANY)
8382 game.balloon_dir = move_direction;
8384 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8385 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8386 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8387 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8390 else if (element == EL_LAMP)
8392 Feld[x][y] = EL_LAMP_ACTIVE;
8393 local_player->lights_still_needed--;
8395 DrawLevelField(x, y);
8397 else if (element == EL_TIME_ORB_FULL)
8399 Feld[x][y] = EL_TIME_ORB_EMPTY;
8401 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8403 DrawLevelField(x, y);
8406 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8414 if (!PLAYER_SWITCHING(player, x, y))
8416 player->is_switching = TRUE;
8417 player->switch_x = x;
8418 player->switch_y = y;
8420 CheckTriggeredElementSideChange(x, y, element, dig_side,
8421 CE_OTHER_IS_SWITCHING);
8422 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8425 CheckTriggeredElementSideChange(x, y, element, dig_side,
8426 CE_OTHER_GETS_PRESSED);
8427 CheckElementSideChange(x, y, element, dig_side,
8428 CE_PRESSED_BY_PLAYER, -1);
8431 return MF_NO_ACTION;
8434 player->push_delay = 0;
8436 if (Feld[x][y] != element) /* really digged/collected something */
8437 player->is_collecting = !player->is_digging;
8442 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8444 int jx = player->jx, jy = player->jy;
8445 int x = jx + dx, y = jy + dy;
8446 int snap_direction = (dx == -1 ? MV_LEFT :
8447 dx == +1 ? MV_RIGHT :
8449 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8451 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8454 if (!player->active || !IN_LEV_FIELD(x, y))
8462 if (player->MovPos == 0)
8463 player->is_pushing = FALSE;
8465 player->is_snapping = FALSE;
8467 if (player->MovPos == 0)
8469 player->is_moving = FALSE;
8470 player->is_digging = FALSE;
8471 player->is_collecting = FALSE;
8477 if (player->is_snapping)
8480 player->MovDir = snap_direction;
8482 player->is_moving = FALSE;
8483 player->is_digging = FALSE;
8484 player->is_collecting = FALSE;
8486 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8489 player->is_snapping = TRUE;
8491 player->is_moving = FALSE;
8492 player->is_digging = FALSE;
8493 player->is_collecting = FALSE;
8495 DrawLevelField(x, y);
8501 boolean DropElement(struct PlayerInfo *player)
8503 int jx = player->jx, jy = player->jy;
8506 if (!player->active || player->MovPos)
8509 old_element = Feld[jx][jy];
8511 /* check if player has anything that can be dropped */
8512 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8515 /* check if anything can be dropped at the current position */
8516 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8519 /* collected custom elements can only be dropped on empty fields */
8520 if (player->inventory_size > 0 &&
8521 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8522 && old_element != EL_EMPTY)
8525 if (old_element != EL_EMPTY)
8526 Back[jx][jy] = old_element; /* store old element on this field */
8528 MovDelay[jx][jy] = 96;
8530 ResetGfxAnimation(jx, jy);
8531 ResetRandomAnimationValue(jx, jy);
8533 if (player->inventory_size > 0)
8535 int new_element = player->inventory_element[--player->inventory_size];
8537 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8538 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8541 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8542 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8544 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8545 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8547 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8549 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8550 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8552 TestIfElementTouchesCustomElement(jx, jy);
8554 else /* player is dropping a dyna bomb */
8556 player->dynabombs_left--;
8559 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8561 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8562 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8564 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8570 /* ------------------------------------------------------------------------- */
8571 /* game sound playing functions */
8572 /* ------------------------------------------------------------------------- */
8574 static int *loop_sound_frame = NULL;
8575 static int *loop_sound_volume = NULL;
8577 void InitPlayLevelSound()
8579 int num_sounds = getSoundListSize();
8581 if (loop_sound_frame != NULL)
8582 free(loop_sound_frame);
8584 if (loop_sound_volume != NULL)
8585 free(loop_sound_volume);
8587 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8588 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8591 static void PlayLevelSound(int x, int y, int nr)
8593 int sx = SCREENX(x), sy = SCREENY(y);
8594 int volume, stereo_position;
8595 int max_distance = 8;
8596 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8598 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8599 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8602 if (!IN_LEV_FIELD(x, y) ||
8603 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8604 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8607 volume = SOUND_MAX_VOLUME;
8609 if (!IN_SCR_FIELD(sx, sy))
8611 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8612 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8614 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8617 stereo_position = (SOUND_MAX_LEFT +
8618 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8619 (SCR_FIELDX + 2 * max_distance));
8621 if (IS_LOOP_SOUND(nr))
8623 /* This assures that quieter loop sounds do not overwrite louder ones,
8624 while restarting sound volume comparison with each new game frame. */
8626 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8629 loop_sound_volume[nr] = volume;
8630 loop_sound_frame[nr] = FrameCounter;
8633 PlaySoundExt(nr, volume, stereo_position, type);
8636 static void PlayLevelSoundNearest(int x, int y, int sound_action)
8638 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
8639 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8640 y < LEVELY(BY1) ? LEVELY(BY1) :
8641 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8645 static void PlayLevelSoundAction(int x, int y, int action)
8647 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
8650 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
8652 int sound_effect = element_info[element].sound[action];
8654 if (sound_effect != SND_UNDEFINED)
8655 PlayLevelSound(x, y, sound_effect);
8658 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
8661 int sound_effect = element_info[element].sound[action];
8663 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8664 PlayLevelSound(x, y, sound_effect);
8667 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
8669 int sound_effect = element_info[Feld[x][y]].sound[action];
8671 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8672 PlayLevelSound(x, y, sound_effect);
8675 static void StopLevelSoundActionIfLoop(int x, int y, int action)
8677 int sound_effect = element_info[Feld[x][y]].sound[action];
8679 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8680 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8683 static void PlayLevelMusic()
8686 if (levelset.music[level_nr] != MUS_UNDEFINED)
8687 PlayMusic(levelset.music[level_nr]); /* from config file */
8689 PlayMusic(-(level_nr + 1)); /* from music dir */
8691 PlayMusic(level_nr);
8695 void RaiseScore(int value)
8697 local_player->score += value;
8698 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8701 void RaiseScoreElement(int element)
8707 case EL_EMERALD_YELLOW:
8708 case EL_EMERALD_RED:
8709 case EL_EMERALD_PURPLE:
8710 case EL_SP_INFOTRON:
8711 RaiseScore(level.score[SC_EMERALD]);
8714 RaiseScore(level.score[SC_DIAMOND]);
8717 RaiseScore(level.score[SC_CRYSTAL]);
8720 RaiseScore(level.score[SC_PEARL]);
8723 case EL_BD_BUTTERFLY:
8724 case EL_SP_ELECTRON:
8725 RaiseScore(level.score[SC_BUG]);
8729 case EL_SP_SNIKSNAK:
8730 RaiseScore(level.score[SC_SPACESHIP]);
8733 case EL_DARK_YAMYAM:
8734 RaiseScore(level.score[SC_YAMYAM]);
8737 RaiseScore(level.score[SC_ROBOT]);
8740 RaiseScore(level.score[SC_PACMAN]);
8743 RaiseScore(level.score[SC_NUT]);
8746 case EL_SP_DISK_RED:
8747 case EL_DYNABOMB_INCREASE_NUMBER:
8748 case EL_DYNABOMB_INCREASE_SIZE:
8749 case EL_DYNABOMB_INCREASE_POWER:
8750 RaiseScore(level.score[SC_DYNAMITE]);
8752 case EL_SHIELD_NORMAL:
8753 case EL_SHIELD_DEADLY:
8754 RaiseScore(level.score[SC_SHIELD]);
8757 RaiseScore(level.score[SC_TIME_BONUS]);
8763 RaiseScore(level.score[SC_KEY]);
8766 RaiseScore(element_info[element].collect_score);
8771 void RequestQuitGame(boolean ask_if_really_quit)
8773 if (AllPlayersGone ||
8774 !ask_if_really_quit ||
8775 level_editor_test_game ||
8776 Request("Do you really want to quit the game ?",
8777 REQ_ASK | REQ_STAY_CLOSED))
8779 #if defined(PLATFORM_UNIX)
8780 if (options.network)
8781 SendToServer_StopPlaying();
8785 game_status = GAME_MODE_MAIN;
8791 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8796 /* ---------- new game button stuff ---------------------------------------- */
8798 /* graphic position values for game buttons */
8799 #define GAME_BUTTON_XSIZE 30
8800 #define GAME_BUTTON_YSIZE 30
8801 #define GAME_BUTTON_XPOS 5
8802 #define GAME_BUTTON_YPOS 215
8803 #define SOUND_BUTTON_XPOS 5
8804 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8806 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8807 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8808 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8809 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8810 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8811 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8818 } gamebutton_info[NUM_GAME_BUTTONS] =
8821 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8826 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8831 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8836 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8837 SOUND_CTRL_ID_MUSIC,
8838 "background music on/off"
8841 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8842 SOUND_CTRL_ID_LOOPS,
8843 "sound loops on/off"
8846 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8847 SOUND_CTRL_ID_SIMPLE,
8848 "normal sounds on/off"
8852 void CreateGameButtons()
8856 for (i=0; i<NUM_GAME_BUTTONS; i++)
8858 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8859 struct GadgetInfo *gi;
8862 unsigned long event_mask;
8863 int gd_xoffset, gd_yoffset;
8864 int gd_x1, gd_x2, gd_y1, gd_y2;
8867 gd_xoffset = gamebutton_info[i].x;
8868 gd_yoffset = gamebutton_info[i].y;
8869 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8870 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8872 if (id == GAME_CTRL_ID_STOP ||
8873 id == GAME_CTRL_ID_PAUSE ||
8874 id == GAME_CTRL_ID_PLAY)
8876 button_type = GD_TYPE_NORMAL_BUTTON;
8878 event_mask = GD_EVENT_RELEASED;
8879 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8880 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8884 button_type = GD_TYPE_CHECK_BUTTON;
8886 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8887 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8888 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8889 event_mask = GD_EVENT_PRESSED;
8890 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8891 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8894 gi = CreateGadget(GDI_CUSTOM_ID, id,
8895 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8896 GDI_X, DX + gd_xoffset,
8897 GDI_Y, DY + gd_yoffset,
8898 GDI_WIDTH, GAME_BUTTON_XSIZE,
8899 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8900 GDI_TYPE, button_type,
8901 GDI_STATE, GD_BUTTON_UNPRESSED,
8902 GDI_CHECKED, checked,
8903 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8904 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8905 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8906 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8907 GDI_EVENT_MASK, event_mask,
8908 GDI_CALLBACK_ACTION, HandleGameButtons,
8912 Error(ERR_EXIT, "cannot create gadget");
8914 game_gadget[id] = gi;
8918 void FreeGameButtons()
8922 for (i=0; i<NUM_GAME_BUTTONS; i++)
8923 FreeGadget(game_gadget[i]);
8926 static void MapGameButtons()
8930 for (i=0; i<NUM_GAME_BUTTONS; i++)
8931 MapGadget(game_gadget[i]);
8934 void UnmapGameButtons()
8938 for (i=0; i<NUM_GAME_BUTTONS; i++)
8939 UnmapGadget(game_gadget[i]);
8942 static void HandleGameButtons(struct GadgetInfo *gi)
8944 int id = gi->custom_id;
8946 if (game_status != GAME_MODE_PLAYING)
8951 case GAME_CTRL_ID_STOP:
8952 RequestQuitGame(TRUE);
8955 case GAME_CTRL_ID_PAUSE:
8956 if (options.network)
8958 #if defined(PLATFORM_UNIX)
8960 SendToServer_ContinuePlaying();
8962 SendToServer_PausePlaying();
8966 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8969 case GAME_CTRL_ID_PLAY:
8972 #if defined(PLATFORM_UNIX)
8973 if (options.network)
8974 SendToServer_ContinuePlaying();
8978 tape.pausing = FALSE;
8979 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8984 case SOUND_CTRL_ID_MUSIC:
8985 if (setup.sound_music)
8987 setup.sound_music = FALSE;
8990 else if (audio.music_available)
8992 setup.sound = setup.sound_music = TRUE;
8994 SetAudioMode(setup.sound);
9000 case SOUND_CTRL_ID_LOOPS:
9001 if (setup.sound_loops)
9002 setup.sound_loops = FALSE;
9003 else if (audio.loops_available)
9005 setup.sound = setup.sound_loops = TRUE;
9006 SetAudioMode(setup.sound);
9010 case SOUND_CTRL_ID_SIMPLE:
9011 if (setup.sound_simple)
9012 setup.sound_simple = FALSE;
9013 else if (audio.sound_available)
9015 setup.sound = setup.sound_simple = TRUE;
9016 SetAudioMode(setup.sound);